摘要:零前期準(zhǔn)備版本版本核心依賴包支持包簡介是官方出品的微服務(wù)框架,底層基于驅(qū)動(dòng),大致的使用套路和相差不是很多筆者只是淺淺的了解過,可能存在理解不透的情況。一配置中的配置類有兩種,一種是用于讀取配置文件的,另一種是用于配置服務(wù)器對(duì)象的。
零 前期準(zhǔn)備 0 版本
JDK 版本 : OpenJDK 11.0.1
IDE : idea 2018.3
Helidon Webserver : helidon-webserver 1.0.0 (核心依賴包)
Helidon Json : helidon-media-jsonp-server 1.0.0 (json 支持包)
1 Helidon 簡介Helidon 是 Oracle 官方出品的 java 微服務(wù)框架,底層基于 Netty 驅(qū)動(dòng),大致的使用套路和 Vertx 相差不是很多(筆者只是淺淺的了解過 Vertx,可能存在理解不透的情況)。在 2019 年的年初,Helidon 迎來了 1.0.0 的版本更新,api 開始趨于穩(wěn)定。
就目前的版本來看,Oracle 應(yīng)該希望 Helidon 是一個(gè)小而美的輕巧型 java 框架,所以對(duì)其的封裝非常之薄,僅僅是對(duì) Netty 做了一層定制化的處理,同時(shí)也深度整合了 jdk 的相關(guān)接口(畢竟是自家的東西,駕馭能力不一般)。就連 json 的處理都引用了 javax 中的 joup 去做,而不是使用第三方工具包。
Helidon 目前的網(wǎng)絡(luò)資料很少,所以本文主要通過其官方文檔進(jìn)行 Demo 的構(gòu)筑。
一 配置Helidon 中的配置類有兩種,一種是用于讀取配置文件的 Config,另一種是用于配置服務(wù)器對(duì)象的 ServerConfiguration。
先來看 Config:
//配置文件 Config conf = Config //builder .builder() //載入配置文件,可以一次載入多張 .sources( //根據(jù)絕對(duì)路徑去載入配置文件 ConfigSources.file("D://application.yaml"), //到 resource 文件夾下去尋找 ConfigSources.classpath("application2.yaml") ) //創(chuàng)建完成 .build();
再來看 ServerConfiguration:
//創(chuàng)建一個(gè)配置對(duì)象,用于注入一些配置信息 ServerConfiguration config = ServerConfiguration //生成 builder .builder() //address //getLoopbackAddress() 會(huì)獲取到 localhost .bindAddress(InetAddress.getLoopbackAddress()) //getLocalHost() 會(huì)獲取到本機(jī)的 ip //.bindAddress(InetAddress.getLocalHost()) //端口 .port(8080) //工作線程的線程數(shù) .workersCount(10) //增加一個(gè) ServerConfiguration 對(duì)象 //.addSocket("serverConfiguration",ServerConfiguration.builder().build()) //載入配置文件 //.config(conf) //創(chuàng)建完成 .build();二 路由
網(wǎng)絡(luò)博文最簡單的 Hello World 的路由對(duì)象:
//創(chuàng)建最簡單的路由邏輯 Routing rt = Routing //builder .builder() //設(shè)置路由的請求方法和業(yè)務(wù)邏輯 //設(shè)置多種請求方法 .anyOf(List.of(Http.Method.GET, Http.Method.POST,Http.Method.DELETE),"/hello",(req, res) -> res.send("hello world")) //單一請求方法 //.post("/hello",(req, res) -> res.send("hello world")) //.get("/hello",(req, res) -> res.send("hello world")) //創(chuàng)建完成 .build();
結(jié)合業(yè)務(wù)邏輯的路由對(duì)象:
//創(chuàng)建路由對(duì)象 Routing routing = Routing //builder .builder() //注冊 json 解析器 .register(JsonSupport.create()) //添加 url 路由,一個(gè)路由對(duì)象可以添加多個(gè) //可以添加業(yè)務(wù)邏輯的 service 類 .post("/hello", Handler.create(JsonObject.class,new HelloService())) //hello world .get("/hello1",(req, res) -> res.send("hello world")) //創(chuàng)建完成 .build();
HelloService 中處理具體的業(yè)務(wù)邏輯:
//HelloService 必須實(shí)現(xiàn) Handler.EntityHandler class HelloService implements Handler.EntityHandler{ //json 的解析門面對(duì)象 private static final JsonBuilderFactory jsonFactory = Json.createBuilderFactory(Collections.emptyMap()); //核心業(yè)務(wù)類,里面可以寫具體的業(yè)務(wù)邏輯,最后 send(...) 進(jìn) response 里就可以了 @Override public void accept(ServerRequest req, ServerResponse res, Object entity) { //entity 本質(zhì)上就是 JsonObject JsonObject reqJson = (JsonObject)entity; //獲取鍵值對(duì)并打印,這個(gè)操作和 Fastjson 很類似 String ret = reqJson.getString("hello"); System.out.println(ret); //創(chuàng)建要返回的 json object JsonObject msg = jsonFactory //builder .createObjectBuilder() //添加一組 json 的 key - value 鍵值對(duì) .add("message", "Hello") //創(chuàng)建 json 對(duì)象完成 .build(); //json array 的創(chuàng)建方式 //JsonArray array = jsonFactory.createArrayBuilder().build(); //返回 res.send(msg); } }三 服務(wù)器對(duì)象
服務(wù)器對(duì)象:
//啟動(dòng)服務(wù)器 WebServer //創(chuàng)建服務(wù)器對(duì)象 //具體依賴 ServerConfiguration 和 Routing 對(duì)象 .create(config,routing) //開啟服務(wù)器 .start() //獲取 future .toCompletableFuture() //設(shè)置超時(shí)時(shí)間為 10 秒 .get(10, TimeUnit.SECONDS);四 服務(wù)實(shí)現(xiàn)
先來看 WebServer 的 create(...) 方法:
//step 1 //WebServer.class static WebServer create(ServerConfiguration configuration, Routing routing) { //有效性驗(yàn)證 Objects.requireNonNull(routing, "Parameter "routing" is null!"); //builder(...) 方法會(huì)創(chuàng)建一個(gè) WebServer.Builder 對(duì)象,最終在 build() 方法里創(chuàng)建 WebServer return builder(routing).config(configuration) .build(); } //step 2 //WebServer.Builder.class public WebServer build() { //routings 是一個(gè) Map 對(duì)象,用來儲(chǔ)存更多的路由對(duì)象 String unpairedRoutings = routings .keySet() .stream() .filter(routingName -> configuration == null || configuration.socket(routingName) == null) .collect(Collectors.joining(", ")); //有效性驗(yàn)證 if (!unpairedRoutings.isEmpty()) { throw new IllegalStateException("No server socket configuration found for named routings: " + unpairedRoutings); } //NettyWebServer 是 WebServer 接口的實(shí)現(xiàn)類 //defaultRouting 是上方 create(...) 方法存入的路由對(duì)象 WebServer result = new NettyWebServer(configuration == null ? ServerBasicConfig.DEFAULT_CONFIGURATION : configuration, defaultRouting, routings); //默認(rèn)的路由對(duì)象即為 RequestRouting 類型 //此處給路由對(duì)象添加回調(diào)方法 if (defaultRouting instanceof RequestRouting) { ((RequestRouting) defaultRouting).fireNewWebServer(result); } //返回 return result; } //step 3 //NettyWebServer.class NettyWebServer(ServerConfiguration config, Routing routing, MapnamedRoutings) { //獲取所有的 SocketConfiguration 對(duì)象 //即為之前 addSocket(...) 方法存入的對(duì)象,也包括主配置對(duì)象自身 Set > sockets = config.sockets().entrySet(); //創(chuàng)建兩個(gè)線程 this.bossGroup = new NioEventLoopGroup(sockets.size()); this.workerGroup = config.workersCount() <= 0 ? new NioEventLoopGroup() : new NioEventLoopGroup(config.workersCount()); this.configuration = config; //循環(huán)所有的配置對(duì)象,然后分別創(chuàng)建 ServerBootstrap 對(duì)象并保存 for (Map.Entry entry : sockets) { String name = entry.getKey(); SocketConfiguration soConfig = entry.getValue(); ServerBootstrap bootstrap = new ServerBootstrap(); JdkSslContext sslContext = null; if (soConfig.ssl() != null) { sslContext = new JdkSslContext(soConfig.ssl(), false, ClientAuth.NONE); } if (soConfig.backlog() > 0) { bootstrap.option(ChannelOption.SO_BACKLOG, soConfig.backlog()); } if (soConfig.timeoutMillis() > 0) { bootstrap.option(ChannelOption.SO_TIMEOUT, soConfig.timeoutMillis()); } if (soConfig.receiveBufferSize() > 0) { bootstrap.option(ChannelOption.SO_RCVBUF, soConfig.receiveBufferSize()); } //childHandler 是核心的業(yè)務(wù) handler,使用者寫的業(yè)務(wù)邏輯也都在里面 HttpInitializer childHandler = new HttpInitializer(sslContext, namedRoutings.getOrDefault(name, routing), this); //儲(chǔ)存 handler initializers.add(childHandler); //配置 Netty 的啟動(dòng)器對(duì)象 bootstrap.group(bossGroup, workerGroup) .channel(NioServerSocketChannel.class) .handler(new LoggingHandler(LogLevel.DEBUG)) .childHandler(childHandler); //儲(chǔ)存啟動(dòng)器 bootstraps.put(name, bootstrap); } }
NettyWebServer 中的構(gòu)造器代碼其實(shí)本質(zhì)上就是半段 Netty 的啟動(dòng)模板代碼。
主要是為了兼容多配置文件帶來的多啟動(dòng)器需求,所以做了一個(gè) for 循環(huán)。在多數(shù)時(shí)候其實(shí)就只有一個(gè)配置對(duì)象,也就只有一個(gè)啟動(dòng)器。
而另外半段 Netty 模板代碼在 start(...) 方法中:
//NettyWebServer.class public synchronized CompletionStage五 一點(diǎn)嘮叨start() { //started 是一個(gè) boolean 類型的變量,用來判斷該服務(wù)器對(duì)象是否啟動(dòng) if (!started) { channelsUpFuture.thenAccept(startFuture::complete) .exceptionally(throwable -> { if (channels.isEmpty()) { startFailureHandler(throwable); } for (Channel channel : channels.values()) { channel.close(); } return null; }); channelsCloseFuture.whenComplete((webServer, throwable) -> shutdown(throwable)); //在之前 NettyWebServer 的構(gòu)造器中儲(chǔ)存下來的所有 Netty 啟動(dòng)器 Set > bootstrapEntries = bootstraps.entrySet(); int bootstrapsSize = bootstrapEntries.size(); for (Map.Entry entry : bootstrapEntries) { ServerBootstrap bootstrap = entry.getValue(); String name = entry.getKey(); SocketConfiguration socketConfig = configuration.socket(name); if (socketConfig == null) { throw new IllegalStateException( "no socket configuration found for name: " + name); } int port = socketConfig.port() <= 0 ? 0 : socketConfig.port(); if (channelsUpFuture.isCompletedExceptionally()) { break; } try { //bootstrap 的設(shè)置 //Helidon 中用監(jiān)聽器方式去異步啟動(dòng) Netty bootstrap.bind(configuration.bindAddress(), port).addListener(channelFuture -> { if (!channelFuture.isSuccess()) { LOGGER.info(() -> "Channel "" + name + "" startup failed with message "" + channelFuture.cause().getMessage() + ""."); channelsUpFuture.completeExceptionally(new IllegalStateException("Channel startup failed: " + name, channelFuture.cause())); return; } Channel channel = ((ChannelFuture) channelFuture).channel(); LOGGER.info(() -> "Channel "" + name + "" started: " + channel); channels.put(name, channel); channel.closeFuture().addListener(future -> { LOGGER.info(() -> "Channel "" + name + "" closed: " + channel); channels.remove(name); if (channelsUpFuture.isCompletedExceptionally()) { if (channels.isEmpty()) { channelsUpFuture.exceptionally(this::startFailureHandler); } else if (future.cause() != null) { LOGGER.log(Level.WARNING, "Startup failure channel close failure", new IllegalStateException(future.cause())); } } else { if (!future.isSuccess()) { channelsCloseFuture.completeExceptionally(new IllegalStateException("Channel stop failure.", future.cause())); } else if (channels.isEmpty()) { channelsCloseFuture.complete(this); } } }); if (channelsUpFuture.isCompletedExceptionally()) { channel.close(); } if (channels.size() >= bootstrapsSize) { LOGGER.finer(() -> "All channels started: " + channels.size()); channelsUpFuture.complete(this); } }); } catch (RejectedExecutionException e) { if (shutdownThreadGroupsInitiated.get()) { break; } else { throw e; } } } started = true; LOGGER.fine(() -> "All channels startup routine initiated: " + bootstrapsSize); } //返回一個(gè) CompletableFuture 對(duì)象 return startFuture; }
· Helidon 框架有很多其它組件,比如 Security、Data Source 等,有待繼續(xù)研究
· 很時(shí)髦很輕巧,其實(shí)就是簡單封裝了一下 Netty,大多數(shù)的組件也是直接用 jdk 自帶的
· json 部分其實(shí)就是把 javax.json 包里的工具利用了一下,但是筆者個(gè)人覺得沒比第三方好用
· 目前來看僅僅是 Oracle 在自 high,社區(qū)熱度還不夠,潛力值不好估計(jì)
六 服務(wù)端全部代碼import io.helidon.common.http.Http; import io.helidon.config.Config; import io.helidon.config.ConfigSources; import io.helidon.media.jsonp.server.JsonSupport; import io.helidon.webserver.*; import javax.json.Json; import javax.json.JsonBuilderFactory; import javax.json.JsonObject; import java.net.InetAddress; import java.util.Collections; import java.util.List; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; public class HelidonService { public void run() throws InterruptedException, ExecutionException, TimeoutException { //配置文件 // Config conf = Config // // //builder // .builder() // // //載入配置文件,可以一次載入多張 // .sources( // //根據(jù)絕對(duì)路徑去載入配置文件 // ConfigSources.file("D://application.yaml"), // //到 resource 文件夾下去尋找 // ConfigSources.classpath("application2.yaml") // ) // // //創(chuàng)建完成 // .build(); ServerConfiguration c = ServerConfiguration.builder().build(); //創(chuàng)建一個(gè)配置對(duì)象,用于注入一些配置信息 ServerConfiguration config = ServerConfiguration //生成 builder .builder() //address //getLoopbackAddress() 會(huì)獲取到 localhost .bindAddress(InetAddress.getLoopbackAddress()) //getLocalHost() 會(huì)獲取到本機(jī)的 ip //.bindAddress(InetAddress.getLocalHost()) //端口 .port(8080) //工作線程的線程數(shù) .workersCount(10) //增加一個(gè) ServerConfiguration 對(duì)象 //.addSocket("serverConfiguration",ServerConfiguration.builder().build()) //載入配置文件 //.config(conf) //創(chuàng)建完成 .build(); //創(chuàng)建最簡單的路由邏輯 // Routing rt = Routing // // //builder // .builder() // // //設(shè)置路由的請求方法和業(yè)務(wù)邏輯 // //設(shè)置多種請求方法 // .anyOf(List.of(Http.Method.GET, Http.Method.POST),"/hello",(req, res) -> res.send("hello world")) // // //單一請求方法 // //.post("/hello",(req, res) -> res.send("hello world")) // //.get("/hello",(req, res) -> res.send("hello world")) // // //創(chuàng)建完成 // .build(); //創(chuàng)建路由對(duì)象 Routing routing = Routing //builder .builder() //注冊 json 解析器 .register(JsonSupport.create()) //添加 url 路由,一個(gè)路由對(duì)象可以添加多個(gè) //可以添加業(yè)務(wù)邏輯的 service 類 .post("/hello", Handler.create(JsonObject.class,new HelloService())) //hello world .get("/hello1",(req, res) -> res.send("hello world")) //創(chuàng)建完成 .build(); //啟動(dòng)服務(wù)器 WebServer //創(chuàng)建服務(wù)器對(duì)象 .create(config,routing) //開啟服務(wù)器 .start() //獲取 future .toCompletableFuture() //設(shè)置超時(shí)時(shí)間 .get(10, TimeUnit.SECONDS); } //main 方法 public static void main(String[] args) throws InterruptedException, ExecutionException, TimeoutException { new HelidonService().run(); } } class HelloService implements Handler.EntityHandler{ //json 的解析門面對(duì)象 private static final JsonBuilderFactory jsonFactory = Json.createBuilderFactory(Collections.emptyMap()); @Override public void accept(ServerRequest req, ServerResponse res, Object entity) { //entity 本質(zhì)上就是 JsonObject JsonObject reqJson = (JsonObject)entity; //獲取鍵值對(duì)并打印,這個(gè)操作和 Fastjson 很類似 String ret = reqJson.getString("hello"); System.out.println(ret); //創(chuàng)建要返回的 json object JsonObject msg = jsonFactory //builder .createObjectBuilder() //添加一組 json 的 key - value 鍵值對(duì) .add("message", "Hello") //創(chuàng)建 json 對(duì)象完成 .build(); //json array 的創(chuàng)建方式 //JsonArray array = jsonFactory.createArrayBuilder().build(); //返回 res.send(msg); } }
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/73710.html
摘要:近日,推出了一個(gè)新的開源框架,該項(xiàng)目是一個(gè)用于創(chuàng)建基于微服務(wù)的應(yīng)用程序的庫集合。下圖說明了和所屬的微服務(wù)框架類別。啟用后,會(huì)將其跟蹤事件發(fā)送到。 近日,Oracle推出了一個(gè)新的開源框架Helidon,該項(xiàng)目是一個(gè)用于創(chuàng)建基于微服務(wù)的應(yīng)用程序的Java庫集合。和Payara Micro、Thorntail(之前的WildFly Swarm)、OpenLiberty、TomEE等項(xiàng)目一樣...
摘要:今天整理了一下近大半年以來的一些文章,和我的預(yù)期一樣,很多文章我都忘記自己曾經(jīng)寫過了,這個(gè)記錄的過程讓我也有了新的理解。希望大家,收藏,點(diǎn)贊,加轉(zhuǎn)發(fā)。 今天整理了一下近大半年以來的一些文章,和我的預(yù)期一樣,很多文章我都忘記自己曾經(jīng)寫過了,這個(gè)記錄的過程讓我也有了新的理解。希望大家,收藏,點(diǎn)贊,加轉(zhuǎn)發(fā)。 面試必備 面試必備:深入Spring MVC DispatchServlet 源碼...
摘要:今天整理了一下近大半年以來的一些文章,和我的預(yù)期一樣,很多文章我都忘記自己曾經(jīng)寫過了,這個(gè)記錄的過程讓我也有了新的理解。希望大家,收藏,點(diǎn)贊,加轉(zhuǎn)發(fā)。 今天整理了一下近大半年以來的一些文章,和我的預(yù)期一樣,很多文章我都忘記自己曾經(jīng)寫過了,這個(gè)記錄的過程讓我也有了新的理解。希望大家,收藏,點(diǎn)贊,加轉(zhuǎn)發(fā)。 面試必備 面試必備:深入Spring MVC DispatchServlet 源碼...
摘要:作為面試官,我是如何甄別應(yīng)聘者的包裝程度語言和等其他語言的對(duì)比分析和主從復(fù)制的原理詳解和持久化的原理是什么面試中經(jīng)常被問到的持久化與恢復(fù)實(shí)現(xiàn)故障恢復(fù)自動(dòng)化詳解哨兵技術(shù)查漏補(bǔ)缺最易錯(cuò)過的技術(shù)要點(diǎn)大掃盲意外宕機(jī)不難解決,但你真的懂?dāng)?shù)據(jù)恢復(fù)嗎每秒 作為面試官,我是如何甄別應(yīng)聘者的包裝程度Go語言和Java、python等其他語言的對(duì)比分析 Redis和MySQL Redis:主從復(fù)制的原理詳...
閱讀 1176·2021-11-15 18:14
閱讀 3649·2021-11-15 11:37
閱讀 770·2021-09-24 09:47
閱讀 2457·2021-09-04 16:48
閱讀 2190·2019-08-30 15:53
閱讀 2396·2019-08-30 15:53
閱讀 401·2019-08-30 11:20
閱讀 1247·2019-08-29 16:08