マイクロサービスフレームワークHelidonの使用について

20157 ワード

前期準備ゼロ
バージョン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マイクロサービスフレームワークであり、下位層はNetty駆動に基づいており、大まかな使用方法とVertxの差は多くない(筆者はVertxを浅く理解しているだけで、理解できない場合があるかもしれない).2019年の初め、Helidonは1.0.0のバージョン更新を迎え、apiは安定し始めた.
現在のバージョンでは、OracleはHelidonが小さくて美しい軽量javaフレームワークであることを望んでいるはずなので、パッケージは非常に薄く、Nettyに対してカスタマイズされた処理を行っただけで、jdkの関連インタフェースを深く統合しています(結局は自分のもので、制御能力は普通ではありません).jsonの処理でもjavaxのjoupを参照して、サードパーティのツールパッケージを使用するのではありません.
Helidonの現在のネットワーク資料は少ないので,本稿では主にその公式文書を通じてDemoの構築を行う.
一構成
Helidonのコンフィギュレーションクラスには、コンフィギュレーションファイルを読み込むためのConfigと、サーバオブジェクトを構成するためのServer Configurationの2種類があります.
まずConfigを見てみましょう.
//    
Config conf = Config

                    //builder
                    .builder()

                    //      ,        
                    .sources(
                            //             
                            ConfigSources.file("D://application.yaml"),
                            //  resource        
                            ConfigSources.classpath("application2.yaml")
                    )

                    //    
                    .build();

ServerConfigurationを見てみましょう.
//        ,          
ServerConfiguration config = ServerConfiguration

                                                //   builder
                                                .builder()

                                                //address

                                                //getLoopbackAddress()      localhost
                                                .bindAddress(InetAddress.getLoopbackAddress())

                                                //getLocalHost()         ip
                                                //.bindAddress(InetAddress.getLocalHost())

                                                //  
                                                .port(8080)

                                                //        
                                                .workersCount(10)

                                                //     ServerConfiguration   
                                                //.addSocket("serverConfiguration",ServerConfiguration.builder().build())

                                                //      
                                                //.config(conf)

                                                //    
                                                .build();

にじゅうろ
ネットワークブログの最も簡単なHello Worldのルーティングオブジェクト:
//          
Routing rt = Routing

                    //builder
                    .builder()

                    //              
                    //        
                    .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"))

                    //    
                    .build();

ビジネスロジックを組み合わせたルーティング・オブジェクト:
//      
Routing routing = Routing

                        //builder
                        .builder()

                        //   json    
                        .register(JsonSupport.create())

                        //   url   ,            

                        //          service  
                        .post("/hello", Handler.create(JsonObject.class,new HelloService()))

                        //hello world
                        .get("/hello1",(req, res) -> res.send("hello world"))

                        //    
                        .build();

HelloServiceでは、特定のビジネスロジックを処理します.
//HelloService      Handler.EntityHandler
class HelloService implements Handler.EntityHandler{

    //json        
    private static final JsonBuilderFactory jsonFactory = Json.createBuilderFactory(Collections.emptyMap());

    //     ,            ,   send(...)   response      
    @Override
    public void accept(ServerRequest req, ServerResponse res, Object entity) {

        //entity       JsonObject
        JsonObject reqJson = (JsonObject)entity;

        //        ,      Fastjson    
        String ret = reqJson.getString("hello");
        System.out.println(ret);

        //       json object
        JsonObject msg = jsonFactory

                                //builder
                                .createObjectBuilder()

                                //     json   key - value    
                                .add("message", "Hello")

                                //   json     
                                .build();

        //json array      
        //JsonArray array = jsonFactory.createArrayBuilder().build();

        //  
        res.send(msg);
    }
}

3サーバオブジェクト
サーバオブジェクト:
//     
WebServer

        //       
        //     ServerConfiguration   Routing   
        .create(config,routing)

        //     
        .start()

        //   future
        .toCompletableFuture()

        //        10  
        .get(10, TimeUnit.SECONDS);

四サービス実現
まずWebServerのcreate()メソッドを見てみましょう.
//step 1
//WebServer.class
static WebServer create(ServerConfiguration configuration, Routing routing) {
    //     
    Objects.requireNonNull(routing, "Parameter 'routing' is null!");

    //builder(...)         WebServer.Builder   ,    build()       WebServer
    return builder(routing).config(configuration)
                            .build();
}

//step 2
//WebServer.Builder.class
public WebServer build() {

    //routings     Map   ,           
    String unpairedRoutings = routings
                                    .keySet()
                                    .stream()
                                    .filter(routingName -> configuration == null || configuration.socket(routingName) == null)
                                    .collect(Collectors.joining(", "));

    //     
    if (!unpairedRoutings.isEmpty()) {
        throw new IllegalStateException("No server socket configuration found for named routings: " + unpairedRoutings);
    }

    //NettyWebServer   WebServer       
    //defaultRouting     create(...)          
    WebServer result = new NettyWebServer(configuration == null
                                                    ? ServerBasicConfig.DEFAULT_CONFIGURATION
                                                    : configuration,
                                            defaultRouting, routings);
    
    //          RequestRouting   
    //             
    if (defaultRouting instanceof RequestRouting) {
        ((RequestRouting) defaultRouting).fireNewWebServer(result);
    }

    //  
    return result;
}

//step 3
//NettyWebServer.class
NettyWebServer(ServerConfiguration config,
                   Routing routing,
                   Map namedRoutings) {
    
    //      SocketConfiguration   
    //     addSocket(...)        ,          
    Set> sockets = config.sockets().entrySet();

    //      
    this.bossGroup = new NioEventLoopGroup(sockets.size());
    this.workerGroup = config.workersCount() <= 0 ? new NioEventLoopGroup() : new NioEventLoopGroup(config.workersCount());

    this.configuration = config;

    //         ,       ServerBootstrap      
    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        handler,              
        HttpInitializer childHandler = new HttpInitializer(sslContext, namedRoutings.getOrDefault(name, routing), this);

        //   handler
        initializers.add(childHandler);

        //   Netty       
        bootstrap.group(bossGroup, workerGroup)
                    .channel(NioServerSocketChannel.class)
                    .handler(new LoggingHandler(LogLevel.DEBUG))
                    .childHandler(childHandler);

        //     
        bootstraps.put(name, bootstrap);
    }
}

NettyWebServerのコンストラクタコードは、本質的にはセグメントNettyの起動テンプレートコードです.
主にマルチプロファイルによるマルチイニシエータのニーズを互換化するためにforループを行いました.多くの場合、構成オブジェクトは1つしかなく、イニシエータは1つしかありません.
一方、他のセグメントNettyテンプレートコードはstart()メソッドにあります.
//NettyWebServer.class
public synchronized CompletionStage start() {

    //started     boolean      ,              
    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              Netty    
        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    
                //Helidon              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);
    }

    //     CompletableFuture   
    return startFuture;
}

メーデーの小言
・Helidonフレームワークには、Security、Data Sourceなど、他にも多くのコンポーネントがあり、引き続き検討する必要がある
・おしゃれで軽くて、実はNettyを簡単にパッケージして、多くのコンポーネントもjdkで直接持っています
・json部分はjavax.jsonパッケージのツールを利用していますが、個人的には第三者より使いやすいとは思いません
・現在のところ、Oracleが上昇しているだけで、コミュニティの熱がまだ足りず、潜在力値が推定できない
六サービス・エンドのすべてのコード
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(
//                                    //             
//                                    ConfigSources.file("D://application.yaml"),
//                                    //  resource        
//                                    ConfigSources.classpath("application2.yaml")
//                            )
//
//                            //    
//                            .build();

        ServerConfiguration c = ServerConfiguration.builder().build();

        //        ,          
        ServerConfiguration config = ServerConfiguration

                                                        //   builder
                                                        .builder()

                                                        //address

                                                        //getLoopbackAddress()      localhost
                                                        .bindAddress(InetAddress.getLoopbackAddress())

                                                        //getLocalHost()         ip
                                                        //.bindAddress(InetAddress.getLocalHost())

                                                        //  
                                                        .port(8080)

                                                        //        
                                                        .workersCount(10)

                                                        //     ServerConfiguration   
                                                        //.addSocket("serverConfiguration",ServerConfiguration.builder().build())

                                                        //      
                                                        //.config(conf)

                                                        //    
                                                        .build();

          //          
//        Routing rt = Routing
//
//                                //builder
//                                .builder()
//
//                                //              
//                                //        
//                                .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"))
//
//                                //    
//                                .build();


        //      
        Routing routing = Routing

                                //builder
                                .builder()

                                //   json    
                                .register(JsonSupport.create())

                                //   url   ,            

                                //          service  
                                .post("/hello", Handler.create(JsonObject.class,new HelloService()))

                                //hello world
                                .get("/hello1",(req, res) -> res.send("hello world"))

                                //    
                                .build();

        //     
        WebServer

                //       
                .create(config,routing)

                //     
                .start()

                //   future
                .toCompletableFuture()

                //      
                .get(10, TimeUnit.SECONDS);
    }

    //main   
    public static void main(String[] args) throws InterruptedException, ExecutionException, TimeoutException {
        new HelidonService().run();
    }
}

class HelloService implements Handler.EntityHandler{

    //json        
    private static final JsonBuilderFactory jsonFactory = Json.createBuilderFactory(Collections.emptyMap());

    @Override
    public void accept(ServerRequest req, ServerResponse res, Object entity) {

        //entity       JsonObject
        JsonObject reqJson = (JsonObject)entity;

        //        ,      Fastjson    
        String ret = reqJson.getString("hello");
        System.out.println(ret);

        //       json object
        JsonObject msg = jsonFactory

                                //builder
                                .createObjectBuilder()

                                //     json   key - value    
                                .add("message", "Hello")

                                //   json     
                                .build();

        //json array      
        //JsonArray array = jsonFactory.createArrayBuilder().build();

        //  
        res.send(msg);
    }
}