RabbitMQ札记-RabbitMQ入门

RabbitMQ是一种消息队列产品。

消息队列简介

什么是消息队列

顾名思义,消息队列是用来接收和转发消息的队列。消息是二进制数据块,可以是简单的字符串,也可以是更复杂的数据对象。队列接收消息生产者发送的消息,并将消息转发给消息消费者。

消息生产者、消息队列、消息消费者不必在同一服务器上,实际上在大多数应用中,它们确实都不在同一服务器上。

为什么要使用消息队列

从上面对消息队列的介绍中不难看出,消息队列可以实现程序之间的异步协作。

以12306为例,购买火车票有下单系统处理下单请求、支付系统处理支付、通知系统发送“购票成功”通知。支付后用户就能马上看到支付结果,这时用户就能知道购票成功了,所以发送通知这个步骤不是需要立即执行的。12306可以发送通知命令到消息队列中,通知系统从队列中取出命令并通过邮件和短信给用户发送“购票成功”的通知。这样就实现了业务之间的解耦,减少了系统的压力。

其他常见场景包括最终一致性、广播、错峰流控等等。

消息队列有哪些

市场上的消息队列产品有很多,比如ActiveMQ、RabbitMQ、ZeroMQ、Kafka、 RocketMQ ,连Redis这样的数据库系统也支持消息队列。

RabbitMQ简介

RabbitMQ是一个开源的AMQP(Advanced Message Queue)实现,服务器端用Erlang语言编写,支持多种语言,如:Python、Ruby、.NET、Java、JMS、C、PHP、ActionScript、XMPP、STOMP等,支持AJAX。用于在分布式系统中存储转发消息,在易用性、扩展性、高可用性等方面表现不俗。

AMQP

Advanced Message Queue,高级消息队列协议。它是应用层协议的一个开放标准,为面向消息的中间件设计,基于此协议的客户端与消息中间件可传递消息,并不受产品、开发语言等条件的限制。AMQP的主要特征是面向消息、队列、路由(包括点对点和发布/订阅)。

RabbitMQ特点
  • 多语言客户端
    上面提到了,RabbitMQ支持很多种语言。
  • 可靠性
    RabbitMQ 使用一些机制来保证可靠性,如持久化、传输确认、发布确认。
  • 消息集群
    多个 RabbitMQ 服务器可以组成一个集群。
  • 提供管理界面
    RabbitMQ 提供了一个易用的用户界面,使得用户可以监控和管理消息。
  • 多插件
    RabbitMQ 提供了许多插件,也支持编写自己的插件。

概念模型

Publisher表示消息的生产者。

Exchange表示交换器,用来接收生产者发送的消息并将这些消息路由给队列。从图中可以看出,Procuder发布的Message进入了Exchange。Exchange通过Routing key与Queue绑定在一起。通过Routing key, RabbitMQ可以得知应该把这个Message放到哪个Queue里。Exchange分发消息时根据类型的不同分发策略有区别,目前共五种类型:fanout、direct、topic、headers、x-delayed-message。

  • fanout。每个发到 fanout 类型交换器的消息都会分到所有绑定的队列上去。
  • direct。如果Routing key匹配, 那么Message就会被传递到相应的Queue中。比如key*可以传递到Routing key为key的Queue。
  • topic。对Routing key进行模式匹配,比如key*可以传递到Routing key为key1、key2、key3的Queue。

Binding表示Exchange通过Routing key与Queue的路由关系。

Queue表示消息队列,用来接收保存消息直到发送给消费者。

Connection表示一个TCP的连接,Producer和Consumer都是通过TCP连接到RabbitMQ服务器的。

Channel表示建立在真实的TCP连接内的虚拟连接,AMQP命令都是通过Channel发出去的,不管是发布消息还是消息,都是通过Channel完成。从下面的Demo中可以看到,建立Connection后,接下来就是通过Connection建立Channel。
那么,为什么使用Channel,而不是直接使用TCP连接?
对于OS来说,建立和关闭TCP连接是有代价的,频繁的建立关闭TCP连接对于系统的性能有很大的影响,而且TCP的连接数也有限制,这也限制了系统处理高并发的能力。但是,在TCP连接中建立Channel是没有上述代价的,所以引入了Channel的概念,以复用一条 TCP 连接。

Consumer表示消息消费者。

Virtual Host表示虚拟主机,表示一批交换器、消息队列和相关对象。vhost 是 AMQP 概念的基础,必须在连接时指定,RabbitMQ 默认的 vhost 是 / 。

Broker表示消息队列服务器实体。

入门应用

安装RabbitMQ

安装RabbitMQ ,用户名设为panweiwei,密码设为panweiwei。
省略。

新建Queue、Exchanges

新建名为名为testExchanges的Exchanges、名为testQueue的Queue,并将两者绑定,Routing key名为testQueue。结果如下如所示

代码
  1. 新建Java Project,下载客户端库amqp-client-4.0.2及其依赖项([
  2. SLF4J API](http://central.maven.org/maven2/org/slf4j/slf4j-api/1.7.21/slf4j-api-1.7.21.jar)和 SLF4J Simple)将其添加到项目的lib目录下并执行add to buildPath,新建消息生产者类Producer,消息消费者类Consumer。结果如下图所示

2.消息生产者类Producer

import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import java.io.IOException;
import java.util.concurrent.TimeoutException;

public class Producer {

    private final static String QUEUE_NAME = "testQueue";
    private final static String EXCHANGE_NAME = "testExchanges";
    private final static String ROUTING_KEY = "testQueue";

    public static void main(String[] args) throws IOException, TimeoutException {
        // 创建一个到服务器的连接
        ConnectionFactory factory = new ConnectionFactory();
        factory.setUsername("panweiwei");
        factory.setPassword("panweiwei");
        factory.setHost("127.0.0.1");
        Connection conn = factory.newConnection();
        Channel channel = conn.createChannel();

        channel.exchangeDeclare(EXCHANGE_NAME, "direct", true);

        // 发布消息到队列中
        String message = "Hello World!";
        channel.basicPublish(EXCHANGE_NAME, ROUTING_KEY, null, message.getBytes());
        System.out.println("已发送消息:" + message);

        //关闭渠道和连接;
        channel.close();
        conn.close();
    }
}

3.消息消费者类Consumer

import com.rabbitmq.client.*;
import java.io.IOException;
import java.util.concurrent.TimeoutException;

public class Consumer {

    private final static String QUEUE_NAME = "testQueue";
    private final static String EXCHANGE_NAME = "testExchanges";
    private final static String ROUTING_KEY = "testQueue";

    public static void main(String[] args) throws IOException, TimeoutException {
        // 创建一个到服务器的连接
        ConnectionFactory factory = new ConnectionFactory();
        factory.setUsername("panweiwei");
        factory.setPassword("panweiwei");
        factory.setHost("127.0.0.1");
        Connection conn = factory.newConnection();
        Channel channel = conn.createChannel();

        channel.exchangeDeclare(EXCHANGE_NAME, "direct", true);
        String queueName = channel.queueDeclare().getQueue();

        // 绑定队列
        channel.queueBind(queueName, EXCHANGE_NAME, ROUTING_KEY);

        DefaultConsumer consumer = new DefaultConsumer(channel) {
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties,
                    byte[] body) throws IOException {
                String message = new String(body, "UTF-8");
                System.out.println("收到消息:" + message);
            }
        };
        channel.basicConsume(queueName, true, consumer);
    }
}
测试

1.运行 Consumer
先运行 Consumer ,这样当生产者发送消息的时候能在消费者后端看到消息记录。

2.运行 Producer
接着运行 Producer ,发布一条消息,在 Consumer 的控制台能看到接收的消息

收到消息:Hello World!

连续发送多条消息,在 Consumer 的控制台能看到接收的消息

收到消息:Hello World!
收到消息:Hello World!
收到消息:Hello World!

如果没有看到,请看RabbitMQ的管理界面,找到testQueue,看其中有没有消息没有发出去

如图所示,有6条消息没有发出去。

EDN.

你可能感兴趣的:(RabbitMQ)