MQ消息中间件介绍及使用
MQ
全称Message Queue(消息队列),在分布式系统中有着广泛应用,满足异步、解耦等需求。消息中间件是分布式系统中接收和发送消息的基础软件,有很多,如RabbitMQ RocketMQ ActiveMQ Kafka等。
消息中间件可使系统间进行异步通讯,实现解耦。
JMS消息模型
JMS是JavaEE中的一个关于消息的规范,是一套与具体平台无关的API。
1、点对点或队列模型
JMS 点对点队列模型特点:
1、消息生产者生产消息发送到queue中,然后消息消费者从queue中取出并且消费消息。
2、消息被消费以后,queue中不再有存储,所以消息消费者不可能消费到已经被消费的消息。
3、Queue支持存在多个消费者,但是对一个消息而言,只会有一个消费者可以消费。
2、发布者/订阅者模型
JMS 发布/订阅模型特点:
消息生产者(发布)将消息发布到topic中,同时有多个消息消费者(订阅)消费该消息。
发布到topic的消息会被所有订阅者消费。
实现了JMS规范的消息中间件产品:ActiveMQ、RocketMQ、RabbitMQ、HornetQ …
activeMQ安装
官网:http://activemq.apache.org
1、安装jdk,参见"linux/服务器搭建1.docx"
2、下载apache-activemq-5.11.1-bin.tar.gz,解压
如放到/root下
tar -zxf apache-activemq-5.11.1-bin.tar.gz
重命名解压后的目录
mv apache-activemq-5.11.1 activemq-01
3、进入activemq-01/bin,查看是否有执行启动脚本activemq文件的权限(绿色表示有),如果没有,则执行
chmod 755 activemq授权。
4、防火墙打开61616和8161端口,61616是消息通讯端口,在activemq-01/conf/activemq.xml中可以修改。8161是管理控制台的jetty端口,在activemq-01/conf/jetty.xml中配置。
5、启动服务,浏览器访问
进入bin目录启动
cd /root/activemq-01/bin/
./activemq start
浏览器即可访问
6、点击这里,默认密码是admin admin
7、配置安全机制
无安全机制情况下所有人都可以随意收发mq消息,不安全。因此需要配置安全机制,在conf/activemq.xml中</broker>结束标签之前增加如下配置:
<plugins>
<simpleAuthenticationPlugin>
<users>
<authenticationUser username="wusc" password="wusc.123"
groups="users,admins" />
</users>
</simpleAuthenticationPlugin>
</plugins>
定义了一个用户wusc,密码是wusc.123,角色是users,admins,表示程序连入mq服务器所需要的验证。
管控台的密码修改
1、确保conf/jetty.xml中securityConstraint下的authenticate是true
2、在conf/jetty-realm.properties下:
三列分别表示用户名、密码、角色
修改后重启jetty即可。
./activemq restart
java客户端
点对点模式
生产者producer配置
spring中配置:
<!-- 真正可以产生Connection的ConnectionFactory,由对应的 JMS服务厂商提供 -->
<bean id="targetConnectionFactory" class="org.apache.activemq.ActiveMQConnectionFactory">
<!-- ActiveMQ服务地址 -->
<property name="brokerURL" value="${mq.brokerURL}" />
<property name="userName" value="${mq.userName}"></property>
<property name="password" value="${mq.password}"></property>
</bean>
<!--
ActiveMQ为我们提供了一个PooledConnectionFactory,通过往里面注入一个ActiveMQConnectionFactory
可以用来将Connection、Session和MessageProducer池化,这样可以大大的减少我们的资源消耗。
要依赖于 activemq-pool包
-->
<bean id="pooledConnectionFactory" class="org.apache.activemq.pool.PooledConnectionFactory">
<property name="connectionFactory" ref="targetConnectionFactory" />
<property name="maxConnections" value="${mq.pool.maxConnections}" />
</bean>
<!-- Spring用于管理真正的ConnectionFactory的ConnectionFactory -->
<bean id="connectionFactory" class="org.springframework.jms.connection.SingleConnectionFactory">
<!-- 目标ConnectionFactory对应真实的可以产生JMS Connection的ConnectionFactory -->
<property name="targetConnectionFactory" ref="pooledConnectionFactory" />
</bean>
<!-- Spring提供的JMS工具类,它可以进行消息发送、接收等 -->
<!-- 队列模板 -->
<bean id="activeMqJmsTemplate" class="org.springframework.jms.core.JmsTemplate">
<!-- 这个connectionFactory对应的是我们定义的Spring提供的那个ConnectionFactory对象 -->
<property name="connectionFactory" ref="connectionFactory"/>
<property name="defaultDestinationName" value="${queueName}"></property>
</bean>
类似于jdbc的配置,targetConnectionFactory配置mq的服务器地址,登录账号密码,
最终使用的是activeMqJmsTemplate,它实现了JmsTemplate
mq.pool.maxConnections是连接池数
queueName是队列名称,表示发送到哪一个队列
使用类:
@Service("mqProducer")
public class MQProducer {
@Autowired
private JmsTemplate activeMqJmsTemplate;
/**
* 发送消息.
* @param mail
*/
public void sendMessage(final MailParam mail) {
activeMqJmsTemplate.send(new MessageCreator() {
public Message createMessage(Session session) throws JMSException {
return session.createTextMessage(JSONObject.toJSONString(mail));
}
});
}
}
public class MQProducerTest {
private static final Log log = LogFactory.getLog(MQProducerTest.class);
public static void main(String[] args) {
try {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("classpath:spring/spring-context.xml");
context.start();
MQProducer mqProducer = (MQProducer) context.getBean("mqProducer");
// 邮件发送
MailParam mail = new MailParam();
mail.setTo("wu-sc@foxmail.com");
mail.setSubject("ActiveMQ测试");
mail.setContent("通过ActiveMQ异步发送邮件!");
mqProducer.sendMessage(mail);
context.stop();
} catch (Exception e) {
log.error("==>MQ context start error:", e);
System.exit(0);
} finally {
log.info("===>System.exit");
System.exit(0);
}
}
}
执行后查看界面,就有记录了
各列分别表示:队列名、等待消费消息数、消息进入数、消费消息数
消费者consumer配置
spring配置类似生产者,只是多了消息目的地和listener配置
<!--这个是sessionAwareQueue目的地 -->
<bean id="sessionAwareQueue" class="org.apache.activemq.command.ActiveMQQueue">
<constructor-arg>
<value>${queueName}</value>
</constructor-arg>
</bean>
<!-- 可以获取session的MessageListener -->
<bean id="consumerSessionAwareMessageListener" class="wusc.edu.demo.mqtest.listener.ConsumerSessionAwareMessageListener"></bean>
<bean id="sessionAwareListenerContainer" class="org.springframework.jms.listener.DefaultMessageListenerContainer">
<property name="connectionFactory" ref="connectionFactory" />
<property name="destination" ref="sessionAwareQueue" />
<property name="messageListener" ref="consumerSessionAwareMessageListener" />
</bean>
queueName表示消息队列名。
mq.properties
## MQ
mq.brokerURL=tcp\://192.168.128.129\:61616
mq.userName=wusc
mq.password=wusc.123
mq.pool.maxConnections=10
#queueName
queueName=wusc.edu.mqtest.v1
监听listener:
public class ConsumerSessionAwareMessageListener implements SessionAwareMessageListener<Message> {
private static final Log log = LogFactory.getLog(ConsumerSessionAwareMessageListener.class);
@Autowired
private JmsTemplate activeMqJmsTemplate;
@Autowired
private Destination sessionAwareQueue;
@Autowired
private MailBiz bailBiz;
public synchronized void onMessage(Message message, Session session) {
try {
ActiveMQTextMessage msg = (ActiveMQTextMessage) message;
final String ms = msg.getText();
log.info("==>receive message:" + ms);
MailParam mailParam = JSONObject.parseObject(ms, MailParam.class);// 转换成相应的对象
if (mailParam == null) {
return;
}
try {
bailBiz.mailSend(mailParam);
} catch (Exception e) {
// 发送异常,重新放回队列
// activeMqJmsTemplate.send(sessionAwareQueue, new MessageCreator() {
// public Message createMessage(Session session) throws JMSException {
// return session.createTextMessage(ms);
// }
// });
log.error("==>MailException:", e);
}
} catch (Exception e) {
log.error("==>", e);
}
}
}
收到消息后会自动调用onMessage,通过msg.getText()获取消息内容
运行消费者的main,启动监听,此时会立刻收到消息,界面变为:
等待消费消息变为0,消费者变为1,入列1,出列1
如果停止监听程序,消费者变为0
在监听程序开启的情况下,再发一条消息,消息会立刻被消费,界面变为:
增加了一个入列和出列数。
发布订阅模式
生产者producer配置
spring配置
<!-- connection+topic=template,每个主题需要一个topic的bean和一个template的bean -->
<!-- connection连接-->
<bean id="targetConnectionFactory" class="org.apache.activemq.ActiveMQConnectionFactory">
<!-- ActiveMQ服务地址 -->
<property name="brokerURL" value="${mq.brokerURL}" />
<property name="userName" value="${mq.userName}"></property>
<property name="password" value="${mq.password}"></property>
</bean>
<!-- 连接池 -->
<bean id="pooledConnectionFactory" class="org.apache.activemq.pool.PooledConnectionFactory">
<property name="connectionFactory" ref="targetConnectionFactory" />
<property name="maxConnections" value="${mq.pool.maxConnections}" />
</bean>
<!-- connectionFactory -->
<bean id="connectionFactory" class="org.springframework.jms.connection.SingleConnectionFactory">
<property name="targetConnectionFactory" ref="pooledConnectionFactory" />
</bean>
<!-- companyList start -->
<bean id="companyListTopic" class="org.apache.activemq.command.ActiveMQTopic">
<constructor-arg index="0" value="${company.list.topic}" />
</bean>
<bean id="companyListTemplate" class="org.springframework.jms.core.JmsTemplate">
<property name="connectionFactory" ref="connectionFactory" /> <!-- connectionFactory -->
<property name="defaultDestination" ref="companyListTopic" /> <!-- topic -->
<property name="pubSubDomain" value="true" /> <!-- 订阅发布模式 -->
<property name="receiveTimeout" value="10000" />
</bean>
<!-- companyList end -->
<!-- companyEdit start -->
<bean id="companyEditTopic" class="org.apache.activemq.command.ActiveMQTopic">
<constructor-arg index="0" value="${company.edit.topic}" />
</bean>
<bean id="companyEditTemplate" class="org.springframework.jms.core.JmsTemplate">
<property name="connectionFactory" ref="connectionFactory" /> <!-- connectionFactory -->
<property name="defaultDestination" ref="companyEditTopic" /> <!-- topic -->
<property name="pubSubDomain" value="true" /> <!-- 订阅发布模式 -->
<property name="receiveTimeout" value="10000" />
</bean>
<!-- companyEdit end -->
test.properties:
#mq
mq.brokerURL=tcp\://192.168.128.134\:61616
mq.userName=kkk
mq.password=kkk.123
mq.pool.maxConnections=10
company.list.topic=mvd.company.man.company.list.test
company.edit.topic==mvd.company.man.company.edit.test
java调用
final String message = "查询公司信息,company=" + JSONObject.toJSON(company);
LOGGER.info("发布company_list的mq:{}", message);
companyListTemplate.send(new MessageCreator() {
public Message createMessage(Session session) throws JMSException {
TextMessage msg = session.createTextMessage();
msg.setStringProperty("hello", "company_list"); // 设置消息属性
msg.setText(message); // 设置消息内容
return msg;
}
});
消费者consumer配置
spring配置
<!-- 接收: connection+topic+自定义接收类MessageListener=ListenerContainer -->
<!-- 配置JMS连接工厂 -->
<bean id="connectionFactory"
class="org.springframework.jms.connection.CachingConnectionFactory">
<!-- Session缓存数量 -->
<property name="sessionCacheSize" value="10" />
<property name="clientId" value="${client.id}" /> <!-- 接收者ID -->
<property name="targetConnectionFactory">
<bean class="org.apache.activemq.ActiveMQConnectionFactory">
<!-- MQ地址 -->
<property name="brokerURL" value="${mq.brokerURL}" />
<property name="userName" value="${mq.userName}"></property>
<property name="password" value="${mq.password}"></property>
</bean>
</property>
</bean>
<!-- companyList start -->
<!-- topic -->
<bean id="companyListTopic" class="org.apache.activemq.command.ActiveMQTopic">
<constructor-arg index="0" value="${company.list.topic}" />
</bean>
<!-- listener -->
<bean id="companyListListener" class="com.mvd.erp.mq.CompanyListListener" />
<!-- container -->
<bean id="companyListListenerContainer"
class="org.springframework.jms.listener.DefaultMessageListenerContainer">
<property name="connectionFactory" ref="connectionFactory" /> <!-- connectionFactory -->
<property name="destination" ref="companyListTopic" /> <!-- topic -->
<property name="messageListener" ref="companyListListener" /> <!-- 自定义Listener -->
<property name="pubSubDomain" value="true" /> <!-- 发布订阅模式 -->
<!-- 消息持久化 -->
<property name="subscriptionDurable" value="true" />
<property name="receiveTimeout" value="10000" />
<property name="clientId" value="${client.id}" /> <!-- 接收者ID -->
<property name="durableSubscriptionName" value="${company.list.topic}" />
</bean>
<!-- companyList end -->
<!-- companyEdit start -->
<!-- topic -->
<bean id="companyEditTopic" class="org.apache.activemq.command.ActiveMQTopic">
<constructor-arg index="0" value="${company.edit.topic}" />
</bean>
<!-- listener -->
<bean id="companyEditListener" class="com.mvd.erp.mq.CompanyEditListener" />
<!-- container -->
<bean id="companyEditListenerContainer"
class="org.springframework.jms.listener.DefaultMessageListenerContainer">
<property name="connectionFactory" ref="connectionFactory" /> <!-- connectionFactory -->
<property name="destination" ref="companyEditTopic" /> <!-- topic -->
<property name="messageListener" ref="companyEditListener" /> <!-- 自定义Listener -->
<property name="pubSubDomain" value="true" /> <!-- 发布订阅模式 -->
<!-- 消息持久化 -->
<property name="subscriptionDurable" value="true" />
<property name="receiveTimeout" value="10000" />
<property name="clientId" value="${client.id}" /> <!-- 接收者ID -->
<property name="durableSubscriptionName" value="${company.edit.topic}" />
</bean>
<!-- companyEdit end -->
listener类
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.MessageListener;
import javax.jms.TextMessage;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
/**
* @描述: companylist的topic监听器 .
*
*/
@Component
public class CompanyListListener implements MessageListener {
private static final Logger LOGGER = LoggerFactory.getLogger(CompanyListListener.class);
@Override
public void onMessage(Message message) {
TextMessage textMessage = (TextMessage)message;
try {
LOGGER.info("CompanyListListener收到消息,getText()={}, getStringProperty(\"hello\")={}", textMessage.getText(), message.getStringProperty("hello"));
} catch (JMSException e) {
LOGGER.error("接收消息失败", e);
}
}
}
test.properties:
#mq
mq.brokerURL=tcp\://192.168.128.134\:61616
mq.userName=kkk
mq.password=kkk.123
client.id=mvd.erp.man
mq.pool.maxConnections=10
company.list.topic=mvd.company.man.company.list.test
company.edit.topic==mvd.company.man.company.edit.test
activeMQ集群
概念
三种集群方式:
共享文件目录、共享数据库、zookeeper+levelDB(基于可复制的levelDB)
主要是为了解决集群切换时的数据同步问题,单节点时默认采用共享文件目录方式。集群配置一般采用zookeeper+levelDB方式。
引用:
LevelDB 是 Google 开发的一套用于持久化数据的高性能类库。LevelDB 并不是一种服务,用户需要自
行实现 Server。是单进程的服务,能够处理十亿级别规模 Key-Value 型数据,占用内存小。
采用zookeeper只用用它提供主备功能,避免单点故障,不实现负载均衡等功能。
高可用的原理:
原理图:
使用 ZooKeeper(集群)注册所有的 ActiveMQ Broker(节点)。只有其中的一个 Broker 可以提供服务,被视为 Master,其他的 Broker 处于待机状态,被视为 Slave。如果 Master 因故障而不能提供服务,ZooKeeper 会从 Slave 中选举出一个 Broker 充当 Master。
Slave 连接 Master 并同步他们的存储状态,Slave 不接受客户端连接。所有的存储操作都将被复制到连接至 Master 的 Slaves。如果 Master 宕了,得到了最新更新的 Slave 会成为 Master。故障节点在恢复后会重新加入到集群中并连接 Master 进入 Slave 模式
zookeeper+levelDB方式集群安装
需要首先安装zookeeper(一般都用zookeeper集群,而不用zookeeper单节点)。
这里用192.168.128.129、192.168.128.130、192.168.128.131三台服务器做集群
主机 | 集群端口 | 消息端口 | 管控台端口 | 节点安装目录 |
192.168.128.129 | 62621 | 51511 | 8161 | ~/activemq/node-01 |
192.168.128.130 | 62622 | 51512 | 8162 | ~/activemq/node-02 |
192.168.128.131 | 62623 | 51513 | 8163 | ~/activemq/node-03 |
安装集群主要是配置activemq内部通信的集群端口、对外提供的消息端口、管理控制台的端口。
1、配置host,三台服务器分别创建activemq目录
三台服务器都增加hosts
vim /etc/hosts
192.168.128.129 edu-zk-01
192.168.128.130 edu-zk-02
192.168.128.131 edu-zk-03
创建activemq目录
mkdir ~/activemq
2、将apache-activemq-5.11.1-bin.tar.gz分别放到三台服务器的~用户目录,解压,按节点重命名
129:
cd ~
tar -zxf apache-activemq-5.11.1-bin.tar.gz
mv apache-activemq-5.11.1 activemq-node-01
130:
cd ~
tar -zxf apache-activemq-5.11.1-bin.tar.gz
mv apache-activemq-5.11.1 activemq-node-02
131:
cd ~
tar -zxf apache-activemq-5.11.1-bin.tar.gz
mv apache-activemq-5.11.1 activemq-node-03
3、分别修改3台服务器activemq管理控制台端口
vim activemq-node-03/conf/jetty.xml
Node-01 管控台端口:
<bean id="jettyPort" class="org.apache.activemq.web.WebConsolePort" init-method="start">
<!-- the default port number for the web console -->
<property name="host" value="0.0.0.0"/>
<property name="port" value="8161"/>
</bean>
Node-02 管控台端口:
<bean id="jettyPort" class="org.apache.activemq.web.WebConsolePort" init-method="start">
<!-- the default port number for the web console -->
<property name="host" value="0.0.0.0"/>
<property name="port" value="8162"/>
</bean>
Node-03 管控台端口:
<bean id="jettyPort" class="org.apache.activemq.web.WebConsolePort" init-method="start">
<!-- the default port number for the web console -->
<property name="host" value="0.0.0.0"/>
<property name="port" value="8163"/>
</bean>
4、分别修改conf/activemq.xml,配置持久化适配器,修改消息端口
129:
<broker xmlns="http://activemq.apache.org/schema/core" brokerName="DubboEdu" dataDirectory="${activemq.data}">
<persistenceAdapter>
<!-- kahaDB directory="${activemq.data}/kahadb"/ -->
<replicatedLevelDB
directory="${activemq.data}/leveldb"
replicas="3"
bind="tcp://0.0.0.0:62621"
zkAddress="192.168.128.129:2181,192.168.128.130:2182,192.168.128.131:2183"
hostname="edu-zk-01"
zkPath="/activemq/leveldb-stores"
/>
</persistenceAdapter>
<transportConnector name="openwire" uri="tcp://0.0.0.0:51511?maximumConnections=1000&wireFormat.maxFrameSize=104857600"/>
130:
<broker xmlns="http://activemq.apache.org/schema/core" brokerName="DubboEdu" dataDirectory="${activemq.data}">
<persistenceAdapter>
<!-- kahaDB directory="${activemq.data}/kahadb"/ -->
<replicatedLevelDB
directory="${activemq.data}/leveldb"
replicas="3"
bind="tcp://0.0.0.0:62622"
zkAddress="192.168.128.129:2181,192.168.128.130:2182,192.168.128.131:2183"
hostname="edu-zk-02"
zkPath="/activemq/leveldb-stores"
/>
</persistenceAdapter>
<transportConnector name="openwire" uri="tcp://0.0.0.0:51512?maximumConnections=1000&wireFormat.maxFrameSize=104857600"/>
131:
<broker xmlns="http://activemq.apache.org/schema/core" brokerName="DubboEdu" dataDirectory="${activemq.data}">
<persistenceAdapter>
<!-- kahaDB directory="${activemq.data}/kahadb"/ -->
<replicatedLevelDB
directory="${activemq.data}/leveldb"
replicas="3"
bind="tcp://0.0.0.0:62623"
zkAddress="192.168.128.129:2181,192.168.128.130:2182,192.168.128.131:2183"
hostname="edu-zk-03"
zkPath="/activemq/leveldb-stores"
/>
</persistenceAdapter>
<transportConnector name="openwire" uri="tcp://0.0.0.0:51513?maximumConnections=1000&wireFormat.maxFrameSize=104857600"/>
说明:
其中persistenceAdapter是配置持久化适配器,replicatedLevelDB 表示用复制levelDB的方式。
replicas 是activemq集群的节点数,每个activemq没有配置其他节点的地址,而是通过注册中心去找其他节点,是否大于一半节点可用的判断就是用这个节点数除以2,再跟zookeeper上找到的节点数做的比较。
bind是集群节点间的通信端口,zkAddress是zookeeper集群地址。
hostname是/etc/hosts配置的。
transportConnector的51511、51512、51513是消息端口。每个 ActiveMQ 的 BrokerName 必须相同,否则不能加入集群
5、按顺序启动三台服务器的mq
129
cd ~/activemq-node-01/bin/
./activemq start
130
cd ~/activemq-node-02/bin/
./activemq start
131
cd ~/activemq-node-03/bin/
./activemq start
6、使用ZooInspector可以查看zookeeper节点状态
windows上:
进入zookeeper-dev-ZooInspector.jar所在目录,然后执行jar即可。
cd D:\acce\dubbo\ZooInspector\build
java -jar zookeeper-dev-ZooInspector.jar
这里输入任何一个zookeeper节点地址都可以,如
192.168.128.129:2181
192.168.128.130:2182
192.168.128.131:2183
可以看到只有节点3是Master,因为elected不为空,其他都是Slave
上面的
对应了activemq.xml 里持久化适配器里的zkPath="/activemq/leveldb-stores"。
这个zookeeper同时在为activemq和dubbo提供分布式协调服务。
6、客户端连接
客户端连接:
mq.brokerURL=failover:(tcp://192.168.128.129:51511,tcp://192.168.128.130:51512,tcp://192.168.128.131:51513)?randomize=false
其中failover是一个协议,表示失败就换另一台再试
集群高可用
activemq的集群与zookeeper是不同概念,activemq的master与slave跟zookeeper的leader与follower没有关系。
activemq集群中哪个是master哪个是slave需要通过ZooInspector连接到zookeeper节点上去看。
通过这个通信端口去查配置即可
activemq集群中只有master的节点的管理控制台可以访问。当master挂掉时,会自动推举出一个新的master,会有短时间的闪断,不过不影响业务和数据。必须有大于一半的节点可用,整个activemq集群才可用,否则不会推举出master,集群无法使用。
在zookeeper正常的情况下,activemq集群大于一般节点挂掉又恢复后,会自动再次选出master,集群可自动恢复。
如果zookeeper集群大于一半节点挂掉又恢复后,activemq节点需要全部重启才能重新选举出master。
activemq多集群负载均衡
有很多种方式,如:
配置:
假设有两个集群
集群1:
192.168.128.129:53511、192.168.128.130:53512、192.168.128.131:53513
集群2(伪集群,因为节点都在同一台服务器132):
192.168.128.132:51511、192.168.128.132:51512、192.168.128.132:51513
在集群1的各节点conf/activemq.xml的<persistenceAdapter>标签前面(外面)增加
<networkConnectors>
<networkConnector
uri="static:(tcp://192.168.128.132:51511,tcp://192.168.128.132:51512,tcp://192.168.128.132:51513)"
duplex="false"/>
</networkConnectors>
在集群2的各节点conf/activemq.xml的<persistenceAdapter>前面增加
<networkConnectors>
<networkConnector
uri="static:(tcp://192.168.128.129:51511,tcp://192.168.128.130:51512,tcp://192.168.128.131:51513)"
duplex="false"/>
</networkConnectors>
表示互相通过网桥连接。两个集群只是生产者和消费者会互相消费,相当于实现了负载均衡。不能有集群挂掉,挂掉的集群依然无法使用。
持久化消息和持久化订阅
持久化消息是指mq发送消息之前,会先存到磁盘或数据库里,发送完以后再删除,目的是防止mq服务器挂掉消息丢失的情况,重启以后自动再发送。
持久化订阅是指发布订阅模式下,接收方服务没有启动,消息服务器会将消息暂存下来,带接收方启动以后再发送,没有开启持久化订阅则会直接丢失消息。
一般会把两个都开启。
持久化消息的方式
有AMQ、KahaDB、Jdbc、LevelDB等。
配置在conf/activemq.xml里
1、AMQ
文件方式存储,默认一条消息最大32M,可设置,发送完以后删除文件,特点是速度很快。
配置:
<persistenceAdapter>
<amqPersistenceAdapter directory="activemq-data"maxFileLength="32mb"/>
</persistenceAdapter>
2、KahaDB(默认的方式)
基于文件的本地数据库储存形式,特点是扩展性强,恢复快
配置:
<persistenceAdapter>
<kahaDB directory="${activemq.data}/kahadb"/>
</persistenceAdapter>
3、jdbc
4、LevelDB
与KahaDB类似,也是文件数据库,速度更快
浙公网安备 33010602011771号