消息队列中间件是分布式系统中重要的组件,主要解决应用耦合,异步消息,流量削锋等问题。实现高性能,高可用,可伸缩和最终一致性架构。是大型分布式系统不可缺少的中间件。消息形式支持点对点和订阅-发布
由于业务、结构和技术是不断变化的,因此为其服务的软件系统就涉及到了整合。在整合、添加服务或扩展可用服务之后,就要考虑服务间消息传递的成本。所以需要增加一些组件去提供一个允许它们进行通信(不考虑它们之间的差异)的层。该层被称作中间件。
将信息以消息的形式,从一个应用程序传输到另一个或者多个应用程序。
消息异步接受:类似于手机短信的行为,消息发送者不需要等待消息接受者的响应,减少软件多系统集成的耦合度;
消息可靠接受:确保消息在消息中间件可靠保存,只要接受方接受到消息后才可删除,多个消息也可以组成原子事务
JMS即Java消息服务(Java Message
Service)应用程序接口,是一个Java平台中关于面向消息中间件(MOM)的API,用于在两个应用程序之间,或分布式系统中发送消息,进行异步通信。Java消息服务是一个与具体平台无关的API,绝大多数MOM提供商都对JMS提供支持。
MQ全称为Message Queue,消息队列(MQ)是正确而又完整的 JMS 实现,消息队列(MQ)是一种应用程序对应用程序的通信方法。应用程序通过写和检索出入列队的针对应用程序的数据(消息)来通信,而无需专用连接来链接它们。消息传递指的是程序之间通过在消息中发送数据进行通信,而不是通过直接调用彼此来通信,直接调用通常是用于诸如远程过程调用的技术。
小明去书店买三本书,然后去吃饭
同步处理的情况下:小明要一本一本的买
消息队列:小明告诉店员,他要哪三本书,然后等他吃完饭在回来拿就好
则店员就是消息队列,只需要告诉他你要做的事情,放进队列,然后处理就好
解耦性场景:
看到公司游戏广告的用户从点击广告页面推送给我方,除了相对应的福利外,首先判断是否有注册过或者玩过本公司游戏,
没有给予注册,但是点击的消息也要传到对应的计算服务里面。
同步:如果点击信息的服务挂了,影响判断是否注册,过度依赖其余系统
消息队列:写入消息队列,不影响主体业务
秒杀:
为了更加直观的展示MQ的应用场景,这里我们就用一个常见的电商系统中的几个业务,来具体说明下MQ在实际开发中应用场景。
我们的实际场景大概是一个基于微服务架构的电商系统,分为用户微服务、商品微服务、订单微服务、促销微服务等。基于微服务模式开发的系统,MQ的使用场景更多,下面我们逐一说明:
P2P (点对点)消息域使用 queue 作为 Destination,消息可以被同步或异步的发送和接收,每个消息只会给一个 Consumer 传送一次。
Pub/Sub(发布/订阅,Publish/Subscribe)消息域使用 topic 作为 Destination,发布者向 topic 发送消息,订阅者注册接收来自 topic 的消息。发送到 topic 的任何消息都将自动传递给所有订阅者。接收方式(同步和异步)与 P2P 域相同。
1.Destination
目的地,JMS Provider(消息中间件)维护,用于对Message进行管理的对象。
MessageProducer需要指定Destination才能发送消息,MessageConsumer需要指定Destination才能接收消息。
2.Producer
消息生成者(客户端,生成消息),负责发送Message到目的地。应用接口为MessageProducer
3.Consumer【Receiver】
消息消费者(处理消息),负责从目的地中消费【处理|监听|订阅】Message。应用接口为MessageConsumer
4.Message
消息(Message),消息封装一次通信的内容。常见类型有:StreamMessage、BytesMessage、TextMessage、ObjectMessage、MapMessage。
5.ConnectionFactory
链接工厂, 用于创建链接的工厂类型。 注意,不能和 JDBC 中的 ConnectionFactory 混
淆。
6.Connection
链接. 用于建立访问 ActiveMQ 连接的类型, 由链接工厂创建. 注意,不能和 JDBC 中的
Connection 混淆。
7.Session
会话, 一次持久有效有状态的访问. 由链接创建. 是具体操作消息的基础支撑。
8.Queue&Topic
Queue是队列目的地,Topic是主题目的地。都是Destination的子接口。
Queue特点:队列中的消息,默认只能有唯一的一个消费者处理。
Topic特点:主题中的消息,会发送给所有的消费者同时处理。只有在消息可以重复处理的业务场景中可使用。
9.PTP
Point to Point。点对点消息模型,就是基于Queue实现的消息处理方式。
10.PUB&SUB
Publish&Subscribe。消息的发布/订阅模型。是基于Topic实现的消息处理方式。
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0modelVersion>
<parent>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-parentartifactId>
<version>2.1.4.RELEASEversion>
<relativePath/>
parent>
<groupId>com.alipaygroupId>
<artifactId>demoartifactId>
<version>0.0.1-SNAPSHOTversion>
<name>demoname>
<description>Demo project for Spring Bootdescription>
<properties>
<java.version>1.8java.version>
properties>
<build>
<plugins>
<plugin>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-maven-pluginartifactId>
plugin>
<plugin>
<groupId>org.mybatis.generatorgroupId>
<artifactId>mybatis-generator-maven-pluginartifactId>
<version>1.3.2version>
<configuration>
<configurationFile>src/main/resources/mybatis-generator-config.xmlconfigurationFile>
<verbose>trueverbose>
<overwrite>trueoverwrite>
configuration>
<executions>
<execution>
<id>Generate MyBatis Artifactsid>
<goals>
<goal>generategoal>
goals>
execution>
executions>
<dependencies>
<dependency>
<groupId>org.mybatis.generatorgroupId>
<artifactId>mybatis-generator-coreartifactId>
<version>1.3.2version>
dependency>
dependencies>
plugin>
plugins>
build>
<dependencies>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-activemqartifactId>
dependency>
<dependency>
<groupId>org.apache.activemqgroupId>
<artifactId>activemq-poolartifactId>
<version>5.15.0version>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-jmsartifactId>
dependency>
<dependency>
<groupId>org.apache.activemqgroupId>
<artifactId>activemq-clientartifactId>
dependency>
dependencies>
project>
#日志
logging:
level:
com:
alipay:
demo:
mapper: debug
quartz: debug
# file: quartz-service.log
#端口
server:
port: 8090
tomcat:
uri-encoding: utf-8
spring:
activemq:
user: admin
password: admin
#true 表示使用内置的MQ,false则连接服务器
in-memory: true
# 给java用的tcp端口是61616
broker-url: tcp://127.0.0.1:61616
#需要加入配置文件,支持发布订阅模型,默认只支持点对点
jms:
pub-sub-domain: true
pool:
#true表示使用连接池;false时,每发送一条数据创建一个连接
enabled: true
#最大连接数
max-connections: 50
packages:
trust-all: true
BaseProducer.java
package com.hiqiblog.queue.producer;
import com.hiqiblog.queue.Producer;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jms.core.JmsMessagingTemplate;
public abstract class BaseProducer implements Producer {
@Autowired
private JmsMessagingTemplate jmsTemplate;
@Override
public void enqueue(String message) {
this.enqueue(this.getQueueName(), message);
}
protected void enqueue(String destinationName, String message) {
jmsTemplate.convertAndSend(destinationName, message);
}
protected abstract String getQueueName();
}
Producer.java
package com.hiqiblog.queue;
/**
* @Author helloc
* @Date 2019/7/26 9:12
* @Version 1.0
*/
public interface Producer {
void enqueue(String message);
}
AppQueueProducer.java
/**
* @Author helloc
* @Date 2019/7/26 9:12
* @Version 1.0
*/
@Component
public class AppQueueProducer extends BaseProducer {
public final static String APP_QUEUE = "helloq";
@Override
protected String getQueueName() {
return APP_QUEUE;
}
}
controller
package com.hiqiblog.controller;
import com.hiqiblog.ViewModel.ResponseMessage;
import com.hiqiblog.queue.producer.BaseProducer;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jms.core.JmsTemplate;
import org.springframework.jms.core.MessageCreator;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.jms.*;
/** 功能描述:模拟微信支付回调 @Author helloc @Date 2019/7/25 14:00 @Version 1.0 */
@RestController
@RequestMapping("/api/v1")
public class OrderController {
@Autowired private BaseProducer baseProducer;
/**
* 功能描述:微信支付回调接口
*
* @param msg 支付信息
* @return
*/
@GetMapping("order")
public Object order(String msg) {
baseProducer.enqueue(msg);
ResponseMessage responseMessage = new ResponseMessage();
responseMessage.setCode("1");
responseMessage.setMsg("Success");
return responseMessage;
}
@GetMapping("common")
public Object common(String msg) {
baseProducer.enqueue(msg);
ResponseMessage responseMessage = new ResponseMessage();
responseMessage.setCode("1");
responseMessage.setMsg("Success");
return responseMessage;
}
}
package com.hiqiblog.queue.consumer;
import com.hiqiblog.queue.Consumer;
import org.springframework.jms.annotation.JmsListener;
import org.springframework.stereotype.Component;
/**
* @Author helloc
* @Date 2019/7/26 11:40
* @Version 1.0
*/
@Component
public class AppQueueConsumer {
@Override
@JmsListener(destination = "helloq")
public void dequeue(String message) {
System.out.println("报文为:"+message);
}
}
1)一个消息只能被一个服务接收
2)消息一旦被消费,就会消失
3)如果没有被消费,就会一直等待,直到被消费
4)多个服务监听同一个消费空间,先到先得
1)一个消息可以被多个服务接收
2)订阅一个主题的消费者,只能消费自它订阅之后发布的消息。
3)消费端如果在生产端发送消息之后启动,是接收不到消息的,除非生产端对消息进行了持久化(例如广播,只有当时听到的人能听到信息)
点对点通信,每个消息只有一个消费者,消息保证送达,离线消费者可以在下次上线后收到之前积压的消息
普通订阅:
不区分消费者,当前有几个客户端在线,就发送几条广播给客户端。
持久订阅:
区分消费者,消费者在线则直接发送消息广播给消费者,消费者离线,只要该消费者有topic登记,就会为其保留消息直至其再次连接后一次性推送,消息可以积压。
如何保证消息队列的高可用性?
如何保证消息消费时的幂等性?
RabbitMQ是ack,Kafka是offset
如何保证消息的可靠性传输?