本文主要提到以下两类错误及解决方法:消息发送超时Systembusy,Brokerbusy1,消息发送错误消息发送超时,通常客户端的日志如下如下:client通常首先怀疑的对象是RocketMQ服务端,是否Broker性能出现波动,无法承受当前流量。那么我们如何检查RocketMQ当前是否存在性能瓶颈呢?首先我们执行如下命令查看RocketMQ消息写入耗时分布:cd/${USER.HOME}/logs/rocketmqlogs/grep-n'PAGECACHERT'store.log|more输出结果如下:RocketMQ会在分钟打印前一分钟内发送消息的耗时分布。从这里可以看出RocketMQ消息写入是否存在详细的性能瓶颈。区间如下:[<=0ms]小于0ms,即细微级[0~10ms]小于10ms的数[10~50ms]大于10ms且小于50ms的数。其他间隔表明它们中的大部分将在微妙的层次上完成。根据笔者的经验,如果100~200ms及以上的间隔超过20次,说明Broker确实存在一定的瓶颈。如果只有几个,说明这是内存或者PageCache的抖动,问题不大。RocketMQbroker中还有一种快速失败机制,即当Broker收到客户端的请求时,会先将消息放入队列中,然后依次执行。如果一条消息在队列中等待超过200ms,它会启动快速失败并返回给客户端[TIMEOUT_CLEAN_QUEUE]brokerbusy错误。另外,如果此时Producer客户端恰好有垃圾回收,也可能会导致消息发送超时的问题。消息发送超时应该怎么处理?一般来说,我们可以减少消息发送的超时时间,增加重试次数,增加快速失败的最大等待时间。具体是通过maxWaitTimeMillsInQueue来配置的。一般来说,超时时间调整为500ms-1000ms。需要说明的是,超时时间在不同版本之间有不同的含义。4.3.0以下版本(4.3.0除外),超时时间是指单次传输的时间;而在4.3.0及以上版本中,中超时是指所有重试的总超时时间。二、Systembusy和Brokerbusy在使用RocketMQ时,如果RocketMQ集群达到1W/tps的压力负载水平,Systembusy和Brokerbusy将是大家经常遇到的问题。例如下图所示的异常栈。查看RocketMQ的Systembusy和Brokerbusy相关的error关键字,一共有5个如下:[REJECTREQUEST]systembusytoomanyrequestsandsystemthreadpoolbusy[PC_SYNCHRONIZED]brokerbusy[PCBUSY_CLEAN_QUEUE]brokerbusy[TIMEOUT_CLEAN_QUEUE]brokerbusy的原因归纳为以下三种:PageCache压力大。下面三种错误属于这种情况:[REJECTREQUEST]systembusy[PC_SYNCHRONIZED]brokerbusy[PCBUSY_CLEAN_QUEUE]brokerbusy判断PageCache是??否繁忙的依据是写入消息时,向内存添加消息时加锁的时间,默认判断标准是加锁时间超过1s,则认为PageCache压力过大,将相关错误日志抛给客户端。发送线程池积压的拒绝策略用于处理RocketMQ中的消息发送。它是一个只有一个线程的线程池,内部维护一个有界队列,默认长度为1W。如果当前队列中积压超过1w,则执行线程池的拒绝策略,从而抛出[请求过多,系统线程池忙]错误。Broker端快速失败Broker端默认启用快速失败机制,即Broker端PageCache一直不忙(锁定超过1s),但消息发送中有请求等待排队200ms,RocketMQ不会继续排队,直接返回Systembusy给客户端。但是由于RocketMQ客户端不会重试错误,所以需要额外处理才能解决此类问题。PageCache忙的解决方法一旦消息服务器上有大量的PageCache忙(向内存中添加数据并锁定超过1s),这是一个严重的问题,需要人为干预才能解决。问题的解决方法如下。transientStorePoolEnable启用transientStorePoolEnable机制(相关说明:RocketMQ源码-MappedFile介绍),即在Broker配置文件中添加如下配置:transientStorePoolEnable=truetransientStorePoolEnable的原理如下图所示:引入背后的关键transientStorePoolEnable可以减轻PageCache的压力如下:先写消息因为在这块内存中启用了内存锁定机制,消息的写入接近于直接操作内存,性能可以得到保证。消息进入堆外内存后,后台会启动一个线程,将一批消息提交给PageCache,即写入消息时对PageCache的写操作由单次写入变为批量写入,从而减轻了PageCache的压力。transientStorePoolEnable的引入会增加数据丢失的可能性。如果BrokerJVM进程异常退出,提交给PageCache的消息不会丢失,但是存在于堆外内存(DirectByteBuffer)但还没有提交给PageCache的消息会丢失。但是一般情况下,RocketMQ进程是不太可能退出的。另外,如果启用了transientStorePoolEnable,消息发送方需要有重推机制(补偿思路)。Expansion如果启用transientStorePoolEnable后PageCache级别仍然很忙,则需要对集群进行扩容,或者拆分集群中的Topics,即将部分Topics迁移到其他集群,以减轻集群的负载。
