先说说JMS
消息队列的JAVAEE规范JMS 。JMS(Java Message Service,java消息服务)API是一个消息服务的标准/规范,允许应用程序组件基于JavaEE平台创建、发送、接收和读取消息。它使分布式通信耦合度更低,消息服务更加可靠以及异步性。
Provider/MessageProvider:生产者
Consumer/MessageConsumer:消费者
PTP:Point To Point,点对点通信消息模型
Pub/Sub:Publish/Subscribe,发布订阅消息模型
Queue:队列,目标类型之一,和PTP结合
Topic:主题,目标类型之一,和Pub/Sub结合
ConnectionFactory:连接工厂,JMS用它创建连接
Connnection:JMS Client到JMS Provider的连接
Destination:消息目的地,由Session创建
Session:会话,由Connection创建,实质上就是发送、接受消息的一个线程,因此生产者、消费者都是Session创建的
ActiveMQ说明
MQ是消息中间件,是一种在分布式系统中应用程序借以传递消息的媒介,常用的有ActiveMQ,RabbitMQ,kafka。ActiveMQ是Apache下的开源项目,完全支持JMS1.1和J2EE1.4规范的JMS Provider实现。
ActiveMQ是一种开源的基于JMS(Java Message Servie)规范的一种消息中间件的实现,ActiveMQ的设计目标是提供标准的,面向消息的,能够跨越多语言和多系统的应用集成消息通信中间件。
ActiveMQ常被应用与系统业务的解耦,异步消息的推送,增加系统并发量,提高用户体验。例如以我在工作中的使用,在比较耗时且异步的远程开锁操作时
ActiveMQ有两种消息传递类型
- 点对点传输(PTP),即一个生产者对应一个消费者,生产者向broke推送数据,数据存储在broke的一个队列中,当消费者接受该条队列里的数据。
- 一个消息只能被一个消费者消费,不可重复消费
- 多个消费者均分消息(负载均衡策略)
- 发送者和接收者之间在时间上没有依赖性,也就是说当发送者发送了消息之后,不管接收者有没有正在运行,它不会影响到消息被发送到队列
- 当消费者在消费某个消息的时候,mq一定要等到它的成功回执,才会分发下一个消息
- queue入队之后,无论等待多久,消息都会一直等待消费者来处理
- 如果希望发送的每个消息都会被成功处理的话,那么需要P2P模式。
- 基于发布/订阅模式的传输(Topic),即根据订阅话题来接收相应数据,一个生产者可向多个消费者推送数据。
- 发布者和订阅者之间有时间上的依赖性。针对某个主题(Topic)的订阅者,它必须创建一个订阅者之后,才能消费发布者的消息
- 为了消费消息,订阅者必须保持运行的状态
ActiveMQ应用场景
- 异步处理:用户注册后储存数据库,发送短信,发送邮件。使用ActiveMQ提高速度,存入数据库后信息写入消息队列后直接返回,异步发送短息发送邮件。
- 应用程序解耦:用户下单后,订单系统需要通知库存系统。传统的做法是,订单系统调用库存系统的接口,假如库存系统无法访问,则订单减库存将失败,从而导致订单失败,订单系统与库存系统耦合。使用ActiveMQ,用户下单后,订单系统完成持久化处理,将消息写入消息队列,返回用户订单下单成功。库存系统订阅下单的消息,采用pub/sub(发布/订阅)的方式,获取下单信息,库存系统根据下单信息,进行库存操作。假如:在下单时库存系统不能正常使用。也不影响正常下单,因为下单后,订单系统写入消息队列就不再关心其他的后续操作了。实现订单系统与库存系统的应用解耦
- 流量削锋:流量削锋也是消息队列中的常用场景,一般在秒杀或团抢活动中使用广泛。秒杀活动,一般会因为流量过大,导致流量暴增,应用挂掉。为解决这个问题,一般需要在应用前端加入消息队列。1.可以控制活动的人数。2.可以缓解短时间内高流量压垮应用。
用户的请求,服务器接收后,首先写入消息队列。假如消息队列长度超过最大数量,则直接抛弃用户请求或跳转到错误页面,秒杀业务根据消息队列中的请求信息,再做后续处理。 - 日志处理:日志采集客户端将日志写入消息队列,日志分析客户端监听消息进行处理
- 消息通讯:消息通讯是指,消息队列一般都内置了高效的通信机制,因此也可以用在纯的消息通讯。比如实现点对点消息队列,或者聊天室等。
ActiveMQ整合Springboot
-
下载ActiveMQ
http://activemq.apache.org/download-archives.html
启动后登陆地址: http://localhost:8161/admin(用户名和密码默认为admin) -
maven导包
<spring-boot.version>2.2.7.RELEASE</spring-boot.version>
<pooled-jms.version>1.0.4</pooled-jms.version>
使用springboot2.1+时候,maven配置依赖是:
<!--activeMQ starter-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-activemq</artifactId>
</dependency>
<!--mq连接池-->
<dependency>
<groupId>org.messaginghub</groupId>
<artifactId>pooled-jms</artifactId>
</dependency>
使用springboot2.0+及以下版本时候,maven配置依赖是:
<dependency>
<groupId>org.apache.activemq</groupId>
<artifactId>activemq-pool</artifactId>
</dependency>
- 配置application.yml
spring:
activemq:
broker-url: tcp://localhost:61616
#true 表示使用内置的MQ,false则连接服务器
in-memory: false
pool:
#true表示使用连接池;false时,每发送一条数据创建一个连接
enabled: true
#连接池最大连接数
max-connections: 10
#空闲的连接过期时间,默认为30秒
idle-timeout: 30000
- 配置Queue
@EnableJms必须有,加到启动类上也可以
@EnableJms
@Configuration
public class MqConfiguration {
@Bean
public Queue getQueue(){
return new ActiveMQQueue("ActiveMQQueue");
}
@Bean
public Topic topic() {
return new ActiveMQTopic("ActiveMQTopic");
}
}
- 提供消息
@RestController
@RequestMapping("/email")
public class EmailController {
@Autowired
private Queue emailQueue;
@Autowired
private JmsMessagingTemplate jmsMessageTemplate;
@PostMapping("/sendMessage/{userid}")
public ResponseEntity<String> sendMessage(@PathVariable("userid") String userid){
MapMessage mapMessage = new ActiveMQMapMessage();
try {
mapMessage.setString("userid", userid);
} catch (JMSException e) {
e.printStackTrace();
}
//方法一:添加消息到消息队列
jmsMessageTemplate.convertAndSend(emailQueue, mapMessage);
//方法二:这种方式不需要手动创建queue,系统会自行创建名为test的队列
//jmsMessageTemplate.convertAndSend("test", name);
return new ResponseEntity<String>(HttpStatus.OK);
}
}
- 消费消息 在customer端
@Service
public class EmailService {
@Autowired
private JmsMessagingTemplate jmsMessagingTemplate;
// 使用JmsListener配置消费者监听的队列,其中name是接收到的消息
@JmsListener(destination = "ActiveMQQueue")
// SendTo 会将此方法返回的数据, 写入到 OutQueue 中去.
@SendTo("SQueue")
public String handleMessage(MapMessage message) {
MapMessage mapMessage = (MapMessage) message;
String name = mapMessage.getString("userid");
System.out.println("成功接受Name" + name);
return "成功接受Name" + name;
}
}
实际项目中的使用
合同管理系统提交合同后将信息保存到DB,然后向MQ发送json,托管人平台监听到MQ消息后将产品预发行信息储存起来,邮件系统监听MQ后发送邮件。