大家学习Java时,都是从打印一条Hello World开始的,同样,作为今天学习RabbitMQ的第一课,就使用RabbitMQ接收并转发一条内容为 ‘Hello World’ 的消息开始。
介绍
RabbitMQ作为一个消息中间件,主要用于接收和转发消息。你可以把它想像成一个邮局,当你往邮局投递信件时,只要写上邮寄的地址,就可以确保这个信件会准确的发送过去。RabbitMQ与邮局的唯一区别是它处理的不是纸质的信件而是二进制类型的数据。
下面有几个核心的专业名词需要介绍下
- Producing 它的意思就是消息的生产者。发送消息的程序就可称为Producing
- Queue 顾名思义,它是一个队列,类似于RabbitMQ内部的邮箱。消息在RabbitMQ中只能存储在Queue中。本质上是一个大的消息缓冲区,大小受内存和硬盘的限制。多个Producers可以往Queue中发送消息,多个Consumers可以从Queue获取消息
- Consuming 它就是信息的接收者,在编程中Consumer大多是等待信息接收
注意:Producer,Consumer,RabbitMQ Server它们没必要在同一个host下,可以部署到不同的host中。一个程序可以即是Producer又是 Consumer
第一个程序(Hello World)
前提条件:需要安装RabbitMQ并在本地运行
在这个例子中,我们需要编写两个类,一个作为Producer发送消息,一个作为Consumer接收并打印消息。
下图中的P就是Producer,C就是Consumer,中间的红色方框就是Queue,作为一个队列,是RabbitMQ的消息缓冲区
maven依赖
<dependency>
<groupId>com.rabbitmq</groupId>
<artifactId>amqp-client</artifactId>
<version>5.7.3</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.slf4j/slf4j-api -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.25</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.slf4j/slf4j-simple -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-simple</artifactId>
<version>1.7.25</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-nop</artifactId>
<version>1.7.2</version>
</dependency>
Producer
ConnectionFactory connectionFactory = new ConnectionFactory();
connectionFactory.setHost("localhost");
try (
Connection connection = connectionFactory.newConnection();
Channel channel = connection.createChannel();
) {
} catch (Exception e) {
System.out.println("发送数据失败:" + e.getMessage());
}
如上面的代码所示,Connection是对socket连接的抽象,并为我们处理了协议版本协商和认证的一些低层工作。由于这次的demo是连接到本地环境,所以host设置为localhost,如果要连接到其它的地址,只需要改成IP或域名即可。然后使用connection对象创建channel对象,程序与RabbitMQ交互都是通过channel对象。
由于Connection、Channel都实现了java.io.Closeable接口,所以将创建connection和channel对象的语句使用try-with-resource方式来写,这样就不需要在代码的关闭资源。
channel.queueDeclare(Constant.QUEUE_NAME, false, false, false, null);
for (int i = 0; i < 100; i++) {
String message = "Hello World! index:" + i;
channel.basicPublish("", Constant.QUEUE_NAME, null, message.getBytes());
System.out.println(" Sent '" + message + "'");
}
如上代码所示,通过调用channel的queueDeclare方法来创建一个queue,第一个参数是指定queue的名字。如果这个queue不存在,则创建指定name的queue,如果已存在,则不作操作。
Consumer
ConnectionFactory connectionFactory = new ConnectionFactory();
connectionFactory.setHost("localhost");
Connection connection = connectionFactory.newConnection();
Channel channel = connection.createChannel();
channel.queueDeclare(Constant.QUEUE_NAME, false, false, false, null);
System.out.println(" [*] Waiting for messages.");
DeliverCallback deliverCallback = (consumeTag,delivery)->{
String message = new String(delivery.getBody(), StandardCharsets.UTF_8);
System.out.println(" Received '" + message + "'");
};
channel.basicConsume(Constant.QUEUE_NAME, deliverCallback, consumeTag -> {
});
如上代码所示,可以看到Consumer的代码和Producer的代码很相似。这段代码有如下几点注意的地方:
- Connection和Channel对象的创建没有用try-with-resource的方式,因为需要与RabbitMQg一直保持连接
- 这里也调用了channel的queueDeclare方法,因为在启动Consumer时不确定Producer是否已经启动。通过调用queueDeclare方法可以确保在启动Consumer时,queue总是存在的
分别启动Producer和Consumer即可看到效果
源码地址 https://github.com/qasrc/rabbitmq_tutorials.git
参考文档 https://www.rabbitmq.com/tutorials/tutorial-one-java.html