AMQP简介与RabbitMQ代码实战

一、AMQP简介

AMQP(Advanced Message Queuing Protocol),高级消息队列协议。一个提供统一消息服务的应用层标准高级消息队列协议,面向消息的中间件设计。
AMQP的主要特征是面向消息、队列、路由(包括点对点和发布/订阅)、可靠性、安全。

相比较于JMS规范,AMQP有以下优势:
1. JMS定义的是API规范,而AMQP定义了线路层的协议。即JMS实现所发送的消息不能保证被另外不同的JMS实现使用;
而AMQP的线路层协议规范了消息的格式,这样不仅能跨AMQP实现,还能跨语言和跨平台。
2. AMQP具有更加灵活和透明的消息模型。
JMS中只有点对点和发布-订阅两种模式;而AMQP通过将消息生产者与消息队列解耦实现多种方式来发送消息。

下面介绍下AMQP时如何实现解耦的。
在JMS规范当中,有3个重要元素:

  • 消息生产者
  • 消息消费者
  • 消息通道(主题或队列)
    Alt text
    (JMS)

消息生产者将消息发送到通道中,消费者从通道中取出数据消费。这里通道具有双重责任:
1)解耦消息的生产者与消费者;
2)传递数据以及确定消息发送地方。

而在AMQP当中,在消息的生产者与通道之间引入了一种机制:Exchange(交换器),解耦了消息的生产者与队列。
Alt text
(AMQP)

可以看到,消息生产者将消息(带有一个routing key参数)发送到Exchange上,Exchange会绑定一个或多个队列上,然后Exchange根据不同的路由模式,对比队列携带的routing key参数,负责将信息发送到不同队列上。

二、RabbitMQ简介

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

1. RabbitMQ系统架构

Alt text
(图片来源:http://blog.csdn.net/u013256816/article/details/59117354
如上图,红色线包围的有Exchange和Queue,在服务端,称作Broker,是由RabbitMQ实现的。剩下的则是客户端,有Prodcer和Consumer两种类型。

2. RabbitMQ核心概念

RabbitMQ两大核心组件是Exchange和Queue。

Queue
Queue是一个不重复,唯一,名字随机的的缓冲区,应用程序在其权限之内可以自由地创建、共享使用和消费消息队列。
(在RabbitMQ中,队列的名字是系统随机创建的,且当Consumer与Queue断开连接的时候,Queue会被自动删除,在下一次连接时又会自动创建。)
Alt text
上图,两个队列的名字分别是“amqp.gen-RQ6…” 和 “amqp.gen-AsB..”,是随机产生的。

Exchange
Exchange称作交换器,它接收消息和路由消息,然后将消息发送给消息队列。每个交换器都有独一无二的名字。

Routing Key
生产者在将消息发送给Exchange的时候,一般会指定一个routing key,来指定这个消息的路由规则。
而这个routing key需要与Exchange Type及binding key联合使用才能最终生效。
在Exchange Type与binding key固定的情况下,生产者就可以在发送消息给Exchange时,通过指定routing key来决定消息流向哪里。
RabbitMQ为routing key设定的长度限制为255 bytes。

Binding 和 Binding Key
Alt text

每个Exchange都和一个特定的Queue绑定(可以是多对多的关系)。绑定的同时会指定一个binding key。
每个发送给Exchange的消息一般都有一个routing key参数;当队列与Exchange绑定的binding key与该消息的routing key参数相同的时候,该消息才会被Exchange发给特定的队列。
(这就好比我们上火车,我们就是消息,而手中的火车票就是routing key 。进站的时候,我们需要找到火车列次(binding key)与我们手中火车票信息匹配的车次才可以进站,即routing key = binding key才可以。)

Exchange Type
AMQP定义了4种不同类型的Exchange,每一种都有不同的路由算法。当消息发送到Exchange时,Exchange 会对比消息的routing key /参数 和 与其绑定的队列的binding key。如果对比结果满足相应的算法,那么消息将会路由到队列上;否则,将不会被路由到队列上。

4种标准的AMQP Exchange 如下所示:
(1)Direct(直接式交换器):

如果消息的routing key 与 binding key 直接匹配,消息会被路由到该队列上(可以用此构建点对点传输模型)。
如图:
Alt text
生产者P发送消息到交换器X。
如果消息的routing key 是 “orange”,则会被路由到队列Q1;
如果消息的routing key 是 “black” 或 “green”,则会被路由到队列Q2。

当然,也可以实现多路绑定,即一个Exchange 和多个Queue绑定时可以有同样的 binding key。
Alt text
上图中,当一个消息的routing key 是 “black”,则会被同时路由到队列Q1和队列Q2。

(2)Topic(主题式交换器):

如果消息的routing key 与 binding key 符合通配符匹配的话,消息会路由到该队列上。

匹配规则:

  • binding key与routing key都是用句点号“. ”分隔的字符串:
    如“stock.usd.nyse”、“nyse.vmw”、“quick.orange.rabbit”;
  • 支持通配符:其中“*”用于匹配一个单词,“#”用于匹配多个单词(可以是零个) 。

Alt text
上图中:

  • routingKey=”quick.orange.rabbit”的消息会同时路由到Q1与Q2;
  • routingKey=”lazy.orange.fox”的消息会路由到Q1和Q2;
  • routingKey=”lazy.brown.fox”的消息会路由到Q2;
  • routingKey=”lazy.pink.rabbit”的消息会路由到Q2(只会投递给Q2一次,虽然这个routingKey与Q2的两个bindingKey都匹配);
  • routingKey=”quick.brown.fox”、routingKey=”orange”、routingKey=”quick.orange.male.rabbit”的消息将会被丢弃,因为它们没有匹配任何bindingKey。

(3)Fanout(广播式交换器):

不管消息的routing key是什么,消息都会被路由到所有与该交换器绑定的队列中。
Alt text
上图中,生产者(P)发送到Exchange(X)的所有消息都会路由到图中的两个Queue,并最终被两个消费者(C1与C2)消费。

(4)Headers

headers类型的Exchange不依赖于routing key与binding key的匹配规则来路由消息,而是根据发送的消息内容中的headers属性进行匹配。

在绑定Queue与Exchange时指定一组键值对;当消息发送到Exchange时,RabbitMQ会取到该消息的headers(也是一个键值对的形式),对比其中的键值对是否完全匹配Queue与Exchange绑定时指定的键值对;如果完全匹配则消息会路由到该Queue,否则不会路由到该Queue。

三、RabbitMQ实战

(通过代码可以发现,RabbitMQ与ActiveMQ最大区别:ActiveMQ更像个救济站,只负责将粮食(消息)发送出去,只要有人来取即可;而RabbitMQ更像是邮局的Postman,负责将邮件(消息)投递到指定人的手中)。
下面用Java实现RabbitMQ的几个模式。
所有代码参考RabbitMQ官网:https://www.rabbitmq.com/getstarted.html(官网给了很详细的介绍,是学习RabbitMQ不二之选)。

1 预备工作:

首先在MAVEN官网导入RabbitMQ Java Client的依赖包;

<dependency>
  <groupId>com.rabbitmq</groupId>
  <artifactId>amqp-client</artifactId>
  <version>4.2.0</version>
</dependency>
2 RabbitMQ的HelloWorld:

不使用Exchange,使用RabbitMQ实现消息的发送与接收:
Producer:

package com.wgs.rabbitmq.helloworld;

import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;

import java.io.IOException;
import java.util.concurrent.TimeoutException;

/**
 * Producer,发送消息到Queue中
 * Created by GenshenWang.nomico on 2017/11/2.
 */
public class RabbitMQProducer {
   

    private static final String QUEUE_NAME = "RABBITMQ_HELLO";

    public static void main(String[] args) throws IOException, TimeoutException {
        ConnectionFactory connectionFactory = new ConnectionFactory();
        //在本地机器创建socket连接
        connectionFactory.setHost("localhost");
        //建立socket连接
        Connection connection = connectionFactory.newConnection();

        //创建Channel,含有处理信息的大部分API
        Channel channel = connection.createChannel();
        //声明一个Queue,用来存放消息
        channel.queueDeclare(QUEUE_NAME, false, false, false, null);
        //消息内容
        String message = "hello, little qute rabbitmq!";
        //发布消息
        channel.basicPublish("", QUEUE_NAME, null, message.getBytes());
        //发布消息成功提示信息
        System.out.println("RABBITMQ客户端成功发送信息:" +  message);

        //关闭连接
        channel.close();
        connection.close();


    }
}

Consumer:

package com.wgs.rabbitmq.helloworld;

import com.rabbitmq.client.*;

import java.io.IOException;
import java.util.concurrent.TimeoutException;

/**
 * Consumer端,从Queue中获取消息,需要一直是监听状态。
 * Created by GenshenWang.nomico on 2017/11/2.
 */
public class RabbitMQConsumer {
   
    private static final String QUEUE_NAME = "RABBITMQ_HELLO";

    public static void main(String[] args) throws IOException, TimeoutException {
        ConnectionFactory connectionFactory = new ConnectionFactory();
        //在本地机器创建socket连接
        connectionFactory.setHost("localhost");
        //建立socket连接
        Connection connection = connectionFactory.newConnection();

        /* 创建Channel,含有处理信息的大部分API */
        Channel channel = connection.createChannel();
        //声明一个Queue,用来获取消息。QUEUE_NAME需要与Producer端相同
        channel.queueDeclare(QUEUE_NAME, 
  • 3
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值