Spring下ActiveMQ实战
MessageQueue是分布式的系统里经常要用到的组件,一般来说,当需要把消息跨网段、跨集群的分发出去,就可以用这个。一些典型的示例就是:
1、集群A中的消息需要发送给多个机器共享;
2、集群A中消息需要主动推送,但彼此的网络不是互通的(如集群A只有过HA才能被外界访问);
当然上面的几个点,除了用MQ还有其它实现方式,但是MQ无疑是非常适合用来做这些事的。众多MQ中,ActiveMQ是比较有名气也很稳定的,它发送消息的成本非常廉价,支持Queue与Topic两种消息机制。本文主要就是讲如何在Spring环境下配置此MQ:
1、场景假设
现有机器两台Server、Worker需要进行异步通信,另有一台ActiveMQ机器,关于MQ的配置信息存放在Zookeeper中,Zookeeper的节点有:
- /mq/activemq/ip:mq的机器ip
-/mq/activemq/port:这是mq的机器端口
2、Server的Spring XML配置
Server主要的工作就是接受Worker消息,并发送消息给Worker。主要是定义了连接MQ的连接池,接受Worker消息的队列worker,发送消息给Worker的队列server:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:jms="http://www.springframework.org/schema/jms" xmlns:p="http://www.springframework.org/schema/p" xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.1.xsd
http://www.springframework.org/schema/jms http://www.springframework.org/schema/jms/spring-jms-4.1.xsd"> <!-- ActiveMQ连接池 -->
<bean id="conFactory" class="org.apache.activemq.pool.PooledConnectionFactory">
<property name="connectionFactory">
<bean class="org.apache.activemq.ActiveMQConnectionFactory">
<property name="brokerURL">
<bean class="lekko.mq.util.MQPropertiesFactory" factory-method="getUrl" />
</property>
<property name="closeTimeout" value="60000" />
<!-- <property name="userName" value="admin" /> -->
<!-- <property name="password" value="admin" /> -->
<!-- <property name="optimizeAcknowledge" value="true" /> -->
<property name="optimizedAckScheduledAckInterval" value="10000" />
</bean>
</property>
</bean> <!-- Worker任务消息 -->
<bean id="taskWorkerTopic" class="org.apache.activemq.command.ActiveMQTopic">
<constructor-arg value="worker_topic" />
</bean>
<!-- 任务监听容器 -->
<bean id="taskWorkerContainer" class="org.springframework.jms.listener.DefaultMessageListenerContainer">
<property name="connectionFactory" ref="conFactory" />
<property name="destination" ref="taskWorkerTopic" />
<property name="messageListener">
<bean class="lekko.mq.task.TaskWorkerListener" />
</property>
<property name="pubSubDomain" value="true" />
</bean> <!-- Server任务消息 -->
<bean id="taskServerTopic" class="org.apache.activemq.command.ActiveMQTopic">
<constructor-arg value="server_topic" />
</bean>
<!-- 任务消息发送模板 -->
<bean id="taskServerTemplate" class="org.springframework.jms.core.JmsTemplate" p:connectionFactory-ref="conFactory" p:defaultDestination-ref="taskServerTopic" /> </beans>
一段一段地分析,ActiveMQ连接池这里,定义了连接的bean为“conFactory”,其中broberURL属性是通过后台Java代码的静态方法来设置的,方便线上环境通过Java代码动态地切换,稍后会介绍这块代码,你现在需要知道的是,它实际上返回的就是一个字符串,格式像:tcp://xxx.xxx.xxx.xxx:port,如果不要用后台来管理连接信息,直接改成“<property name="brokerURL" value="tcp://xxx.xxx.xxx.xxx:port">”也是OK的。
接下来,便是Worker消息队列的定义,这里定义为“taskWorkerTopic”,类型是org.apache.activemq.command.ActiveMQTopic,(订阅模式)它表示一个消息可以被多个机器收到并处理,其它的还有org.apache.activemq.command.ActiveMQQueue,(点对点模式)表示一个消息只能被一台机器收到,当收到后消息就出队列了,其它机器无法处理。它们都有一个构造参数constructor-arg,指定了消息队列的名称,一个MQ中一个消息队列的名字是唯一的。
Worker的消息队列定义好了之后,就是接受Worker的里消息了,这里定义了“taskWorkerContainer”,其属性分别定义了连接池、目标队列、消息处理器(我们自己的Java类,后面再讲),参数pubSubDomain用于指定是使用订阅模式还是使用点对点模式,如果是ActiveMQTopic则要设置为true,默认是false。
好了,Server现在已经可以通过自己定义的“lekko.mq.task.TaskWorkerListener”类接受并处理taskWorkerTopic的消息了。
如法炮制,定义一个专门用于往Worker里发消息的队列“taskServerTopic”,并定义发送消息的模板“taskServerTemplate”备用。
3、Server端的接收类与发送类
lekko.mq.task.TaskWorkerListener便是一个接收类示例:
package lekko.mq.task; import javax.jms.Message;
import javax.jms.MessageListener; import org.apache.activemq.command.ActiveMQObjectMessage;
import org.apache.log4j.Logger;
import org.springframework.stereotype.Service;
import lekko.mq.model.MessageModel; /**
* Task消息监听类
* @author lekko
*/
@Service
public class TaskWorkerListener implements MessageListener { private Logger _logger = Logger.getLogger(TaskWorkerListener.class); @Override
public void onMessage(Message message) {
if (message instanceof ActiveMQObjectMessage) {
ActiveMQObjectMessage aMsg = (ActiveMQObjectMessage) message;
try {
onMessage((MessageModel) aMsg.getObject());
} catch (Exception e) {
_logger.warn("Message:${} is not a instance of MessageModel.", e);
}
} else {
_logger.warn("Message:${} is not a instance of ActiveMQObjectMessage.");
}
} /**
* 处理消息
* @param message 自定义消息实体
*/
public void onMessage(MessageModel message) { ... } }
这里给大家演示的并不是最基础的知识,处理的消息是一个自定义的类“lekko.mq.model.MessageModel”,这个类怎么写可以随便整,反正就是一些你要传递的数据字段,但是记得要实现Serializable接口。如果你需要传递的仅仅是纯字符串,那么直接在代码的23行片,把message.toString()即可。这个类通过前面XML配置会处理来自“worker_topic”队列中的消息。
再就是发送类,实际上就是把前面的taskServiceTemplate拿来用就行了:
package lekko.mq.task; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.jms.core.JmsTemplate;
import org.springframework.stereotype.Service;
import lekko.mq.model.MessageModel; /**
* 服务器任务消息分发
* @author lekko
*/
@Service
public class TaskServerSender { @Autowired
@Qualifier("taskServerTemplate")
private JmsTemplate jmsTemplate; /**
* 发送消息
*/
public void sendMessage(MessageModel msg) {
jmsTemplate.convertAndSend(msg);
} }
把这个类TaskServerSender注入到任意需要用到的地方,调用sendMessage方法即可。它会往前面定义的“server_topic”中塞消息,等Worker来取。
4、关于Zookeeper配置MQ连接信息
Worker端的配置我这里不再阐述,因为它跟在Server端的配置太相像,区别就在于Server端是从worker_topic中取消息,往server_topic中写消息;而Worker端的代码则是反过来,往worker_topic中写消息,从server_topic中取消息。
那么如何使用Java代码来控制ActiveMQ的配置消息呢:
package lekko.mq.util; import org.apache.zookeeper.ZooKeeper;
import org.apache.zookeeper.data.Stat; /**
* 获取MQ配置
* @author lekkoli
*/
public class MQPropertiesFactory { private static boolean isLoaded = false;
private static String ZOOKEEPER_CLUST = "xxx.xxx.xxx.xxx:2181";
private static ZooKeeper _zk;
private static String _ip;
private static String _port; private static String getProperty(String path) throws Exception {
if (_zk == null) {
if (ZOOKEEPER_CLUST == null) {
throw new Exception("Zookeeper, Host \"" + ZOOKEEPER_CLUST + "\" is null!");
}
_zk = new ZooKeeper(ZOOKEEPER_CLUST, 90000, null);
}
Stat s = _zk.exists(path, false);
if (s != null)
return new String(_zk.getData(path, false, s));
throw new Exception("Zookeeper, Path \"" + path + "\" is not exist!");
} private static void load() throws Exception {
if (!isLoaded) {
_ip = getProperty("/mq/activemq/ip");
_port = getProperty("/mq/activemq/port");
isLoaded = true;
}
} public static String getUrl() throws Exception {
load();
StringBuilder failover = new StringBuilder();
String[] ips = _ip.split(";"), ports = _port.split(";");
for (int i = 0; i < ips.length; ++i) {
failover.append("tcp://").append(ips[i]).append(":").append(ports[i]).append(",");
}
failover.setLength(failover.length() - 1);
String failovers = failover.toString();
if (ips.length > 1) {
failovers = "failover:(" + failovers + ")";
}
return failovers;
}
}
上面的代码需要解释的地方跟MQ相关的不多,主要就是如果是mq集群,则格式是:failover:(tcp://192.168.1.117:1001,tcp://192.168.1.118:1001,tcp://xxx.xxx.xxx.xxx:port)。其它上面代码没有对Zookeeper集群都挂了的情况,做应急连接方案。当然,无论如何本节都不是全文的重点,但是多学一技何尝不可?
最近工作越来越忙,更新博客也是时有时无,但是我会坚持下去,还有许多工作中的点滴,在这里沉淀一下,也希望更进一步吧。
转载请注明原址:http://www.cnblogs.com/lekko/p/4940976.html
Spring下ActiveMQ实战的更多相关文章
- spring +ActiveMQ 实战 topic selecter指定接收
spring +ActiveMQ 实战 topic selecter指定接收 queue:点对点模式,一个消息只能由一个消费者接受 topic:一对多,发布/订阅模式,需要消费者都在线(可能会导致信息 ...
- 在Spring下集成ActiveMQ
1.参考文献 Spring集成ActiveMQ配置 Spring JMS异步发收消息 ActiveMQ 2.环境 在前面的一篇ActiveMQ入门实例中我们实现了消息的异步传送,这篇博文将如何在spr ...
- Java消息队列-Spring整合ActiveMq
1.概述 首先和大家一起回顾一下Java 消息服务,在我之前的博客<Java消息队列-JMS概述>中,我为大家分析了: 消息服务:一个中间件,用于解决两个活多个程序之间的耦合,底层由Jav ...
- 从零开始学 Java - Spring 集成 ActiveMQ 配置(一)
你家小区下面有没有快递柜 近两年来,我们收取快递的方式好像变了,变得我们其实并不需要见到快递小哥也能拿到自己的快递了.对,我说的就是类似快递柜.菜鸟驿站这类的代收点的出现,把我们原来快递小哥必须拿着快 ...
- 从零开始学 Java - Spring 集成 ActiveMQ 配置(二)
从上一篇开始说起 上一篇从零开始学 Java - Spring 集成 ActiveMQ 配置(一)文章中讲了我关于消息队列的思考过程,现在这一篇会讲到 ActivMQ 与 Spring 框架的整合配置 ...
- Apache ActiveMQ实战(1)-基本安装配置与消息类型
ActiveMQ简介 ActiveMQ是一种开源的,实现了JMS1.1规范的,面向消息(MOM)的中间件,为应用程序提供高效的.可扩展的.稳定的和安全的企业级消息通信.ActiveMQ使用Apache ...
- Spring整合ActiveMQ及多个Queue消息监听的配置
消息队列(MQ)越来越火,在java开发的项目也属于比较常见的技术,MQ的相关使用也成java开发人员必备的技能.笔者公司采用的MQ是ActiveMQ,且消息都是用的点对点的模式.本文记录了实 ...
- 淘淘商城项目_同步索引库问题分析 + ActiveMQ介绍/安装/使用 + ActiveMQ整合spring + 使用ActiveMQ实现添加商品后同步索引库_匠心笔记
文章目录 1.同步索引库问题分析 2.ActiveM的介绍 2.1.什么是ActiveMQ 2.2.ActiveMQ的消息形式 3.ActiveMQ的安装 3.1.安装环境 3.2.安装步骤 4.Ac ...
- ActiveMQ学习总结(3)——spring整合ActiveMQ
1.参考文献 Spring集成ActiveMQ配置 Spring JMS异步发收消息 ActiveMQ 2.环境 在前面的一篇ActiveMQ入门实例中我们实现了消息的异步传送,这篇博文将如何在spr ...
随机推荐
- Response.Redirect引起的性能问题分析
现象: 最近做的一个系统通过单点登录(SSO) 技术验证用户登录.用户在SSO 系统上通过验证后,跳转到该系统的不同模块.而跳转的时间一直维持子啊几分钟左右. 分析步骤: 在问题复现时抓取Hang d ...
- VICA 架构设计(1)
本文记录最近完成的一个通用实时通信客户端的架构. 背景 我们公司是做税务相关的软件,有针对大客户 MIS 系统,也有针对中小客户的 SaaS 平台.这些系统虽然都是 B/S 的,但是也需要使用 A ...
- ORA-00821: Specified value of sga_target 3072M is too small, needs to be at least 12896M
在测试PlateSpine克隆的数据库服务器时,由于资源有限,克隆过来的数据库服务器只给了9G的内存,结果在测试时,老是会出现OOMkiller导致宕机,即out of memory killer,是 ...
- SQLServer如何添加try catch
在.net中我们经常用到try catch.不过在sqlserver中我们也可以使用try catch捕捉错误,在这里把语法记录下来和大家分享一下, --构建存储过程CREATE PROCEDURE ...
- Struts2日期类型转换
针对日期类java.util.Date进行类型转换,要求客户端使用"yyyy-MM-dd","yyyy/MM/dd"中的任意一种输入,并以"yyyy- ...
- Mono产品生命周期
软件生命周期 同任何事物一样,一个软件产品或软件系统也要经历孕育.诞生.成长.成熟.衰亡等阶段,一般称为软件生命周期(软件生存周期) .软件生命周期模型是指人们为开发更好的软件而归纳总结的软件生命周期 ...
- Mono 4.0 Mac上运行asp.net mvc 5.2.3
Mono 4.0 已经发布,二进制包已经准备好,具体的发布说明参见:http://www.mono-project.com/docs/about-mono/releases/4.0.0/. 今天在Ma ...
- 【完全开源】知乎日报UWP版:项目结构说明、关键源代码解释
目录 说明 项目结构 关键代码 演示视频 说明 上一篇博客将源码放出来了,但是并没有做过多的介绍,所以如果自己硬看可能需要花费很长的时间,尤其这些代码并不是自己写的.项目不算复杂但是也不算简单,这篇文 ...
- CSharpGL(28)得到高精度可定制字形贴图的极简方法
CSharpGL(28)得到高精度可定制字形贴图的极简方法 回顾 以前我用SharpFont实现了解析TTF文件从而获取字形贴图的功能,并最终实现了用OpenGL渲染文字. 使用SharpFont,美 ...
- 父ListView嵌套子ListView时点击事件没有响应
转发请备注出处:http://www.cnblogs.com/LT5505/p/5972999.html 问题: 在ListView中嵌套ListView之后,子ListView会把父ListView ...