RocketMQ高性能之网络通信模块

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);
}

#设置编解码处理器NettyEncoderNettyDecoder,业务转发处理器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对象
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的线程池

线程池名称默认线程数作用
eventLoopGroupBoss1负责监听 TCP网络连接请求,建立好连接,创建SocketChannel,并注册到selector上
eventLoopGroupSelector3selector线程池,监听网络读写事件,转发网络数据到work线程池
defaultEventExecutorGroup8work线程池,处理SSL验证、编解码、空闲检查、网络连接管理
*remotingExecutor8业务processor处理线程池。namesrv中使用,broker注册、获取topic route信息等
*sendMessageExecutor4业务processor处理线程池。broker中使用,处理producer发送消息业务

可见RocketMQ中线程池各司其职,相互隔离,互不影响。其中带*号的业务线程池会根据具体场景配置不同的线程池参数。

网络通信的三种方式

NettyRemotingClient和NettyRemotingServer都支持下面的三种通信方式
RocketMQ通信方式

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);

发送同步消息主要逻辑图
同步消息实现

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值