使用ActiveMQ实现消息订阅

449 阅读7分钟

小知识,大挑战!本文正在参与“程序员必备小知识”创作活动

一、发布订阅

1,JMS

JMS,(Java Message Service),通过面向消息中间件(MOM:Message Oriented Middleware)的方式很好的解决了上面的问题,大致的过程是这样的:发送者把消息发送给消息服务器,消息服务器将消息存放在若干队列/主题中,在合适的时候,消息服务器会将消息转发给接收者,在这个过程中,发送和接收是异步的,也就是无需等待,而且发送者和接收者的生命周期也没有必然关系;在pub/sub模式下,也可以完成一对多的通信,即让一个消息有多个接收者。

角色划分

  1. 提供者:实现JMS规范的消息中间件服务器(存放消息容器)。
  2. 客户端:发送或接收消息的应用程序。
  3. 生产者/发布者:创建并发送消息的客户端(向消息容器存放消息)。
  4. 消费者/订阅者:接收并处理消息的客户端。
  5. 消息:应用程序之间传递的数据内容。
  6. 消息模式:在客户端之间传递消息的方式,JMS中定义了主题和队列两种模式,点对点与发布订阅模式。

2,什么是消息模型

  • Point-to-Point(P2P)---点对点(生产者发送一条消息到queue,只有一个消费者能收到)
  • Publish/Subscribe(Pub/Sub)---发布订阅(发布者发送到topic的消息,只有订阅者才会收到消息)

即点对点和发布订阅模型

P2P

特点:

  1. 每个消息只有一个消费者(Consumer)(即一旦被消费,消息就不会在消息队列中)。
  2. 发送者和接收者之间在时间上没有依赖性,也就是当发送者发送了消息之后,不管接收者有没有正在运行,它不会影响到消息被发送到队列。
  3. 接收者在成功接收消息之后需向队列应答成功,如果希望发送的每个消息都应该被成功处理的话,那么需要P2P模式。

应用场景:

A用户和B用户发送消息

Pub/Sub(发布与订阅)

特点:

  1. 发布者和订阅者之间有时间上的依赖性,针对某个主题(Topic)的订阅者,它必须创建一个订阅者之后,才能消费发布者的消息,而且为了消费消息,订阅者必须保持运行的状态。
  2. 为了缓和这样严格的时间相关性,JMS允许订阅者创建一个可持久化的订阅,这样,即使订阅者没有被激活(运行),它也能接收到发布者的消息,如果希望发送的消息可以不被做任何处理或者被一个消息者处理、或者可以被多个消费者处理的话,那么可以采用Pub/Sub模型。
  3. 消息的消费,在JMS中,消息的产生和消息是异步的,对于消费来说,JMS的消息者可以通过两种方式来消费消息。同步:订阅者或接收者调用recevive方法来接收消息,receive方法在能够接收到消息之前(或超时之前)将一直阻塞。异步:订阅者或接收者可以注册为一个消息监听者,当消息到达知乎,系统自动调用监听者的onMessage方法。

发布订阅与点对点通讯方式区别:

点对点:只能保证一个消费者进行消费。

发布订阅:只要集群服务订阅该主题都会收到消息。

二、ActiveMQ

1,ActiveMQ的消息形式

应用场景:

  • 异步处理
  • 实现系统之间的解耦
  • 流量削锋
  • 消息通讯

异步处理应用场景

异步处理场景说明:用户注册后,需要发注册邮件和注册短信,传统的做法有两种

1,串行的方式,2,并行的方式

a、串行方式:将注册信息写入数据库成功后,发送注册邮件,再发送注册短信,以上三个任务全部完成后,返回给客户端。

b、并行方式:将注册信息写入数据库成功后,发送注册邮件的同时,发送注册短信,以上三个任务完成后,返回给客户端。

与串行的差别是,并行的方式可以提高处理的时间。

而当使用消息队列,将发送注册邮件,和发送注册短信写入消息队列时所用的时间很短,基本可以忽略,此方法比串行提高了3倍,比并行提高了两倍。

解耦应用场景

场景说明:用户下单后,订单系统需要通知库存系统,传统的做法是,订单系统调用库存系统的接口

传统模式的缺点:假如库存系统无法访问,则订单减库存将失败,从而导致订单失败,订单系统与库存系统耦合,耗时时间采用MQ推送,不建议采用同步方法。

应用消息队列后的解决方案:

订单系统:用户下单后,订单系统完成持久化处理,将消息写入消息队列,返回用户订单,下单成功。

库存系统:订阅下单的消息,采用拉/推的方式,获取下单信息,库存系统根据下单信息,进行库存操作,假如:在下单时库存系统不能正常使用,也不影响正常下单,因为下单后,订单系统写入消息队列就不再关心其他的后续操作了,实现订单系统与库存系统的应用解耦。

流量削锋应用解耦

流量削锋也是消息队列中的常用场景,一般在秒杀或团抢活动中使用广泛,应用场景:秒杀活动,一般会因为流量过大,导致流量暴增,应用挂掉。

为解决这个问题,一般需要在应用前端加入消息队列。

a、可以控制活动的人数

b、可以缓解段时间内高流量压垮应用

服务器接收到用户的请求后,首先写入消息队列,假如消息队列长度超过最大数量,则直接抛弃用户请求直接跳转到错误页面。

JMS定义了五种不同的消息正文格式,以及调用的消息类型,允许发送并接收以一些不同形式的数据,提供现有消息格式的一些级别的兼容性。

  • StreamMessage --Java原始值的数据流
  • MapMessage -- 一套名称-值对
  • TextMessage --一个字符串对象(常用)
  • ObjectMessage--一个序列化的java对象
  • BytesMessage--一个字节的数据流

三、Linux下安装ActiveMQ

下载安装

下载地址

activemq.apache.org/activemq-50…

放进/usr/local/activemq中解压

tar -zxvf apache-activemq-5.15.8-bin.tar.gz

启动

./bin/activemq start

停止

./bin/activemq stop

操作

1,创建一个systemd服务文件vim /usr/lib/systemd/system/activemq.service

2,放入内容

[Unit] 
Description=ActiveMQ service 
After=network.target 

[Service] 
Type=forking 
ExecStart=/usr/local/activemq/apache-activemq-5.15.8/bin/activemq start 
ExecStop=/usr/local/activemq/apache-activemq-5.15.8/bin/activemq stop 
User=root 
Group=root 
Restart=always 
RestartSec=9 
StandardOutput=syslog 
StandardError=syslog 
SyslogIdentifier=activemq 

[Install] 
WantedBy=multi-user.target

3-4可以省略

3,找到java命令所在的目录 whereis java

4,设置activemq配置文件/var/activemq/bin/env中的JAVA_HOME

5,通过systemctl管理activemq启停

  • 启动acivemq服务:systemctl start activemq
  • 查看服务状态:systemctl status activemq
  • 创建软件链接:ln -s /usr/lib/systemd/system/acivemq.service /etc/systemd/system/multiuser.target.wants/activemq.service
  • 开机自启:systemctl enable activemq
  • 检测是否开启成功:systemctl list-unit-files | grep activemq

6,防火墙配置,web管理端口默认为8161(admin、admin),通讯端口默认为61616

  • 添加并重启放火墙
firewall-cmd --zone=public --add-port=8161/tcp --permanent 
firewall-cmd --zone=public --add-port=61616/tcp --permanent 
systemctl restart firewalld.service
  • 或者直接关闭防火墙 systemctl stop firewalld.service

7,修改web管理系统的部分配置,配置文件在/usr/local/activemq/apache-activemq-5.15.8/conf

  • 端口修改
<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>
  • 关闭登录
<bean id="securityConstraint" class="org.eclipse.jetty.util.security.Constraint">
    <property name="name" value="BASIC" /> 
    <property name="roles" value="user,admin" /> 
    <!-- 改为false即可关闭登陆 --> 
    <property name="authenticate" value="true" />
</bean>
  • 其他配置/usr/local/activemq/apache-activemq-5.15.8/conf/jetty-realm.properties
## ---------------------------------------------------------------------------
# 在此即可维护账号密码,格式: 
# 用户名:密码,角色 
# Defines users that can access the web (console, demo, etc.) 
# username: password [,rolename ...] 
admin: admin, admin 
user: 123, user