rocketmq-remoting

503 阅读3分钟

RocketMQ的通信相关代码都在remoting模块中。先看主要类结构:

这里有个大致印象,只要知道有个netty实现的server和client就可以了。

我们直接看单元测试类RemotingServerTest。在测试其他方法之前,主要就是创建了一个NettyRemotingServer和NettyRemotingClient来进行通信。

实际测试时,也只是很简单的构建一个request,调用client的invokeSync得到一个response。

一、NettyRemotingClient

RemotingClient client = new NettyRemotingClient(nettyClientConfig);

先看在new了一个client时做了什么操作,在此之前我们先看下NettyRemotingClient中的关键属性有哪些?

new构造器里:设置nettyClientConfig、publicExecutor、eventLoopGroupWorker。eventLoopGroupWorker是最重要的,属于netty框架中的client的worker线程组,专门用来处理IO读写连接的,为什么只有1个线程,很简单,因为对于client而言,并不会像服务器那样同时会有上万个连接,client只有一个连接,所以1个线程足够。

再看client的start方法:创建defaultEventExecutorGroup,这是有了读写事件后处理报文的业务逻辑的线程组,使用netty框架那套构建引导器bootstrap,也即是client的启动器。对于每条连接的pipeline里加了编码器(继承MessageToByteEncoder,主要是把RemotingCommand对象编码为ByteBuffer,写出时最后一道门槛)、解码器(继承LengthFieldBasedFrameDecoder,根据协议,读取ByteBuffer,解码成RemotingCommand对象,读入时第一道门槛)、空闲连接的handler、NettyConnectManageHandler(不知道干嘛的,暂时没有用到它)以及最重要的NettyClientHandler。

NettyClientHandler是对client连接的最后一道读入处理的handler,就是处理读入报文的,报文字节流被前面的解码器处理后,到这里就是一个RemotingCommand对象了。client处理的肯定都是从服务器响应的RESPONSE_COMMAND类型的RemotingCommand对象。一直说NettyClientHandler是处理业务逻辑的,想想这里的业务逻辑应该是什么?结合NettyRemotingClient的invokeSync方法来看下。

invokeSync是一个同步方法,顾名思义,远程连接到服务器后,需要同步等到服务器的结果,再返回出来。

说明下createChannel方法:缓存channelTables是CurrentHashMap,key是服务器addr,value是建立的连(也就是channel)。第一次是用bootstrap去connect服务器得到的future,当连接建立成功时,会回调future,若ok,则返回channel。

这里同步方法等到结果,是如何等待responseFuture中的结果被填充的呢?responseFuture有个countdownlatch来控制的。再结合上面说的最重要的处理业务逻辑的NettyClientHandler来看。handler读到服务器的响应,找到缓存中对应的responseFuture,把响应(也就是RemotingCommand对象)塞到future,同时countdownlatch-1,这就通知到前面的同步方法invokeSyncImpl获得响应结果了。

二、NettyRemotingServer

服务端,我们先想想它会做什么事情?答案就是接受客户端的请求,根据请求的code,使用对应的processor和线程池来处理,同时也要把request中的唯一id也就是opaque,塞到response中,这样client才知道应答是对应之前哪个请求。

server的start方法就是构造一个serverBootstrap(netty中的服务端启动器),向每条建立连接channel的pipeline中注册一系列的handler。这里最重要的是serverHandler。最后serverBootstrap的bind端口启动服务器。

再看看serverHandler对收到的请求,也就是RemotingCommand对象(由前面的解码器传过来的)做了什么?1、获取之前注册好的processor和线程池。2、使用processor(定义处理逻辑)和线程池(提供处理线程)处理请求对象RemotingCommand。3、callback把请求唯一标识写入response,并把response从网卡写出。

三、结束语

我们从头在看看测试类的例子和对应的结果

server对请求类型为0注册了一个processor,主要就是设置remark=Hi+remoteAddress,在把request当做response返回出来,所以我们收到的应答,应该是这样的。我们看下debug结果,确实是这样子。