深度解析Cat源码系列专栏点击访问
文章目录
CAT源码分析5 - 服务端消息接收
1. TcpSocketReceiver初始化
在CAT服务端启动初始化过程中,CatHomeModule在初始化时,会通过setUp()和execute()两个方法实现对消息接收器的初始化。
- 通过setUp方法完成对Netty客户端网络通信的初始化
- execute方法中有其他功能的初始化逻辑,包括对消息消费者的初始化
整体的初始化逻辑如下图
com.dianping.cat.CatHomeModule#setup 设置方法如下
- 通过Peluxs容器实例化TcpSocketReceiver,并执行init初始化
- 对进程设置销毁监听
@Override
protected void setup(ModuleContext ctx) throws Exception {
final TcpSocketReceiver messageReceiver = ctx.lookup(TcpSocketReceiver.class);
messageReceiver.init();
// 销毁监听
Runtime.getRuntime().addShutdownHook(new Thread() {
@Override
public void run() {
messageReceiver.destory();
}
});
}
com.dianping.cat.analysis.TcpSocketReceiver#init的初始化逻辑如下:
- 主要负责实例化Netty客户端,开启对2280端口通信的监听
- 选择网络通信方式时,根据操作系统,linux下使用epoll更加高效
- 绑定消息编解码器、设置网络配置参数
- m_logger是通过Plexus容器的LoggerManager自动初始化注入的,LoggerManager默认设置为Log4j。该logger的打印是落磁盘的
public void init() {
try {
// 默认2280端口启动Netty客户端
startServer(m_port);
} catch (Exception e) {
m_logger.error(e.getMessage(), e);
}
}
public synchronized void startServer(int port) throws InterruptedException {
// 根据操作系统,设置netty的相关工作线程数量和channel通信方式
boolean linux = getOSMatches("Linux") || getOSMatches("LINUX");
int threads = 24;
ServerBootstrap bootstrap = new ServerBootstrap();
m_bossGroup = linux ? new EpollEventLoopGroup(threads) : new NioEventLoopGroup(threads);
m_workerGroup = linux ? new EpollEventLoopGroup(threads) : new NioEventLoopGroup(threads);
bootstrap.group(m_bossGroup, m_workerGroup);
// linux使用epoll
bootstrap.channel(linux ? EpollServerSocketChannel.class : NioServerSocketChannel.class);
bootstrap.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
ChannelPipeline pipeline = ch.pipeline();
// pipeline添加消息编解码器
pipeline.addLast("decode", new MessageDecoder());
pipeline.addLast("encode", new ClientMessageEncoder());
}
});
// 对bootstrap进行网络通信配置
bootstrap.childOption(ChannelOption.SO_REUSEADDR, true);
bootstrap.childOption(ChannelOption.TCP_NODELAY, true);
bootstrap.childOption(ChannelOption.SO_KEEPALIVE, true);
bootstrap.childOption(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT);
try {
m_future = bootstrap.bind(port).sync();
m_logger.info("start netty server!");
} catch (Exception e) {
m_logger.error("Started Netty Server Failed:" + port, e);
}
}
2. MessageDecoder消息解码器
MessageDecoder继承了ByteToMessageDecoder类,在netty通道上有消息进入时会被调用。对Byte字节码进行解析。且,MessageDecoder是TcpSocketReceiver的一个内部类,直接引用了后者注入的MessageHandler和ServerStatisticManager,是服务端消息处理的入口。
在其代码中,首先对消息定长读取,获取消息的长度,实现对半包/粘包问题的处理。之后,读到消息体,通过NativeMessa