rabbitMQ是一个在AMQP协议标准基础上完整的,可服用的企业消息系统。它遵循Mozilla Public License开源协议,采用 Erlang 实现的工业级的消息队列(MQ)服务器,Rabbit MQ 是建立在Erlang OTP平台上
-
RabbitMQ的安装部署
1、Erlang:Erlang是RabbitMQ的运行环境,类似于jre/jdk对于java语言支撑的作用,Erlang支持多种平台和语言。
2、安装Erlang:根据自己的电脑的OS选择下载合适的版本,地址为http://www.erlang.org/downloads。
3、安装RabbitMQ:官网下载地址http://www.rabbitmq.com/install-windows.html。
4、配置环境变量:
新增ERLANG_HOME,路径:D:\Erlang\erl7.0(本地安装目录,至bin目录上一级),如下图1-1
图1-1
新增RABBITMQ_SERVER,路径:D:\Rabbitmq\rabbitmq_server-3.6.9(本地安装目录,至bin目录上一级),如下图1-2
图1-2
将新增的环境变量添加到path路径下,在path最后面加上%ERLANG_HOME%\bin;(注:若path最后面没有;,则先加上;,再加上刚配置好的环境变量%ERLANG_HOME%\bin;),同样的操作在path后面加上%RABBITMQ_SERVER%\sbin如下图1-3所示
2.激活 RabbitMQ's Management Plugin
使用RabbitMQ 管理插件,可以更好的可视化方式查看Rabbit MQ 服务实例的状态。
打开命令窗口:以管理员身份运行CMD进入D:\Rabbitmq\rabbitmq_server-3.6.9\sbin目录下
输入命令:rabbitmq-plugins.bat enable rabbitmq_management
安装完成,可以在任务管理器的服务里面看下如下图2-1所示
图2-1
说明RabbitMQ安装成功,自定义设置下启动类型,RabbitMQ默认占用端口为15672,可在本地浏览器输入:http://localhost:15672,如果成功显示如下图2-2内容表示大功告成
图2-2
默认的用户名guest,密码guest。
3. 简单的数据接收与发送
java作为消息的生产者向MQ发送数据:
1、创建与RabbitMQ远程服务器的Connection的连接,获取connection对象。
2、开辟Channel通道,用connection.createChannel()创建。
3、将数据或消息Date&Message丢入队列中,channel.queueDeclare(“队列别名”……)。
4、关闭连接,关闭通道。
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
/**
* 生产者
* Created by MM on 2019/3/14.
*/
public class Send {
private final static String QUEUE_NAME = "hello";
public static void main(String[] args) throws Exception {
//创建连接工厂
ConnectionFactory factory = new ConnectionFactory();
//连接某地址的rabbitmq服务
factory.setHost("localhost");
//获取到连接以及mq通道
Connection connection = factory.newConnection();
//从链接中创建通道,这是完成大部分API的地方
Channel channel = connection.createChannel();
//声明一个队列,必须生命队列才能够发送消息,我们可以把消息发送到队列中。
//声明一个队列是幂等的-只有当它不存在时才会被创建
channel.queueDeclare(QUEUE_NAME, false, false, false, null);
//消息内容
String message1 = "Hello World! you is a bab boy !";
String message2 = "Hello World! ";
//发送消息到队列中
for (int i = 0; i < 10; i++){
channel.basicPublish("", QUEUE_NAME, null, message1.getBytes("UTF-8"));
System.out.println(" [x] Sent '" + message1 + "'");
}
channel.basicPublish("", QUEUE_NAME, null, message2.getBytes("UTF-8"));
System.out.println(" [x] Sent '" + message2 + "'");
//释放通道,连接
channel.close();
connection.close();
} }
java作为消费者接收MQ的数据:
import com.rabbitmq.client.*;
import java.io.IOException;
/**
* 消费者
* Created by MM on 2019/3/14.
*/
public class Recv {
private final static String QUEUE_NAME="hello";
public static void main(String[] args) throws Exception {
//创建连接工厂
ConnectionFactory factory = new ConnectionFactory();
//设置rabbitmq地址
factory.setHost("localhost");
//创建一个新的连接
Connection connection = factory.newConnection();
//创建一个通道
Channel channel = connection.createChannel();
//声明要关注的队列--在rabbitmq中,队列声明是幂等性
// (一个幂等操作的特点是其任意多次执行所产生的影响均与一次执行的影响相同),
// 也就是说,如果不存在就创建,如果存在,不会对已经存在的队列产生任何影响
channel.queueDeclare(QUEUE_NAME,false,false,false,null);
System.out.println("C [*] Waiting for messages. ");
//DefaultConsumer类实现了Consumer接口,通过传入一个频道,
// 告诉服务器我们需要哪个频道的消息,如果频道中有消息,就会执行回调函数handleDelivery
//(这个方法类似事件监听,如果有消息的时候,会被自动调用)
Consumer consumer = new DefaultConsumer(channel){
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
//body 即消息体
String message = new String(body,"utf-8");
System.out.println("C [x] Received '" + message + "'");
}
};
//自动回复队列应答--rabbitmq中的消息确认机制(监听队列,第二个参数是否自动进行消息确认,自动使用true)
channel.basicConsume(QUEUE_NAME,true,consumer);
} }
4.消息确认机制(ACK)
通过上面的案例,多调试几次会发现,消息一旦被消费者接收,队列中的消息就会被删除。
那么问题来了:rabbitmq怎么知道消息被接收了呢?
如果消费者领取消息后,还没执行操作就挂掉了呢?或者抛出了异常?消息消费失败,但是rabbitmq无从得知,这样消息就丢失了。
因此,rabbitmq有一个ack机制,当消费者获取消息后,回想rabbitmq发送回执ack,告知消息已经被接收。不过这种回执ack分两种情况:
-
自动ack:消息一旦被接收,消费者自动发送ack
-
手动ack:消息接收后,不会发送ack,需要手动调用。
如何选择呢?
这需要看消息的重要性:
-
如果消息不太重要,丢失了也没有影响,那么自动ack会比较方便。
-
如果消息非常重要,不容丢失。那么最好在消费完成后手动ack,否则接收消息后就自动ack,rabbitmq就会把消息从队列中删除。如果此时消费者宕机,那么消息就丢失了。
代码只需要修改一点点
import com.rabbitmq.client.*;
import java.io.IOException;
/**
* Created by MM on 2019/3/14.
*/
public class Recv2 {
private final static String QUEUE_NAME="hello";
public static void main(String[] args) throws Exception {
//创建连接工厂
ConnectionFactory factory = new ConnectionFactory();
//设置rabbitmq地址
factory.setHost("localhost");
//创建一个新的连接
Connection connection = factory.newConnection();
//创建一个通道
final Channel channel = connection.createChannel();
//声明要关注的队列--在rabbitmq中,队列声明是幂等性
// (一个幂等操作的特点是其任意多次执行所产生的影响均与一次执行的影响相同),
// 也就是说,如果不存在就创建,如果存在,不会对已经存在的队列产生任何影响
channel.queueDeclare(QUEUE_NAME,false,false,false,null);
System.out.println("C [*] Waiting for messages. ");
//DefaultConsumer类实现了Consumer接口,通过传入一个频道,
// 告诉服务器我们需要哪个频道的消息,如果频道中有消息,就会执行回调函数handleDelivery
//(这个方法类似事件监听,如果有消息的时候,会被自动调用)
DefaultConsumer consumer = new DefaultConsumer(channel){
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
//body 即消息体
String message = new String(body,"utf-8");
System.out.println("C [x] Received '" + message + "'");
//手动进行ACK
channel.basicAck(envelope.getDeliveryTag(),false);
}
};
//(监听队列,第二个参数是否自动进行消息确认,手动使用false)
channel.basicConsume(QUEUE_NAME,false,consumer);
}
注意到最后一行代码:
channel.basicConsume(QUEUE_NAME, false, consumer);
如果第二个参数为true,则会自动进行ACK;如果为false,则需要手动ACK。方法的声明: