﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>BlogJava-往事如风-随笔分类-ActiveMQ</title><link>http://www.blogjava.net/zhuanggl/category/45587.html</link><description>记录工作中的点点滴滴
留住那些淡淡的回忆</description><language>zh-cn</language><lastBuildDate>Wed, 14 Jul 2010 18:20:51 GMT</lastBuildDate><pubDate>Wed, 14 Jul 2010 18:20:51 GMT</pubDate><ttl>60</ttl><item><title>ActiveMQ的插件开发介绍</title><link>http://www.blogjava.net/zhuanggl/archive/2010/07/14/326119.html</link><dc:creator>井底青蛙,常望天空</dc:creator><author>井底青蛙,常望天空</author><pubDate>Wed, 14 Jul 2010 11:18:00 GMT</pubDate><guid>http://www.blogjava.net/zhuanggl/archive/2010/07/14/326119.html</guid><wfw:comment>http://www.blogjava.net/zhuanggl/comments/326119.html</wfw:comment><comments>http://www.blogjava.net/zhuanggl/archive/2010/07/14/326119.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/zhuanggl/comments/commentRss/326119.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/zhuanggl/services/trackbacks/326119.html</trackback:ping><description><![CDATA[<p>ActiveMQ是一个流行的开源MQ，我们也大规模应用在网站的方方面面，每天处理上亿消息，取得了较好效果。ActiveMQ有一个很好很强大的插件体系，提供了很强的扩展能力，ActiveMQ本身就是使用这一套插件体系实现了很多扩展功能，包括他的权限管理，日志管理，事务等模块都是作为一个插件集成的，我们自己也在消息路由、补偿式事务方面使用了它的插件功能，确实非常方便。</p>
<p>在ActiveMQ中，Broker代表一个运行的MQ节点，ActiveMQ的插件实际上是基于Broker的一个Filter链，整个设计类似于servlet的Filter结构，所有的Plugin构成一个链式结构，每个插件实际上都是一个"Interceptor"，类结构图如下：</p>
<p>
<img src="http://pt.alibaba-inc.com/wp/wp-content/uploads/2010/06/Main.jpg" alt="Main.jpg" height="406" width="857" />
</p>
<p>其中Broker接口封装了一个AMQ节点的方方面面的方法，包括连接管理、session管理、消息的发送和接收以及其它的一些功能，BrokerFilter实现这个接口，并提供了链式结构支持，可以拦截所有Broker方法的实现并传递结果给链式结构的下一个，形成了一个完整的"职责链"模式，具体层次关系如下，其中，"System Plugin"是指AMQ内部使用Plugin机制实现的一些系统功能，用户不能定制，"AMQ Plugin"指的是ActiveMQ已经实现好了，可以在配置文件中自由选择的一些插件，例如简单的安全插件，JAAS安全插件和DLQ插件等等，用户插件就是指用户自己实现的amq插件，需要用户把相关jar包放入到amq的启动classpath中，并在配置文件中进行配置才能正确加载的插件。</p>
<p>
<img src="http://pt.alibaba-inc.com/wp/wp-content/uploads/2010/06/xn-1-jf0b415f.jpg" alt="图片1.jpg" height="951" width="868" />
</p>
<p>在上面这个层次结构中，最下面的RegionBroker是核心组件，在其之上的都是Broker的插件，继承之于BrokerFilter，和Broker保持接口兼容但是扩展Broker的功能。</p>
<p>下面举一个简单的例子，具体说明一下AMQ的插件是如何工作的。</p>
<p>我们在使用AMQ的过程中发现，在测试环境维护方面有很大的麻烦，具体表现在很多同学在测试项目的时候往往只关注自己项目牵涉的队列，不会去消费其他"不相关"的队列，这样导致的一个问题就是ActiveMQ经常发生大量数据阻塞，导致测试环境不可用，影响相关项目的测试工作。为了避免这个问题，我们假定在测试环境可以定义以下一些限制条件：</p>
<p>1、 所有队列堆积消息不超过1000条，超过之后立即清除。</p>
<p>2、 消息超过1个小时没有消费，就直接过期。</p>
<p>我们可以编写一个简单的amq插件来完成这两个限制条件：</p>
<p>首先，编写一个插件安装类：</p>
<p>package com.alibaba.napoli.plugins;</p>
<p>import org.apache.activemq.broker.Broker;
<br />
import org.apache.activemq.broker.BrokerPlugin;
<br />
import org.apache.commons.logging.Log;
<br />
import org.apache.commons.logging.LogFactory;</p>
<p>public class MessageControlBrokerPlugin implements BrokerPlugin {
<br />
private static Log log = LogFactory.getLog(StatisticsBrokerPlugin.class);</p>
<p>public Broker installPlugin(Broker broker) throws Exception {
<br />
log.info("install MessageControlBrokerPlugin");
<br />
return new MessageControlBroker(broker);
<br />
}
<br />
}</p>
<p>其次，编写真正的插件实现：</p>
<p>package com.alibaba.napoli.plugins;</p>
<p>import java.io.IOException;</p>
<p>import org.apache.activemq.broker.Broker;
<br />
import org.apache.activemq.broker.BrokerFilter;
<br />
import org.apache.activemq.broker.ConnectionContext;
<br />
import org.apache.activemq.broker.ProducerBrokerExchange;
<br />
import org.apache.activemq.broker.region.Destination;
<br />
import org.apache.activemq.broker.region.MessageReference;
<br />
import org.apache.activemq.broker.region.Queue;
<br />
import org.apache.activemq.command.Message;
<br />
import org.apache.commons.logging.Log;
<br />
import org.apache.commons.logging.LogFactory;</p>
<p>/**
<br />
* 开发环境管理插件，符合两个条件进行消息清理：&lt;br&gt;
<br />
* 1 消息累积超过1000条
<br />
* 2 消息超过1个小时无人消费
<br />
* @author guolin.zhuanggl
<br />
*
<br />
*/
<br />
public class MessageControlBroker extends BrokerFilter {
<br />
public static Log log = LogFactory.getLog(DiscardingDLQBroker.class);
<br />
private static final long DEFAULT_EXPIRATION = 3600*1000;
<br />
private static final long DEFAULT_PURGE_COUNT = 1000;</p>
<p>public MessageControlBroker(Broker next) {
<br />
super(next);
<br />
}</p>
<p>@Override
<br />
public void messageExpired(ConnectionContext context,
<br />
MessageReference message) {</p>
<p>Message msg = null;
<br />
try {
<br />
msg = message.getMessage();
<br />
} catch (IOException e) {
<br />
log.error("failed to fetch content: ",e);
<br />
}
<br />
purgeMessage(msg);
<br />
// TODO Auto-generated method stub
<br />
super.messageExpired(context, message);
<br />
}</p>
<p>/**
<br />
* 清除队列中的所有消息
<br />
*/
<br />
private void purgeMessage(Message message){
<br />
Destination r = message.getRegionDestination();
<br />
if(r instanceof Queue){
<br />
try {
<br />
//如果累积消息超过1000个，清除队列消息
<br />
if(((Queue) r).getMessages().size() &gt; DEFAULT_PURGE_COUNT){
<br />
((Queue) r).purge();
<br />
}
<br />
} catch (Exception e) {
<br />
// TODO Auto-generated catch block
<br />
log.error("failed to purge queue "+r.getName(),e);
<br />
}
<br />
}</p>
<p>}
<br />
/**
<br />
* 当消息发送时，全部设置过期时间1个小时，测试环境专用！！！
<br />
*/
<br />
@Override
<br />
public void send(ProducerBrokerExchange producerExchange,Message messageSend) throws Exception {
<br />
long oldExp = messageSend.getExpiration();
<br />
messageSend.setExpiration(oldExp &lt; DEFAULT_EXPIRATION &amp;&amp; oldExp &gt; 0 ? oldExp : DEFAULT_EXPIRATION );
<br />
purgeMessage(messageSend);
<br />
super.send(producerExchange, messageSend);
<br />
}</p>
<p>}</p>
<p>然后，将这两个类打包为myplugin.jar，并放在activemq启动目录下的lib目录下</p>
<p>最后，在activemq.xml文件中增加一个简单的spring配置项：</p>
<p>&lt;bean xmlns="<a href="http://www.springframework.org/schema/beans">http://www.springframework.org/schema/beans</a>"
<br />
id="purgePlugin"
<br />
class="com.alibaba.napoli.plugins.MessageControlBrokerPlugin"&gt;
<br />
&lt;/bean&gt;</p>
<p>然后，重启activemq，就会发现这个插件已经被加载。</p>
<img src ="http://www.blogjava.net/zhuanggl/aggbug/326119.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/zhuanggl/" target="_blank">井底青蛙,常望天空</a> 2010-07-14 19:18 <a href="http://www.blogjava.net/zhuanggl/archive/2010/07/14/326119.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>误解API，后果很严重</title><link>http://www.blogjava.net/zhuanggl/archive/2010/07/14/326116.html</link><dc:creator>井底青蛙,常望天空</dc:creator><author>井底青蛙,常望天空</author><pubDate>Wed, 14 Jul 2010 11:17:00 GMT</pubDate><guid>http://www.blogjava.net/zhuanggl/archive/2010/07/14/326116.html</guid><wfw:comment>http://www.blogjava.net/zhuanggl/comments/326116.html</wfw:comment><comments>http://www.blogjava.net/zhuanggl/archive/2010/07/14/326116.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/zhuanggl/comments/commentRss/326116.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/zhuanggl/services/trackbacks/326116.html</trackback:ping><description><![CDATA[<p>最近在线上部署的ActiveMQ发生一次故障，因为一台ActiveMQ故障将前台的关键应用全部连接挂住，根本原因有两条：session的timeout设置不合理以及session池没有限制大小。这里说的不是这个问题，而是在后续设置client的timeout过程中,有同学发现AMQ有一个严重的bug，timeout根本不起作用!!!</p>
<p>调试代码发现：</p>
<p>
<img src="http://pt.alibaba-inc.com/wp/wp-content/uploads/2010/07/fr.png" alt="fr.png" height="502" width="970" />
</p>
<p>在Activemq的send response处理中，使用了一个BlockingQueue，在有timeout的方法里，使用了poll方法，这个方法的api说明中指出，当timeout发生时，这个方法返回null！！！</p>
<p>我们在看AMQ经过层层调用后，在ActiveMQConnection方法中如何处理这个返回值：</p>
<p>
<img src="http://pt.alibaba-inc.com/wp/wp-content/uploads/2010/07/amc.png" alt="amc.png" height="320" width="808" />
</p>
<p>对返回值为空的情况没有做任何处理，即使消息发送超时，amq也认为这个消息发送成功！估计这哥们理解poll在timeout的时候会抛出异常吧。</p>
<p>解决办法很简单，在response为空的时候，抛出JMSException，告知发生Timeout错误。</p>
<img src ="http://www.blogjava.net/zhuanggl/aggbug/326116.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/zhuanggl/" target="_blank">井底青蛙,常望天空</a> 2010-07-14 19:17 <a href="http://www.blogjava.net/zhuanggl/archive/2010/07/14/326116.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>