前言:
moco-runner是github上一个基于netty的mock开源项目
这里以Http服务启动时的moco-runner为例介绍
以Socket服务启动时基本处理逻辑是一致的
启动流程
Netty服务端创建时序图
结合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
MocoHandler继承自SimpleChannelInboundHandler
对于SimpleChannelInboundHandler
对于MocoHandler,所有Mock的业务逻辑都在channelRead0()中实现
MocoHandler的构造器参数(ActualHttpServer)serverSetting从哪来?是什么?
回到MocoHandler的实例化过程:
- MocoHandler在MocoHttpServer类的channelInitializer方法中实例化,构造参数(ActualHttpServer)serverSetting又来自于MocoHttpServer的构造器参数
- MocoHttpServer在Runner类的类方法runner(final HttpServer server)中被实例化,构造参数(HttpServer)server又来自于runner方法的参数
- runner在StandaloneRunner类中的newRunner方法被调用
- newRunner方法在StandaloneRunner类中的run方法被调用
- 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
它是mocorunner定义的一个接口,定义了post/get/put/delete/proxy等接口方法
而我们实际最终使用的ActualHttpServer类实例是它的一个子类,ActualHttpServer直接继承自HttpConfiguration,
而HttpConfiguration实现了HttpServer接口的同时又继承自BaseActualServer
单从以上服务器的启动逻辑来看,BaseActualServer是我们目前更需要关注的类,
它的实例实际持有了挡板的配置项(List
并可以通过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;
}
}