RocketMQ作为一款高性能消息中间件,namesrv、broker、producer、consumer之间的通信性能尤为重要,RocketMQ使用了高性能通信框架netty。
RocketMQ的网络通信模块remoting包封装了netty的使用,主要启动类是NettyRemotingServer
RocketMQ是怎么使用netty的
以下是简化了的RocketMQ中的源码,需要了解以下几个线程池的作用
#创建编解码线程池
DefaultEventExecutorGroup defaultEventExecutorGroup = new DefaultEventExecutorGroup(8);
#创建serverBootstrap
ServerBootstrap serverBootstrap = new ServerBootstrap();
#创建boss线程组和work线程组,其中SocketChannel会自动根据OS的类型选择NIO和Epoll
EventLoopGroup eventLoopGroupBoss;
EventLoopGroup eventLoopGroupSelector
if (useEpoll()) {
eventLoopGroupBoss = new EpollEventLoopGroup(1);
eventLoopGroupSelector = new EpollEventLoopGroup(3);
} else {
eventLoopGroupBoss = new NioEventLoopGroup(1);
eventLoopGroupSelector = new NioEventLoopGroup(3);
}
#设置编解码处理器NettyEncoder和NettyDecoder,业务转发处理器NettyServerHandler到netty的pipeline中
ServerBootstrap childHandler = serverBootstrap.group(eventLoopGroupBoss, eventLoopGroupSelector)
.channel(useEpoll() ? EpollServerSocketChannel.class : NioServerSocketChannel.class)
.option(ChannelOption.SO_BACKLOG, 1024)
.option(ChannelOption.SO_REUSEADDR, true)
.option(ChannelOption.SO_KEEPALIVE, false)
.childOption(ChannelOption.TCP_NODELAY, true)
.childOption(ChannelOption.SO_SNDBUF, 65535)
.childOption(ChannelOption.SO_RCVBUF, 65535)
.localAddress(new InetSocketAddress(9876))
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
public void initChannel(SocketChannel ch) throws Exception {
ch.pipeline()
.addLast(defaultEventExecutorGroup, HANDSHAKE_HANDLER_NAME, handshakeHandler)
.addLast(defaultEventExecutorGroup,
encoder,
new NettyDecoder(),
new IdleStateHandler(0, 0, nettyServerConfig.getServerChannelMaxIdleTimeSeconds()),
connectionManageHandler,
serverHandler
);
}
});
#监听端口,启动服务
ChannelFuture sync = this.serverBootstrap.bind().sync();
编解码处理
NettyEncoder、NettyDecoder和serverHandler的作用
NettyDecoder:当接收到数据包时,需要处理粘包拆包。TCP为了提高网络通信效率,会缓冲要发送的数据,达到帧frame大小时才发送。而消息message的大小是不固定的,所以NettyDecoder的作用就是分割出message的字节数组byte[],并对byte[]解码,创建RemotingCommand对象。
NettyEncoder:跟NettyDecoder相反,需要将RemotingCommand对象编码成byte[]数组
NettyServerHandler:将RemotingCommand对象根据code派发给对应的线程池处理
RemotingCommand协议
RocketMQ不管是请求还是响应都会封装成RemotingCommand对象
消息体 B1 = L - 4 - H1
{
"code": 103,//业务类型
"extFields": {
"brokerId": "0",//broker节点Id
"bodyCrc32": "321011453",//body校验码
"clusterName": "DefaultCluster",//集群名称
"brokerAddr": "10.101.200.197:10911",//broker地址
"haServerAddr": "10.101.200.197:10912",
"compressed": "false",//body是否压缩
"brokerName": "N80368446"//broker名称
},
"flag": 0,//1、请求还是响应;2、消息发送是否是sendOneWay方式
"language": "JAVA",
"opaque": 11,//请求id
"serializeTypeCurrentRPC": "JSON",//header序列化类型
"version": 395,//当前RocketMQ版本
"body": []//body
}
RocketMQ的线程池
线程池名称 | 默认线程数 | 作用 |
---|---|---|
eventLoopGroupBoss | 1 | 负责监听 TCP网络连接请求,建立好连接,创建SocketChannel,并注册到selector上 |
eventLoopGroupSelector | 3 | selector线程池,监听网络读写事件,转发网络数据到work线程池 |
defaultEventExecutorGroup | 8 | work线程池,处理SSL验证、编解码、空闲检查、网络连接管理 |
*remotingExecutor | 8 | 业务processor处理线程池。namesrv中使用,broker注册、获取topic route信息等 |
*sendMessageExecutor | 4 | 业务processor处理线程池。broker中使用,处理producer发送消息业务 |
可见RocketMQ中线程池各司其职,相互隔离,互不影响。其中带*号的业务线程池会根据具体场景配置不同的线程池参数。
网络通信的三种方式
NettyRemotingClient和NettyRemotingServer都支持下面的三种通信方式
oneWay单向通信
不等待服务端响应,只要发送成功就完事儿了
RemotingCommand request = new RemotingCommand();
...
channel.writeAndFlush(request);
async异步通信
发送异步消息代码
DefaultMQProducer producer = new DefaultMQProducer("please_rename_unique_group_name");
Message msg = new Message("TopicTest", "TagA", "Hello RocketMQ".getBytes("UTF-8"));
producer.send(msg, new SendCallback() {
public void onSuccess(SendResult sendResult) {
//InvokeCallback的operationComplete方法拿到结果触发
}
public void onException(Throwable e) {
//InvokeCallback的operationComplete方法执行异常时触发
}
});
发送异步消息主要逻辑图
sync同步通信
发送同步消息代码
DefaultMQProducer producer = new DefaultMQProducer("please_rename_unique_group_name");
Message msg = new Message("TopicTest", "TagA", "Hello RocketMQ".getBytes("UTF-8"));
#可以直接返回响应
SendResult sendResult = producer.send(msg);
发送同步消息主要逻辑图