一、序言
有时候我们追求最快的方式发送消息,我们就采用的异步方式,并且不持久化。但是这样带来的问题有这样几个:
1.如果消费者的消费能力低于生产者,那么消息就会积压在broker, 从而导致broker 可能挂掉。
2.我们知道存放内存的模式,只要出现宕机或者其他问题,容易丢消息,因此得看情况而定
对于问题1,activemq 采用了限流 内存溢出提醒的方式进行处理,下面是一些实例过程。
官方介绍可以参考:http://activemq.apache.org/producer-flow-control.html
二、操作过程:
1.我用的activemq.5.11 版本,下载下来打开conf/activemq.xml默认配置改为异步persistent=false
<broker xmlns="http://activemq.apache.org/schema/core" persistent="false" brokerName="localhost" dataDirectory="${activemq.data}">
同时开启限流模式,未了测试方便,我们限流5M
<policyEntry queue=">" producerFlowControl="true" memoryLimit="5mb" /> # 经过测试 producerFlowControl 是默认开启的
这里有原文信息:
Note that, since the introduction of the new file cursor in ActiveMQ 5.x, non-persisted messages are shunted into the temporary file store to reduce the amount of memory used for non-persistent messaging. As a result, you may find that a queue's memoryLimit is never reached, as the cursor doesn't use very much memory. 英文不好,大概意思是: activemq 5.0 以后引入了new file cursor ,非持久化消息会分流到temporary file 存储,用来减少实际内存使用。结果你会发现queue 的内存限制,无法超标,cursor 不会消耗很多内存。 这里有个疑问,反正temporary file 我是没看到- -,不知道为什么 注意:这里是每个queue 限制5M,不是总的
2.测试方式:
打开borker,然后发送10W消息到queue,你可以看到到了一定条数,producer 就不动了,这时候你再打开customer ,才会正常消费。
3.当内存达到limit 限制的时候,我们可能需要做其他处理,方便我们得知,比如:让生产者异常
<systemUsage> <systemUsage sendFailIfNoSpace="true"> <memoryUsage> <memoryUsage limit="20 mb"/> </memoryUsage> </systemUsage> </systemUsage>
然后生产者,或者说客户端就可以捕获:javax.jms.ResourceAllocationException
当然这个会直接抛出异常,后面了个属性,表示5秒后才抛出这个异常。
<systemUsage sendFailIfNoSpaceAfterTimeout="5000">
我们捕获异常之后就可以持续发送消息之后,就可以做其他处理了,broker 也不会爆掉了。
3.Disabling Flow Control 不控制流量的做法
其实这是为了使用整个服务器的配置资源,也就是说上面是单个控制,这里是整体控制
# 默认的系统配置,分别代表
#memoryUsage 内存
#storeUsage 持久化
#tempUsage 临时数据
#当整体数据 操作下面的时候,才爆异常,就不用单个控制了
# 比如用异步 非持久化 测试,Memory percent used 到100 就不动了
# 这部分的使用可以参考:
# http://akuntamukkala.blogspot.com/2014/01/understanding-memory-usage-in-activemq.html
<systemUsage>
<systemUsage>
<memoryUsage>
<memoryUsage limit="64 mb" />
</memoryUsage>
<storeUsage>
<storeUsage limit="100 gb" />
</storeUsage>
<tempUsage>
<tempUsage limit="10 gb" />
</tempUsage>
</systemUsage>
</systemUsage>
三、基础伪代码
@Autowired private Destination destination; @Autowired JmsTemplate jmsTemplate; @Test public void Sender(){ for(int i=0;i<50000;i++){ sendMsg(i); } } // 消息发送 private void sendMsg(int i){ try { jmsTemplate.convertAndSend(destination,""+i); }catch (Exception e){ sendMsg(i); } }
<bean id = "jmsTemplate" class = "org.springframework.jms.core.JmsTemplate"> <!-- 链接工长 --> <property name="connectionFactory" ref="cachingConnectionFactory"/> <!-- 1:非持久化,2:持久化 --> <property name="deliveryMode" value="1" /> <!-- 消息转换器 --> <property name="messageConverter" ref="msgConvert"/> </bean> <!-- 队列的目的地描述 --> <bean id="queueDestination" class="org.apache.activemq.command.ActiveMQQueue"> <!-- 通过 构造 设定 队列的名字 --> <constructor-arg index="0" value="orderQueue"/> </bean>
四、测试步骤:
1.开启broker,开启限流5M
2.发送queue 10W消息,如果没配置异常,则会一直阻塞,配置了则会抛异常
3.阻塞之后开启消费者,消息能正常收到。
小结:
1.这里将官网的东西拿来做了个简单的说明和解释,如果需要这里的详细实习,得去看ActiveMQMessageProducer 类
2.我这里用的spring +mq 的形式,如果用原生的东西,有朋友没测试成功,测试的时候尽量剔除其他东西,比如事务之类的。。
3.有朋友问自己发送了1W消息,为什么控制台只有几千条,因为你发送1W消息其实达到limit 限制之后,后面的消息没有进入quque,因此你在控制台看到的消息是没有那么多的,假设是8K条,而且你的1W消息 只是根据发送点打印的,没进入queue.那么那些消息去哪儿了呢?源码还没具体分析,但是肯定是在内存中,没进入队列。
这种场景我们很好思考:我限制房间只有100个人,但是来的人很快,一次来10-20个,那么满了的时候就会剩下一些在门口进不去,而客户端觉得我已经发送了120个人过去了。。。,实际我房间只有100个,实际多的20个还在客户端的发送队列里面,如果这时候客户端挂了,那么消费者只能收到100个~。~
4.这种场景是允许的,如果保证消息不丢失,那么就要持久化的,一般高效的东西都会容忍这类事件的。
5.有错误的请指点,不胜感谢~。~