推广

RabbitMQ专题讲座

https://segmentfault.com/l/15...

CoolMQ开源项目

我们利用消息队列实现了分布式事务的最终一致性解决方案,请大家围观。可以参考源码:https://github.com/vvsuperman…,项目支持网站: http://rabbitmq.org.cn,最新文章或实现会更新在上面

前言

消息队列想必大家都有一定了解:用来解耦,上级模块不用关心下级模块是否执行成功,最常见的比如说日志,核心系统并不关心日志是否成功,日志什么时候记录。这种情形就可以用消息队列来解耦。

RabbitMQ作为消息队列的一个典型实践,完全实现了AMQ标准,与Kafka的快快快不同,它追求的稳定、可靠。下面就来几篇文章来详细介绍下,均翻译至RabbitMQ的官方文档。

RabbitMQ是一个消息的中介(用来接受以及转发消息),就像是一个非常可靠的邮局,当信件放到邮局时,信件就确保能到达,所以,RabbitMQ可以看成是邮箱、邮局、以及邮递员的合体

RabbitMQ的一些重要概念

produceing(生产者):生产数据

clipboard.png

queue(队列):

类似于邮箱,存在于RabbitMQ服务器的内部,用来存储消息,并且消息只能存储在队列里面。队列的大小只受RabbitMQ主机内存和硬盘的影响。同一个队列不仅可以绑定多个生产者,而且能够发送消息到多个消费者。

clipboard.png

Consuming(消费者):接受并消费消息。

clipboard.png

Hello World

下面我们来写我们的第一个“Hello World”,我们会使用Java的API来编写一个生产者来生产消息,以及一个消费者来消费消息

P是我们的生产者,而C是我们的消费者。中间的box是我们的queue:作为消息缓冲,是RabbitMQ用来存储转发消息给消费者的。

clipboard.png

Java客户端库

RabbitMQ支持多重协议,这里我们会用AMQP 0-9-1来说明,它是一个消息队列的通用协议。RabbitMQ同时也有多种语言的客户端,我们在这里用Java来做说明。

首先请下载Java客户端包以及它所依赖的SLF4JSLF4J SIMPLE,将它们拷贝到自己的工作区。

引入RabbitMQ同样也可以使用Maven来做依赖管理, groupId是com.rabbitmq 以及artifactId amqp-client

发送请求

clipboard.png

生产者会发送消息到MQ,然后退出
在Send.java中,首先我们import一些类

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

设置我们的主类

public class Send {
  private final static String QUEUE_NAME = "hello";

  public static void main(String[] argv)
      throws java.io.IOException {
      ...
  }
}   

创建Connection

ConnectionFactory factory = new ConnectionFactory();
factory.setHost("localhost");
Connection connection = factory.newConnection();
Channel channel = connection.createChannel();

这里我们连接的是本地,你当然也可以连接到另一个服务器上,只需要指明服务器的名称和ip地址。

下面我们要创建一个Channel,大家可以想象一些,消息的产生和发送都是通过这个Channel完成的。

当然,我们还需要顶一个一个Queue来接受消息
channel.queueDeclare(QUEUE_NAME, false, false, false, null);
String message = "Hello World!";
channel.basicPublish("", QUEUE_NAME, null, message.getBytes());
System.out.println(" [x] Sent '" + message + "'");

对于Queue的定义是冥等的,如果不存在才会创建,如果存在则不会再建新的。消息会被格式化成byte的数组,方便进行任意的转换。

最后,我们关闭通道

channel.close();
connection.close();

完整的代码可以看这个地方:send.java

接受请求

消费者会从RabbitMQ接收到请求,消息是被到消费者,而且消费者会一直监听着消息队列,一旦有有新的消息就会打印出来。

Recv.java几乎于Send完全类似

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

Defaultconsumer是一个继承了Consumer接口的类,方便我们来存储消息队列来的消息。建立消费者与我们建立生产者非常类似:

public class Recv {
  private final static String QUEUE_NAME = "hello";

  public static void main(String[] argv)
      throws java.io.IOException,
             java.lang.InterruptedException {

    ConnectionFactory factory = new ConnectionFactory();
    factory.setHost("localhost");
    Connection connection = factory.newConnection();
    Channel channel = connection.createChannel();

    channel.queueDeclare(QUEUE_NAME, false, false, false, null);
    System.out.println(" [*] Waiting for messages. To exit press CTRL+C");
    ...
    }
}

你可以注意到我们在消费者定义了一个Queue,因此我们是需要在生产者之前启动消费者的,我们要确保我们在消费消息之前这个队列是已经存在的。

然后我们需要告诉mq服务可以推送消息给我们。因为这个推送是异步的,因此我们可以提供一个回调方法,DefaultConsumer会暂时存储这个消息,直到消费者以及准备好来处理接受到的消息了(消息会存储在消费者中直到消费者有能力来消费它,可以想象一下数据库等高IO操作)

Consumer 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(" [x] Received '" + message + "'");
  }
};
channel.basicConsume(QUEUE_NAME, true, consumer);

完整的Recv.java地址

跑起来

我们可以先用javac来编译程序

javac -cp amqp-client-4.0.2.jar Send.java Recv.java

而后来运行它,这需要我们在路径加上它的依赖包,我们首先启动的是消费者

java -cp .:amqp-client-4.0.2.jar:slf4j-api-1.7.21.jar:slf4j-simple-1.7.22.jar Recv

而后启动发送者

java -cp .:amqp-client-4.0.2.jar:slf4j-api-1.7.21.jar:slf4j-simple-1.7.22.jar Send

消费者会持续等待,并打印从生产者哪里来的消息,你可以用(Ctrl-C)来停止它。所以你要另外开启一个命令行窗口来运行生产者。

查看队列

也许你想知道RabbitMQ中到底有多少个消息,你可以使用rabbitmqctl工具:

sudo rabbitmqctl list_queues

在Windows中:

rabbitmqctl.bat list_queues




载入中...