netty与其应用moco-runner的学习笔记

前言:
moco-runner是github上一个基于netty的mock开源项目
这里以Http服务启动时的moco-runner为例介绍
以Socket服务启动时基本处理逻辑是一致的

启动流程

Netty服务端创建时序图

Netty服务端创建时序图.png

结合Netty的Demo代码

moco-runner封装程度比较高,用Demo代码结合上面的时序图自己捋一下基础实现

public class NettyDemo {    
    public static void main(String[] args) {
        NioEventLoopGroup boosGroup = new NioEventLoopGroup();
        NioEventLoopGroup workerGroup = new NioEventLoopGroup();
        
        final ServerBootstrap serverBootstrap = new ServerBootstrap(); 
                // 1. 创建ServerBootStrap实例
                // 在moco-runner中 : MocoServer类 : start(final int port, final ChannelInitializer pipelineFactory)
        serverBootstrap
            .group(boosGroup, workerGroup)                                            
                // 2. 设置并绑定Reactor线程池:EventLoopGroup, EventLoop就是处理所有注册到本线程的Selector上面的Channel
                // 在moco-runner中 : MocoServer类 : group = new NioEventLoopGroup(0, MocoExecutors.executor());
            .channel(NioServerSocketChannel.class)
                // 3. 设置并绑定服务端的channel
            .option(ChannelOption.SO_BACKLOG, 1024)
            .childOption(ChannelOption.SO_KEEPALIVE, true)
            .childOption(ChannelOption.TCP_NODELAY, true)
            .childHandler(new ChannelInitializer() {
                // 4和5. 创建处理网络事件的ChannelPipeline和handler,网络时间以流的形式在其中流转,handler完成多数的功能定制:比如编解码 SSl安全认证
                // 在moco-runner中 : ServerRunner类 : ((MocoHttpServer)configuration).channelInitializer()
                // 在moco-runner中 : MocoHttpServer的channelInitializer实现如下(省略部分非关键代码)
                private ActualHttpServer serverSetting;
                
                @Override
                protected void initChannel(SocketChannel ch) throws Exception {
                    ChannelPipeline pipeline = ch.pipeline();
                    pipeline.addFirst("ssl", serverSetting.sslHandler().get());
                    ServerConfig serverConfig = serverSetting.getServerConfig();
                    pipeline.addLast("codec", new HttpServerCodec(4096,
                            serverConfig.getHeaderSize(),
                            8192, false));
                    pipeline.addLast("aggregator", new HttpObjectAggregator(serverConfig.getContentLength()));
                    pipeline.addLast("handler", new MocoHandler(serverSetting));
                }
            });
        serverBootstrap.bind(8001);
                // 6. 绑定监听端口并启动服务端
    }
}

业务处理流程

moco-runner主Handler : MocoHandler

SimpleChannelInboundHandler

SimpleChannelInboundHandler是用来接收解码消息,并处理该数据业务逻辑的一个抽象类实现。
MocoHandler继承自SimpleChannelInboundHandler处理Mock流程
对于SimpleChannelInboundHandler中的T为待处理的消息的Java类型

对于MocoHandler,所有Mock的业务逻辑都在channelRead0()中实现

MocoHandler的构造器参数(ActualHttpServer)serverSetting从哪来?是什么?

回到MocoHandler的实例化过程:

  1. MocoHandler在MocoHttpServer类的channelInitializer方法中实例化,构造参数(ActualHttpServer)serverSetting又来自于MocoHttpServer的构造器参数
  2. MocoHttpServer在Runner类的类方法runner(final HttpServer server)中被实例化,构造参数(HttpServer)server又来自于runner方法的参数
  3. runner在StandaloneRunner类中的newRunner方法被调用
  4. newRunner方法在StandaloneRunner类中的run方法被调用
  5. run方法在JsonRunner类中的run方法被调用,参数(Server)server在JsonRunner的构造方法中
    private JsonRunner(final Iterable settings, final StartArgs startArgs) {
        this.server = newServer(settings, startArgs);
    }

通过以下方法组装

    private Server newServer(final Iterable settings, final StartArgs startArgs) {
        // 省略对Socket服务器的判断与处理
        return createHttpServer(settings, startArgs);
    }
    private HttpServer createHttpServer(final Iterable settings, final StartArgs startArgs) {
        HttpServer targetServer = createBaseHttpServer(settings, startArgs);
        return targetServer;
    }
    private HttpServer createBaseHttpServer(final Iterable settings,
                                            final StartArgs startArgs) {
        HttpServer targetServer = createHttpServer(startArgs);
        // 遍历settings
        for (RunnerSetting setting : settings) {
            // httpParser.parseServer()最终是在BaseActualServer类中处理配置文件json列表中的request/response匹配响应配置(接口挡板配置),获取对应挡板配置的HttpServer实例
            HttpServer parsedServer = httpParser.parseServer(setting.getStreams(),
                    startArgs.getPort(), toConfigs(setting));
            // 合并parsedServer与原targetServer
            targetServer = mergeServer(targetServer, parsedServer);
        }
        return targetServer;
    }

其中

    private HttpServer createHttpServer(final StartArgs startArgs) {
        // 省略Https的情况
        return ActualHttpServer.createLogServer(startArgs.getPort().or(0));
    }

这里来看下最终返回的HttpServer对象targetServer,也就是MocoHandler类的构造器参数(ActualHttpServer)serverSetting


HttpServer

它是mocorunner定义的一个接口,定义了post/get/put/delete/proxy等接口方法
而我们实际最终使用的ActualHttpServer类实例是它的一个子类,ActualHttpServer直接继承自HttpConfiguration,
而HttpConfiguration实现了HttpServer接口的同时又继承自BaseActualServer

单从以上服务器的启动逻辑来看,BaseActualServer是我们目前更需要关注的类,
它的实例实际持有了挡板的配置项(List>)settings,
并可以通过BaseActualServer的mergeServer(final U thatServer)方法,
将thatServer实例的settings合并到当前BaseActualServer的实例中

MocoHandler类实例持有的属性ImmutableList> settings

看清楚以上逻辑后,这里就知道了MocoHandler持有的属性settings,就是来自于前期经过多次merge的BaseActualServer实例的settings,保存的是挡板的配置项

这个settings在MocoHandler的业务处理方法
channelRead0(final ChannelHandlerContext ctx, final FullHttpRequest message)
的子调用
doGetHttpResponse(final HttpRequest request)
中被遍历>匹配>命中则调用writeToResponse处理实际业务并作返回

        for (Setting setting : settings) {
            if (setting.match(request)) {
                logger.info(String.format("Response by setting : %s", setting.toString()));
                setting.writeToResponse(context);
                return httpResponse;
            }
        }

你可能感兴趣的:(netty与其应用moco-runner的学习笔记)