快速了解消息中间件与ActiveMQ基础学习

什么是消息中间?

消息中间件,此处指的是面向消息的中间件。
消息中间件主要作为消息通讯使用,你可以简单理解为两个程序即时通讯的一种技术,是用于两个程序之间交互的一种方式。
在这里插入图片描述
消息队列中间件中的“消息队列”是什么意思?
消息队列,是消息中间件的一种实现方式,就是一个用于存放消息的队列(容器)

消息队列中间件中的“中间件”是什么意思?
百度百科介绍:中间件(Middleware)是处于操作系统和应用程序之间的软件。
在这里插入图片描述
中间件又能细分成如下:

  1. 基于远程过程调用 (Remote Procedure Call, RPC)的中间件
  2. 基于对象请求代理 (Object Request Broker, ORB) 的中间件
  3. 面向消息的中间件或基于 MOM 的中间件 (本文就是这种)。

消息中间件的主要作用

消息(队列)中间件是分布式系统中重要的组件,主要解决应用耦合异步消息流量削锋等问题。实现高性能、高可用、可伸缩和最终一致性架构。

消息中间件的产生的背景

由于客户端在于服务端进行通讯的时候,客户端调用某些(API)业务后,必须等待服务端处理完成后返回结果才能继续执行。这个过程中,代码是同步执行的。

JMS(Java Message Service)

百度百科介绍:JMSJava消息服务(Java Message Service)应用程序接口,是一个Java平台中关于面向消息中间件(MOM)的API,用于在两个应用程序之间,或分布式系统中发送消息,进行异步通信。Java消息服务是一个与具体平台无关的API,绝大多数MOM提供商都对JMS提供支持。

你可以简单理解为JMSjava的消息服务,JMS的客户端之间可以通过JMS服务进行异步的消息传输。

应用场景

系统解耦、异步处理、流量削峰

举例:用户购物下单,下单需要生成订单并且减少商品的库存数量。
传统是同步方式处理,非常明显的缺点就是:商品订单的逻辑和商品库存的逻辑都是耦合在一起处理的。
在这里插入图片描述
使用消息中间件后:
在这里插入图片描述
商品订单系统仅处理自己的业务逻辑,然后通过消息中间件,异步的方式去处理商品库存系统的逻辑。

举例(短信注册新用户):新用户注册就送积分/礼包/限时VIP
在这里插入图片描述
使用消息中间件后:
在这里插入图片描述
还有流量削峰等等应用场景等场景就不画图了(毕竟都是瞎画,没正经学过,凑合看)

消息模型介绍

  • Point to Point (P2P) 点对点
    在这里插入图片描述

    P2P 有以下几个概念:
    1.消息队列(Queue)存放消息的队列(容器)
    2.发送者(Sender) 消息的生产者
    3.接收者(Receiver) 消息的消费者

    每个消息都会被发送到一个特定的队列,接收者 (Receiver) 从队列中获取(消费)消息,直到队列的消息被消费或者超时。

    P2P 特点:
    1.每个消息只能被一个消费者消费(被消费的消息就不会保留在消息队列)
    2.发送者和接收者,在时间上没有依赖性,即:发送者发送了消息之后,不管接收者有没有在线,都没有影响。(发送者:你接收者宕机了都不管我事,我只管发)
    3.接收者成功接收消息后需要向消息队列应答(告诉消息队列,我消费了消息)

P2P 模式:保证每个消息都被成功消费(如果你是这么希望的,请使用它)

  • Publish/Subscribe(Pub/Sub) 发布/订阅
    在这里插入图片描述
    Pub/Sub 的几个概念:
    1.主题(Topic
    2.发布者(Publisher
    3.订阅者(Subscriber

客户端将消息发送到主题,多个发布者将消息发送到Topic,系统将这些消息传递给多个订阅者。

Pub/Sub的特点:
1.每个消息可以有多个消费者

发布者和订阅者之间有时间上的依赖性。针对某个主题(Topic)的订阅者,它必须创建一个订阅者之后,才能消费发布者的消息,而且为了消费消息,订阅者必须保持运行的状态。
为了缓和这样严格的时间相关性,JMS允许订阅者创建一个可持久化的订阅。这样,即使订阅者没有被激活(运行),它也能接收到发布者的消息。
如果你希望发送的消息可以不被做任何处理、或者被一个消息者处理、或者可以被多个消费者处理的话,那么可以采用Pub/Sub模型

2.消息的消费
JMS中,消息的产生和消息是异步的。对于消费来说,JMS的消息者可以通过两种方式来消费消息。

  • 同步
    订阅者或接收者调用receive方法来接收消息,receive方法在能够接收到消息之前(或超时之前)将一直阻塞 ,直到消息的到来。
  • 异步
    订阅者或接收者可以注册为一个消息监听器。当消息到达之后,系统自动调用监听器的onMessage方法。

常见的消息队列中间件

RabbitMQ、Redis(Pub/Sub)、ZeroMQ、RocketMQ、ActiveMQ、Jafka/Kafka 等…

Java中使用ActiveMQ

我是以Docker for Windows (Docker Desktop)方式使用,非常的方便,不会Docker的朋友,推荐你学习Docker哦,对你学习开发的帮助将会很大。

ActiveMQ启动后,浏览器访问 localhost:8161/admin (默认账户为admin密码admin)
在这里插入图片描述
页面和模块就不多进行介绍了,“面向百度学习”就能了解清楚,或者右键“翻译成(中文)”

P2P 模式 Java 代码:

Maven 依赖

<!--只是activemq的依赖,不是springboot整合的依赖,所以要标明版本号,不能省略-->
<dependency>
    <groupId>org.apache.activemq</groupId>
    <artifactId>activemq-core</artifactId>
    <version>5.7.0</version>
</dependency>

消息生产者

public class Producter {
    public static void main(String[] args) throws JMSException {
        ConnectionFactory connectionFactory = new ActiveMQConnectionFactory(ActiveMQConnection.DEFAULT_USER,
                ActiveMQConnection.DEFAULT_PASSWORD, "tcp://127.0.0.1:61616");
        Connection connection = connectionFactory.createConnection();
        connection.start();
        // false 表示不开启事务 、Session.CLIENT_ACKNOWLEDGE 表示手动应答模式 / AUTO_ACKNOWLEDGE 自动应答
        Session session = connection.createSession(Boolean.FALSE, Session.CLIENT_ACKNOWLEDGE);
        // 参数值darian-queue是Query的名字
        Queue queue = session.createQueue("darian-queue");
        // 消息生产者
        MessageProducer producer = session.createProducer(queue);
        // 设置不持久化
        producer.setDeliveryMode(DeliveryMode.NON_PERSISTENT);
        for (int i = 1; i <= 5; i++) {
            sendMsg(session, producer, i);
            System.out.println("发送消息第 "+i+" 条");
        }
        connection.close();
    }

    public static void sendMsg(Session session, MessageProducer producer, int i) throws JMSException {
        TextMessage message = session.createTextMessage("Hello ActiveMQ!" + i);
        producer.send(message);
    }

}

消息消费者

public class Consumer {

    public static void main(String[] args) throws JMSException {
        ConnectionFactory connectionFactory = new ActiveMQConnectionFactory(ActiveMQConnection.DEFAULT_USER,
                ActiveMQConnection.DEFAULT_PASSWORD, "tcp://127.0.0.1:61616");
        Connection connection = connectionFactory.createConnection();
        connection.start();
        Session session = connection.createSession(Boolean.FALSE, Session.CLIENT_ACKNOWLEDGE);
        // 获取session注意参数值darian-queue是一个服务器的queue,须在在ActiveMq的console配置
        Destination destination = session.createQueue("darian-queue");
        // 消费者
        MessageConsumer consumer = session.createConsumer(destination);
        while (true) {
            TextMessage message = (TextMessage) consumer.receive();
            if (null != message) {
                System.out.println("收到消息:" + message.getText());
                // 手动应答
                message.acknowledge();
            } else {
                break;
            }
        }
        session.close();
        connection.close();
    }

}

Pub/Sub 模式 Java 代码:

发布者代码

public class Publishe {

    private static String BROKER_URL = "tcp://127.0.0.1:61616";
    private static String TOPIC = "darian-topic";

    public static void main(String[] args) throws JMSException {
        start();
    }

    static public void start() throws JMSException {
        System.out.println("生产者启动!");
        ActiveMQConnectionFactory activeMQConnectionFactory = new ActiveMQConnectionFactory(
                ActiveMQConnection.DEFAULT_USER, ActiveMQConnection.DEFAULT_PASSWORD, BROKER_URL);
        Connection connection = activeMQConnectionFactory.createConnection();
        connection.start();
        Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
        MessageProducer producer = session.createProducer(null);
        producer.setDeliveryMode(DeliveryMode.PERSISTENT);
        send(producer, session);
        System.out.println("消息发送成功!");
        connection.close();
    }

    static public void send(MessageProducer producer, Session session) throws JMSException {
        for (int i = 1; i <= 5; i++) {
            System.out.println("消息" + i);
            TextMessage textMessage = session.createTextMessage("消息" + i);
            Destination destination = session.createTopic(TOPIC);
            producer.send(destination, textMessage);
        }
    }

}

订阅者代码

public class Subscribe {

    private static String BROKER_URL = "tcp://127.0.0.1:61616";
    private static String TOPIC = "darian-topic";

    public static void main(String[] args) throws JMSException {
        start();
    }

    static public void start() throws JMSException {
        System.out.println("消费者启动!");
        // 创建ActiveMQConnectionFactory 会话工厂
        ActiveMQConnectionFactory activeMQConnectionFactory = new ActiveMQConnectionFactory(
                ActiveMQConnection.DEFAULT_USER, ActiveMQConnection.DEFAULT_PASSWORD, BROKER_URL);
        Connection connection = activeMQConnectionFactory.createConnection();
        // 启动JMS 连接
        connection.start();
        // 不开启消息事物,消息主要发送消费者,则表示消息已经签收
        Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
        // 创建一个队列
        Topic topic = session.createTopic(TOPIC);
        MessageConsumer consumer = session.createConsumer(topic);
        // consumer.setMessageListener(new MsgListener());
        while (true) {
            TextMessage textMessage = (TextMessage) consumer.receive();
            if (textMessage != null) {
                System.out.println("接受到消息:" + textMessage.getText());
                // textMessage.acknowledge();// 手动签收
                // session.commit();
            } else {
                break;
            }
        }
        connection.close();
    }

先启动消费者1-N个,再启动生产者发送消息,再看控制台。
在这里插入图片描述
页面面板属性解释:

Number Of Consumers 消费者数量
Number Of Pending Messages 等待消费的消息数量
Messages Enqueued 进入队列的消息 [ 队列消息数量(进过队列的消息数量)]
Messages Dequeued 出了队列的消息 [ 可以理解为是消费这消费掉的数量 ]

JMS消息一致性(消息可靠机制)

ActiveMQ消息签收机制:客戶端成功接收一条消息的标志是一条消息被签收,成功应答。

消息的签收情形分两种:

  1. 带事务的 Session
// Session 带事务,自动签收
// false表示不带事务,Session.AUTO_ACKNOWLEDGE表示自动签收
Session session = connection.createSession(true, Session.AUTO_ACKNOWLEDGE);

如果session带有事务,并且事务成功提交,则消息被自动签收。如果事务回滚,则消息会被再次传送。

  1. 不带事务的 Session
// Session 不带事务,手动签收
// false表示不带事务,Session.CLIENT_ACKNOWLEDGE 表示手动签收
Session session = connection.createSession(false, Session.CLIENT_ACKNOWLEDGE);

不带事务的session的签收方式,取决于session的配置。

ActiveMQ支持一下三種模式:
在这里插入图片描述
Session.AUTO_ACKNOWLEDGE 消息自动签收
Session.CLIENT_ACKNOWLEDGE 消息手动签收
(客戶端调用 acknowledge方法手动签收)

// 消费者
textMessage.acknowledge();//手动签收

Session.DUPS_OK_ACKNOWLEDGE 非必须签收,消息可能会重复发送。
在第二次重新传送消息的时候,消息只有在被确认之后,才认为已经被成功地消费了。

消息的成功消费通常包含三个阶段:
1.客户接收消息
2.客户处理消息
3.消息被确认

在事务性会话中,当一个事务被提交的时候,确认自动发生。在非事务性会话中,消息何时被确认取决于创建会话时的应答模式(acknowledgement mode

SpringBoot整合ActiveMQ

下面我用两个项目的案例示范(P2P(点对点)方式):
示范案例模拟情况:项目B 需要 项目A 的某某数据

两个项目分别都引入 ActiveMQMaven 依赖和配置Yml文件

<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-activemq</artifactId>
</dependency>

yml配置

spring:
  activemq:
    broker-url: tcp://127.0.0.1:61616
    user: admin
    password: admin
# 自定义参数Queue名
myqueue: darian-queue

项目A(生产者)
队列配置

@Configuration
public class QueueConfig {

    @Value("${myqueue}")
    private String queue;

    // 创建ActiveMQ队列
    @Bean
    public Queue logQueue() {
        return new ActiveMQQueue(queue);
    }

}

生产者

@Component
@EnableScheduling // 开启定时任务,模拟生产者实际情况
public class Producer {

    @Autowired
    private JmsMessagingTemplate jsm;
    // JmsTemplate 和 JmsMessagingTemplate 选一个就行 差不多
//    private JmsTemplate jmsTemplate;

    @Autowired
    private Queue queue;

    private int count;

    // 定时生产消息
    @Scheduled(fixedDelay = 5000)
    public void sendMsg(){
        String nowMsg = "test message:" + System.currentTimeMillis();
        System.out.println("生产消息:" + nowMsg);
        // 懒得用对象,喜欢传对象的传对象,传JSON的传JSON 我这偷个懒
        jsm.convertAndSend(queue, nowMsg);
    }

}

项目B(消费者)

消费者

@Component
public class Consumer {

	@JmsListener(destination = "${myqueue}")
	public void receive(String msg) {
		System.out.println("收到消息:"+ msg);
	}

}

然后启动项目B(消费者),再启动项目A(生产者),效果如下:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
这样就Over了!

MQ原理

没有仔细去了解,但大概应该是:消息队列作为消费者获取消息的一个入口,生产者/消费者基于 Socket TCP 协议一直保持和消息队列的连接,生产者往队列中添加消息,消息队列给消费者推送消息。

有点多线程Wati/Notifly的感觉。

tips:虽然都是一些比较基础知识,但我们也不能停止学习的脚步,活到老,学到老嘛。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值