单向发送模式:
该方法首先将请求标记为单向发送,然后基于Semaphore信号量尝试获取单向发送的资源,通过信号量控制单向消息并发发送的消息数,从而保护系统内存占用。客户端单向发送的Semaphore信号量默认为65535,即单向消息最大并发为65535,可通过配置"com.rocketmq.remoting.clientOnewaySemaphoreValue"系统变量更改。
获取到了信号量资源之后。构建SemaphoreReleaseOnlyOnce对象,保证信号量本次只被释放一次,防止并发操作引起线程安全问题,然后就通过channel发送请求即可。
在其监听器ChannelFutureListener中,会释放信号量,如果发送失败了,仅仅是打印一行warn日志,然后就不管了。如果没有获取到信号量资源,那么直接抛出异常即可,并且不再发送。
只管发送不管结果,不会进行任何重试,这就是单向发送消息的真正含义。
同步发送模式:
首先创建一个ResponseFuture,然后将本次请求id和respone存入responseTable缓存。
随后执行调用,并添加一个ChannelFutureListener,消息发送完毕会进行回调。然后responseFuture通过waitResponse方法阻塞当前线程,直到得到响应结果或者到达超时时间。
当ChannelFutureListener回调的时候会判断如果消息发送成功,那么设置发送成功并返回,否则设置发送失败标志和失败原因,并且设置响应结果为null,唤醒阻塞的responseFuture。
responseFuture被唤醒后会进行一系列判断。如果响应结果为null,那么会根据不同情况抛出不同的异常,如果响应结果不为null,那么返回响应结果。
最后在finaly块中从responseTable中移除响应结果缓存。
异步发送模式:
invokeAsyncImpl方法发起异步调用。该方法和单向发送的方法一样,都会基于Semaphore信号量尝试获取异步发送的资源,通过信号量控制异步消息并发发送的消息数,从而保护系统内存占用。客户端单向发送的Semaphore信号量默认为65535,即异步消息最大并发为65535,可通过配置"com.rocketmq.remoting.clientAsyncSemaphoreValue"系统变量更改。
创建一个ResponseFuture,设置超时时间、回调函数。然后将本次请求id和respone存入responseTable缓存。
随后执行调用,并添加一个ChannelFutureListener,消息发送完毕会进行回调。当ChannelFutureListener回调的时候会判断如果消息发送成功,那么设置发送成功并返回,否则如果发送失败了,则移除缓存、设置false、并且执行InvokeCallback#operationComplete回调。
如果发送成功了,那么InvokeCallback#operationComplete回调会执行吗,当让会了,当请求正常处理完毕的时候,在processResponseCommand方法中会将执行InvokeCallback#operationComplete回调。
onExceptionImpl异常处理
重试之前,首先会判断本次重试的次数是否大于重试总次数,参数为retryTimesWhenSendFailed,默认2次。如果超过了最大重试次数,那么便不会重试
NettyClientHandler处理服务端消息(主要用来回调各种发送成功、失败后的逻辑):
生产者发送-》broker响应=》生产者NettyClientHandler处理服务端消息
processResponseCommand处理响应
客户端发送消息之后服务端的响应会被processResponseCommand方法处理。消息发送请求的响应处理也是该方法完成的。其大概流程为:
先根据请求id找到之前放到responseTable的ResponseFuture,然后从responseTable中移除ResponseFuture缓存。
判断如果存在回调函数,即异步请求,那么调用executeInvokeCallback方法,该方法会执行回调函数的方法。
如果没有回调函数,则调用putResponse方法。该方法将响应数据设置到responseCommand,然后调用countDownLatch.countDown,即倒计数减去1,唤醒等待的线程。
总结
本次我们讲解了重要的发送消息的内部方法MQClientAPIImpl#sendMessage的源码,该方法内部又会根据发送模式执行不同的发送逻辑。单向发送模式调用RemotingClient#invokeOneway方法;异步发送模式调用MQClientAPIImpl#sendMessageAsync方法;同步发送模式调用MQClientAPIImpl#sendMessageSync方法。在异步和同步模式发送方法的调用前还会再检查是否超时,如果超时则不再调用。
同步发送模式内部采用CountDownLatch工具实现线程的阻塞和唤醒,当发送了同步消息之后,当前线程阻塞,当服务端响应返回之后,将会通过CountDownLatch减少倒计数来唤醒阻塞的线程。发送请求和响应怎么对应上的呢?发送请求的时候会生成并带上本次请求的请求Id,客户端返回响应中带有对应的请求的请求Id,这样就能对应上了。
同步发送和异步发送模式都会有消息重试,消息发送过程中如果抛出了RemotingException、MQClientException、以及部分MQBrokerException异常时,那么会进行重试,默认重试2次。如果抛出了InterruptedException,或者因为超时则不再重试。