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<? extends Channel> 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<SocketChannel>() {
                // 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<T>是用来接收解码消息,并处理该数据业务逻辑的一个抽象类实现。
MocoHandler继承自SimpleChannelInboundHandler<T>处理Mock流程
对于SimpleChannelInboundHandler<T>中的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<? extends RunnerSetting> settings, final StartArgs startArgs) {
        this.server = newServer(settings, startArgs);
    }

通过以下方法组装

    private Server newServer(final Iterable<? extends RunnerSetting> settings, final StartArgs startArgs) {
        // 省略对Socket服务器的判断与处理
        return createHttpServer(settings, startArgs);
    }
    private HttpServer createHttpServer(final Iterable<? extends RunnerSetting> settings, final StartArgs startArgs) {
        HttpServer targetServer = createBaseHttpServer(settings, startArgs);
        return targetServer;
    }
    private HttpServer createBaseHttpServer(final Iterable<? extends RunnerSetting> 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<Setting<T>>)settings,
并可以通过BaseActualServer的mergeServer(final U thatServer)方法,
将thatServer实例的settings合并到当前BaseActualServer的实例中

MocoHandler类实例持有的属性ImmutableList<Setting<HttpResponseSetting>> 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;
            }
        }
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,723评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,080评论 2 379
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,604评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,440评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,431评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,499评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,893评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,541评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,751评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,547评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,619评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,320评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,890评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,896评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,137评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,796评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,335评论 2 342