多种MQ的探究-ActiveMQ
很久很久以前在公众号上写过一篇关于各消息中间件的分析与简单应用,时隔N久,现在针对这几个MQ做下探究,当然包括最新的应用。
公众号:https://mp.weixin.qq.com/s/0R9MbR8ISf51A9ulJMJlrg
activeMQ
原理
简介:
activemq是一个实现了jms规范的消息代理,支持p2p、pub/sub两种通信机制,对spring支持良好,拥有多种通讯协议。
(jms全称java message service,所以也称他为java消息服务,它是一个技术规范,提供标准的API。)
优缺点:
activemq关键代码比较简单,对配置文件的依赖比较重,配置文件也相对复杂,所以简单快捷可控,不支持自动分片、集群之类的自动化配置,如果需要做集群处理,需要手动进行分片处理,故不是很适用大吞吐量的系统。
activemq可以很好的做到解耦以及数据持久化,并且持久化类型多样(kahaDB、amq、jdbc、mysql等),同时还支持异步和同步的消息推送,消息重发机制也很完善。
关键参数:
provider:mq推送的jms接口实现
domains:mq传递方式:p2p,pub/sub
connection factory:mq创建连接的工厂
destination:消息推送对象
处理步骤:
创建connection
->创建session
->获取destionation->创建producer->创建message->推送->关闭producer
->关闭session
->释放connection
a) 消息推送可以即时获得反馈使用receive()
b) 消息消费可以设置确认ack_mode与ack_type
c) 消息推送失败可以设置重发recover
原理
1.概况:
activemq拥有多种类型消息推送,结构:
producer—transport—broker(topic) —ack—consumer
producer—transport—broker(queue)—ack—consumer
主流程就是service通过transport将消息推送到broker与queue中,然后依据消息类型与队列名或主题去监听对应的模块从而获取消息消费掉。
2.推送
推送主方法:
首先,将消息内容message放入创建的消息转换器中,然后获取jsmTemplate对象来推送消息。
详细说明:
推送的源码:
这里的execute执行方法为:
首先,创建connection、session,然后做对应的action,最后关闭session并释放connection。
session做的action为:
首先是创建producer,会依据消息类型创建对应的producer,源码:
下一步是通过session创建消息对象,这里会根据消息类型来处理:
最后就是消息推送了,有二种推送方式(直接推、延迟推):
最终,消息经过序列化,调用tcptranspor实现:
源码比较多,功能主要为通过transport将消息推送到对应的broker上,然后session提交,消息状态更改,完成推送操作。
最后关闭session与connection。
消费的源码:
消息transport后,我们可以看一下:
消息推送消费同时有配置,我们通过注解@JmsListener来实现监听:
activemq默认队列监听,如果不自己配置主题监听factory,那么就会使用默认:
监听的底层功能为将监听的信息注册到监听其中,实现实时监听,只要消息匹配即实现消费:
安装
1.下载
官网下载:http://activemq.apache.org/components/classic/download/
然后放到服务器中(linux,windows则放在自定义文件夹下)
2.配置
依据需要调整配置:
2.1 conf/activemq.xml:
主要配置为broker节点:brokerName-唯一节点名,dataDirectory-数据目录,可作持久化数据目录,此时要加参数persistent=true
2.1.1 policy
节点内第一个模块为策略,可以存在多组策略,policyEntry策略包含:topic-主题
pendingMessageLimitStrategy为消息限制策略,包含:limit-限制数,超过该数则移除消息。这里本地测试未作调整,实际生产调优则需要优化策略模块,不仅限于消息队列的限制。
2.1.3 managementContext
连接是否自动创建,这里默认关闭,如有需要可以开启,配合代码中创建连接池,所有消息使用一个连接池。
2.1.4 persistenceAdapter
持久化存储,这里kahadb是一个针对消息作持久化的解决方案,数据会追加到logs中,也有其他db支持消息持久化,例如db2,mysql,oracle等等,可以根据需要进行设置。
ps:很多时候消息持久化进库是一个消息监控记录最普遍的应用,那么这里需要作调整:
删除kahaDB配置,加入:
jdbcPersistence dataSourceRef=“xxx”//这里配置需要的db名字,可自定义,然后加入一个bean配置db属性:
< bean id = “xx” class=“xx”/>
<property name=“drivername” …
2.1.5 systemUsage
系统使用情况,包含memoryUsage-非持久化消息占用内存大小,storeUsage-持久化消息硬盘占用,temUsage-临时消息占用硬盘大小
2.1.6 transportConnectors
传输连接配置,activemq支持的传输协议:vm、tcp、ssl、http https等等,这里配置后会排除我们不用的协议。
配置中包含五种协议:openwire-自定义协议,amqp-一个消息队列标准协议,stomp-流文本定向消息协议,mqtt-即时通讯协议,ws-websocket协议。
这五种协议性能存在差异,amqp>mqtt>openwire>stomp>ws
2.1.7 shutdownHooks
认证插件,这里主要配置用户名、ip啥的,默认可以不用配。
broker节点到这里就基本结束了,然而在之后activemq.xml还引用了另一个配置文件,那就是jettty.xml:
2.2 conf/jetty.xml
关于jetty配置文件,是属于activemq的安全方面的配置,而我们需要使用的主要的节点:
其中可以调整的配置为ip与端口。
其余配置基本为权限安全方面的,日常使用中其实基本不用作调整。
3.启动
在调整完配置后,进入目录./bin启动:
./activemq start
启动会输出日志,如果无报错,查看是否启动成功:
然后浏览器打开地址:ip:8161(默认端口8161如果未调整的话,java端口为61616)
第一个选择为管理mq,可以监测queue与topic以及连接推送情况:
到此activemq安装启动成功结束。
ps:
笔者重启虚拟机后,再次启动activemq后,突然发现访问不到了,内部curl http://xxx.xx.xx.xx:8161是可以的,外部无论怎么请求都不行,查看了data目录下的activemq.log也没报错,于是查看防火墙:
也是关闭了的,由于笔者linux是centos7系统,最后发现是iptables这个防火墙的问题,虽然centos7不使用centos6的ipadbles,但还是存在,所以需要禁用:
systemctl stop iptables
systemctl disable iptables
应用
开发环境:
IntelliJ IDEA + springboot
方式一、单机应用
简单使用:
首先引入依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-activemq</artifactId>
</dependency>
由于集成了queue与topic两种模式,故需配置:
然后定义需要的queue与topic:
@Bean(name = "myQueue")
public Queue myQueue()
{
return new ActiveMQQueue(MqQueue.ACTIVE_MQ_MY_QUEUE);
}
@Bean(name = "myTopic")
public Topic topic()
{
return new ActiveMQTopic(MqTopic.ACTIVE_MQ_MY_TOPIC) ;
}
然后直接注入依赖推送消息:
消费者:
配置文件:
spring:
activemq:
broker-url: tcp://192.168.99.128:61616
user: admin
password: admin
in-memory: false
packages:
trust-all: true
pool:
enabled: false
# jms:
# pub-sub-domain: false # false-queue true-topoc ,并存queue与topic不用配置该参数
方式二、组件应用
基于消息组件的开发,需要一个稳定可上线的产品,故需抽成独立的组件应用。
那么就需要有针对性的抽象及配置。
这里再提一句,activemq默认queue,对队列的支持相对较好,例如推送了一条消息,服务宕机未接收到,重启服务会接收到该信息,而主题模式则没接收到就是没了。
建议与项目独立开,开发一个activemq的组件工程:
以jar包的方式提供使用:
1.配置
由于版本更新,许多老配置已弃用,故根据实际使用情况采用针对性配置:
2.queue与topic并存设置
activemq默认queue模式,故如需同时支持topic则需要进行配置:
@Configuration
@EnableJms
public class ActiveMQConfig
{
public static final String TOPIC_FACTORY = "jmsTopicListenerContainerFactory";
@Bean
public JmsListenerContainerFactory jmsTopicListenerContainerFactory(ConnectionFactory connectionFactory)
{
DefaultJmsListenerContainerFactory factory = new DefaultJmsListenerContainerFactory();
factory.setConnectionFactory(connectionFactory);
//这里必须设置为true,false则表示是queue类型
factory.setPubSubDomain(true);
return factory;
}
}
3.消息推送
下面就是消息推送的类:
默认的destination是queue名,不支持topic,需要新增topic的推送:
public Boolean sendTopicMessage(Topic topic, String message)
{
try
{
jmsMessagingTemplate.convertAndSend(topic, message);
} catch (Exception e)
{
log.error("message send error ! " + e.getMessage());
return Boolean.FALSE;
}
return Boolean.TRUE;
}
topic模式参数需要提前创建,其实queue也可采用此方式(sendQueueMessage(Queue queue, String message)),此时只需要创建对应topic:
@Configuration
public class MqTopic
{
public static final String ACTIVE_MQ_MY_TOPIC = "MY_ACTIVE_MQ_TOPIC";
@Bean(name = "myTopic")
public Topic myTopic()
{
return new ActiveMQTopic(ACTIVE_MQ_MY_TOPIC);
}
}
有人可能使用另一种,即直接在使用的时候new出来使用,笔者并不推荐,每次new的对象都是一个新的,虽然底层针对new都是进行set操作,但提前定义好公用可以防止很多问题的发生,底层对new出名称的set操作(每次new都需要跑一遍,然后比对!):
protected ActiveMQDestination(String name) {
setPhysicalName(name);
}
最后实际推送开发引入依赖:
@Autowired
private ActiveMQProducer activeMQProducer;
@Autowired
private Topic myTopic;
调用接口:
activeMQProducer.sendMessage(MqQueue.ACTIVE_MQ_MY_QUEUE, params.get("context"));
activeMQProducer.sendTopicMessage(myTopic, params.get("context"));
4.消息消费
消息消费的时候,队列模式直接使用队列名,而主题模式则需要另外配置factory支持:
@JmsListener(destination= MqQueue.ACTIVE_MQ_MY_QUEUE)
public void ListenMyQueue(String msg)
{
System.out.println("ActiveMQConsumer接收到queue消息:" + msg);
}
@JmsListener(destination= MqTopic.ACTIVE_MQ_MY_TOPIC, containerFactory = ActiveMQConfig.TOPIC_FACTORY)
public void ListenMyTopic(String msg)
{
System.out.println("ActiveMQConsumer接收到topic消息:" + msg);
}
建立两个topic类两个queue类来测试:
queue会被无序接收,且只会被处理一次,而topic所有sub都会接收,且每次都处理。
code源码:
https://github.com/lovezmming/zm-mq
微信公众号:像是风