RocketMq之Netty通讯源码解析

时光怂恿深爱的人放手 提交于 2019-12-02 03:33:30

1,RocketMq四大核心组件:

  RocketMQ里,有以下几个核心的模块:ProducerConsumerBrokerNameSrv。他们之间的关系如下

 

 

 

先简单了解一下各个模块的功能,下面会有章节详细介绍各个模块的功能。

 

Producer和Consumer很好理解,顾名思义就是生产者和消费者,生产者负责生产消息,消费者负责消费消息,这2块的逻辑都是由业务使用者定义的。

 

 

 

Broker是RocketMQ的核心,Broker实现了消息的存储、拉取等功能。Broker通常以集群方式启动,并可配置主从,每个Broker上提供对指定topic的服务。理解了Broker的原理,以及和其他服务交互的方式就基本弄懂了整个消息中间件的原理。

 

 

 

NameSrv是一个无状态的名称服务,可以集群部署。所有Broker启动的时候会向NameSrv注册自己的信息。Producer会根据目标topic从NameSrv获取到达指定Broker的路由信息,Consumer同理。

 

对于Producer端RocketMQ采用了轮询的方式保证了负载均衡,Consumer端通常采用cluster集群方式消费消息,我们可以自己定义消息在消息端的分配方式。另外,MQ还提供了顺序消息的特性,简单了解一下MQ提供的特性即可,具体实现后面章节会进行阐述。

 

2,RocketMQ包结构详析:

 

源码目录结构介绍 RocketMQ源码分为以下几个package:

 

rocketmq-broker:整个mq的核心,他能够接受producer和consumer的请求,并调用store层服务对消息进行处理。HA服务的基本单元,支持同步双写异步双写等模式。

 

rocketmq-clien::mq客户端实现,目前官方仅仅开源了java版本的mq客户端c++,go客户端有社区开源贡献。

 

rocketmq-common:一些模块间通用的功能类,比如一些配置文件、常量。

 

rocketmq-example:官方提供的例子,对典型的功能比如order message,push consumer,pull consumer的用法进行了示范。

 

rocketmq-filtersrv:消息过滤服务,相当于在broker和consumer中间加入了一个filter代理。

 

rocketmq-remoting:基于netty的底层通信实现,所有服务间的交互都基于此模块。

 

rocketmq-srvut:解析命令行的工具类。

 

rocketmq-store:存储层实现,同时包括了索引服务,高可用HA服务实现。

 

rocketmq-toolsmq集群管理工具,提供了消息查询等功能。

 

3, Rocketmq-remoting远程通讯解析:

 

rocketmq-remoting通信层介绍 remoting模块是mq的基础通信模块,理解通信层的原理对理解模块间的交互很有帮助。

 

对于一个实际的请求,mq是如何进行编解码以及分发请求的呢?比较重要的两个类包括NettyRemotingClient和NettyRemotingServer,这里以NettyRemotingServer为例子先看它的启动

 

 

可以看到ch.pipeline().addLast就是往管道里添加数据的处理逻辑,首先需要知道对于每一个事件处理器handler,他可以处理的事件包括了以下几种(覆盖父类方法即可实现),只要满足条件数据会经过每一个handler对应的事件处理方法:

channelActivechannelInactive连接建立连接关闭的时候会被回调。

channelRead:当channel有数据可读时会回调到这个函数。mq正是从这个函数将请求分发到后端线程进行处理的。

exceptionCaught:发生异常时回调。

userEventTriggered:当上面的事件都不满足自己的需求时,用户可以在这里面自定义的事件处理方法。

mq的**pipeline管道**定义如下handler的含义:_ - `NettyEncoder`、`NettyDecoder`:mq对应的编码器和解码器的逻辑,他们分别覆盖了父类的**encode**和**decode**方法。 - `IdleStateHandler`:Netty自带的心跳管理器 - `NettyConnetManageHandle`:连接管理器,他负责捕获新连接、连接断开、异常等事件,然后统一调度到NettyEventExecuter处理器处理。 - `NettyServerHandler`:当一个消息经过前面的解码等步骤后,然后调度到channelRead0方法,然后根据消息类型进行分发 
继续跟踪`NettyServerHandler`代码:

![delegate](delegate_message.png) 

_接下去的代码的处理逻辑分为处理消息请求和消息响应。
_ **(a)处理消息请求processRequestCommand**
首先看`NettyRemotingAbstract`类中的一个成员: ``` HashMap> processorTable ``` 可以看到注释,键表示了`request code`,mq中可以为不同类型的请求码指定不同的处理器`Processor`处理,但是要注意消息实际的处理并不是在当前线程,而是被封装成task放到`Processor`对应的线程池处理:

final RequestTask requestTask = new RequestTask(run, ctx.channel(), cmd);
pair.getObject2().submit(requestTask);

RocketMQ中能看到很多地方都是这样的处理,这样的设计能够最大程度的保证异步,保证每个线程都专注处理自己负责的东西。 以下是Processor的实现:

 

 

最后,processRequestCommand这个函数的整体处理逻辑如下所示:

 

 

另外,要注意一下,第二步构建task的时候,运用了模板设计模式,在任务的执行前后加入了一个hook:我们可以利用这个hook进行一些额外的操作,比如消息的加密解密。

rpcHook.doBeforeRequest(RemotingHelper.parseChannelRemoteAddr(ctx.channel()), cmd);Processor.processRequest()rpcHook.doAfterResponse(RemotingHelper.parseChannelRemoteAddr(ctx.channel()), cmd, response);

**(b)处理消息响应processResponseCommand**
实际上,这部分的处理并不是太难,首先理解下面这个结构:

protected final ConcurrentHashMap<Integer /* opaque */, ResponseFuture> responseTable

 

opaque表示请求发起连接方在同个连接上不同的请求标识代码,每次发送一个消息的时候,他可以选择同步阻塞的方式和异步非阻塞的方式,不管是哪种方式,他都会保存操作码到ResponseFuture的映射。 重点讲解一下ResponseFuture这个类,这个类中比较重要的成员包括一个回调函数invokeCallback,以及一个信号量semaphore

  • 对于同步消息,这二个参数通常是个null。

对于异步消息invokeCallback的作用就是在收到消息响应的时候能够根据responseTable找到操作码对应的回调函数;semaphore的主要作用是用作流控,当多个线程同时往一个连接写数据时可以通过信号量控制permit同时写许可的数量。
简单来说,总体流程如下:

 

 

  • 当然,流程图未列举的操作还包括释放信号量资源,以及清空responseTable表相关键值对信息等操作。

`NettyRemotingClient`的处理实际上与·NettyRemotingServer·的处理基本一致,唯一不同的是Netty pipeline中**连接管理**相关的handler额外还处理了`connect事件`,该事件在客户端主动连接对端成功后回调。

https://mp.weixin.qq.com/s/vkvYJnKfQyuUeD_BDQy_1g

获取更多学习资料,可以加群:473984645或扫描下方二维码

 

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!