MQ(Message Queue)应用场景分析
消息队列中间件是分布式系统中的重要组件,主要解决异步消息,应用解耦,流量削锋等问题,从而实现高性能,高可用,可伸缩和最终一致性的架构
使用较多的消息队列有ActiveMQ,RabbitMQ,Kafka,MetaMQ等
异步处理
场景说明:用户注册后,需要发送注册邮件和注册短信。传统的做法如下:
将注册信息写入数据库成功后,发送注册邮件,再发送注册短信。以上三个任务全部完成后,返回给客户端
引入消息队列,异步处理。改造后的架构如下:
应用解耦
场景说明:用户下单后,订单系统需要通知库存系统。传统的做法是,订单系统调用库存系统的接口。如下图
传统模式的缺点:
假如库存系统无法访问,则订单减库存将失败,从而导致下单失败
订单系统与库存系统耦合
引入消息队列后的方案,如下图:
订单系统:用户下单后,订单系统完成持久化处理,将消息写入消息队列,返回用户,下单成功
库存系统:订阅下单的消息,获取下单信息,库存系统根据下单信息,进行库存操作
假如:在下单时库存系统不能正常使用。也不影响正常下单,因为下单后,订单系统写入消息队列就不再关心其他的后续操作了。实现订单系统与库存系统的应用解耦
流量削锋
流量削锋也是消息队列中的常用场景,一般在秒杀或团抢活动中广泛使用
应用场景:秒杀活动,一般会因为流量过大,导致流量暴增,应用挂掉。为解决这个问题,一般需要在应用前端加入消息队列。
可以控制活动的人数
可以缓解短时间内高流量压垮应用
用户的请求,服务器接收后,首先写入消息队列。假如消息队列长度超过最大数量,则直接抛弃用户请求或跳转到错误页面
秒杀业务根据消息队列中的请求信息,再做后续处理
JMS消息模型
P2P(Point to Point)点对点模式
P2P模式包含三个角色:消息队列(Queue),发送者(Sender),接收者(Receiver)。
每个消息都被发送到一个特定的队列,接收者从队列中获取消息。队列保留着消息,直到他们被消费或超时。
P2P的特点:
每个消息只有一个消费者(Consumer)(即一旦被消费,消息就不再在消息队列中)
发送者和接收者之间在时间上没有依赖性,也就是说当发送者发送了消息之后,不管接收者有没有正在运行,它不会影响到消息被发送到队列
接收者在成功接收消息之后需向队列应答成功
如果希望发送的每个消息都会被成功处理的话,那么需要P2P模式
Publish/Subscribe(Pub/Sub) 发布订阅模式
Pub/Sub模式包含三个角色:主题(Topic),发布者(Publisher),订阅者(Subscriber)。
多个发布者将消息发送到Topic,系统将这些消息传递给多个订阅者。
Pub/Sub的特点
每个消息可以有多个消费者
发布者和订阅者之间有时间上的依赖性。针对某个主题(Topic)的订阅者,它必须创建一个订阅之后,才能消费发布者的消息
为了消费消息,订阅者必须保持运行的状态
为了缓和这样严格的时间相关性,JMS允许订阅者创建一个可持久化的订阅。这样,即使订阅者没有被激活(运行),它也能接收到发布者的消息。
如果希望发送的消息可以被多个消费者处理的话,那么可以采用Pub/Sub模型。
ActiveMQ Topic消息失败重发
JMS消息确认机制
在Session接口中定义的几个常量:
AUTO_ACKNOWLEDGE = 1 自动确认
CLIENT_ACKNOWLEDGE = 2 客户端手动确认
DUPS_OK_ACKNOWLEDGE = 3 自动批量确认
SESSION_TRANSACTED = 0 事务提交并确认
代码实现
消息消费端在创建Session对象时需要指定应答模式为客户端手动应答,当消费者获取到消息并成功处理后需要调用message.acknowledge()方法进行应答,通知Broker消费成功。
如果处理过程中出现异常,需要调用session.recover()通知Broker重复消息,默认最多重复6次
ActiveMQ Topic消息持久化订阅
持久化到文件(默认)
第一步:在${activemq.base}/conf/activemq.xml文件中配置持久化适配器
<persistenceAdapter> <kahaDB directory="${activemq.base}/data/kahadb"/> </persistenceAdapter> |
第二步:在Java代码中发送消息时
ConnectionFactory connectionFactory = new ActiveMQConnectionFactory("tcp://localhost:61616"); Connection connection = connectionFactory.createConnection(); connection.start(); Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE); Topic topic = session.createTopic("myTopic"); MessageProducer producer = session.createProducer(topic); TextMessage textMessage = session.createTextMessage("hello world!"); producer.send(textMessage, DeliveryMode.PERSISTENT, 1, 1000*60*60*24); producer.close(); session.close(); connection.close(); |
第三步:消息订阅者方创建消费者对象时
connection.setClientID("client-1"); TopicSubscriber consumer = session.createDurableSubscriber(topic, "client1-sub"); |
持久化到数据库(mysql)
第一步:将MySQL的数据库驱动复制到activeMQ的lib目录下
第二步:在${activemq.base}/conf/activemq.xml文件中配置持久化适配器
<persistenceAdapter> <jdbcPersistenceAdapter dataDirectory="${activemq.base}/data" dataSource="#derby-ds"/> </persistenceAdapter> |
第三步:在${activemq.base}/conf/activemq.xml文件中配置数据源
<bean id="derby-ds" class="org.apache.commons.dbcp2.BasicDataSource" destroy-method="close"> <property name="driverClassName" value="com.mysql.jdbc.Driver"/> <property name="url" value="jdbc:mysql://localhost/activemq?relaxAutoCommit=true"/> <property name="username" value="root"/> <property name="password" value="root"/> <property name="poolPreparedStatements" value="true"/> </bean> |
测试持久化效果
第一步:启动MQ服务
第二步:启动消费端,需要消费者进行持久化订阅,进行注册
TopicSubscriber consumer = session.createDurableSubscriber(topic, "client1-sub");
第三步:停止消费者
第四步:生产者发送消息到Topic
第五步:启动消费端,能够获得到消息说明持久化成功
zookeeper+activemq集群实现高可用
使用ZooKeeper实现的Master-Slave实现方式,是对ActiveMQ进行高可用的一种有效的解决方案。高可用的原理:使用ZooKeeper(集群)注册所有的ActiveMQ Broker。只有其中的一个Broker可以对外提供服务(也就是Master节点),其他的Broker处于待机状态,被视为Slave。如果Master因故障而不能提供服务,则利用ZooKeeper的内部选举机制从Slave中选举出一个Broker充当Master节点,继续对外提供服务。通过zookeeper+activeMQ实现的集群,可以有效的排除单点故障引起的服务中断。
环境准备
1、准备三台虚拟机,IP分别为:
192.168.112.128
192.168.112.129
192.168.112.130
2、提供JDK安装包(安装过程略)
3、提供zookeeper和activeMQ安装包
安装zookeeper
第一步:将下载的zookeeper安装包分别上传到三台虚拟机,分别进行下面操作
第二步:解压安装包
tar –zxvf zookeeper-3.4.10.tar.gz –C /usr/local
第三步:将/usr/local目录下的zookeeper-3.4.10改名
mv zookeeper-3.4.10 zookeeper
第四步:在zookeeper目录下创建两个目录data和logs,分别存放数据和日志
cd /usr/local/zookeeper/
mkdir data
mkdir logs
第五步:将zookeeper/conf目录下的zoo_sample.cfg文件名改为zoo.cfg
cd /usr/local/zookeeper/conf/
mv zoo_sample.cfg zoo.cfg
安装activeMQ
第一步:将下载的activemq安装包分别上传到三台虚拟机,分别进行下面操作
第二步:解压安装包
tar –zxvf apache-activemq-5.14.0-bin.tar.gz –C /usr/local
第三步:将/usr/local目录下的apache-activemq-5.14.0改名
mv apache-activemq-5.14.0 activemq
集群配置
第一步:在/usr/local/zookeeper/data下创建myid文件,文件内容为1。同理,其他虚拟机中也创建myid文件,内容分别为2和3
第二步:修改/usr/local/zookeeper/conf/zoo.cfg文件,加入以下内容
dataDir=/usr/local/zookeeper/data/ server.1=192.168.112.128:2888:3888 |
第三步:分别启动三台zookeeper(需要关闭防火墙)
service iptables stop
/usr/local/zookeeper/bin/zkServer.sh start
第四步:修改/usr/local/activemq/conf目录下的activemq.xml
vim /usr/local/activemq/conf/activemq.xml
修改brokerName=”activemq-cluster”
将文件中持久化适配器改为
<persistenceAdapter> <replicatedLevelDB directory="${activemq.data}/leveldb" replicas="3" bind="tcp://0.0.0.0:0" zkAddress="192.168.112.128:2181,192.168.112.129:2181,192.168.112.130:2181" hostname="192.168.112.128" zkPath="/activemq/leveldb-stores"/> </persistenceAdapter> |
注意:其他虚拟机中修改时hostname需要改为对应的ip
第五步:启动mq服务
./activemq start