一、安装RabbitMQ安装
安装RabbitMQ之前必须安装它的依赖Erlang
Erlang的安装方式大概有两种:
从Erlang Solution安装(推荐)
添加erlang solutions源
$ wget https://packages.erlang-solutions.com/erlang-solutions-1.0-1.noarch.rpm
$ sudo rpm -Uvh erlang-solutions-1.0-1.noarch.rpm
$ sudo yum install erlang
从EPEL源安装(这种方式安装的Erlang版本可能不是最新的,有时候不能满足RabbitMQ需要的最低版本)
启动EPEL源
$ sudo yum install epel-release
安装erlang
$ sudo yum install erlang
完成后安装RabbitMQ:
先下载rpm:
wget http://www.rabbitmq.com/releases/rabbitmq-server/v3.6.6/rabbitmq-server-3.6.6-1.el7.noarch.rpm
下载完成后安装:
yum install rabbitmq-server-3.6.6-1.el7.noarch.rpm
安装时如果遇到下面的依赖错误
Error: Package: socat-1.7.2.3-1.el6.x86_64 (epel)
Requires: libreadline.so.5()(64bit)
可以尝试先执行
$ sudo yum install socat
关于RabbitMQ的一些基本操作
$ sudo chkconfig rabbitmq-server on # 添加开机启动RabbitMQ服务
$ sudo /sbin/service rabbitmq-server start # 启动服务
$ sudo /sbin/service rabbitmq-server status # 查看服务状态
$ sudo /sbin/service rabbitmq-server stop # 停止服务
查看当前所有用户
$ sudo rabbitmqctl list_users
查看默认guest用户的权限
$ sudo rabbitmqctl list_user_permissions guest
添加新用户
$ sudo rabbitmqctl add_user username password
设置用户tag
$ sudo rabbitmqctl set_user_tags username administrator
赋予用户默认vhost的全部操作权限
$ sudo rabbitmqctl set_permissions -p / username ".*" ".*" ".*"
查看用户的权限
$ sudo rabbitmqctl list_user_permissions username
开启web管理接口
如果只从命令行操作RabbitMQ,多少有点不方便。幸好RabbitMQ自带了web管理界面,只需要启动插件便可以使用。
$ sudo rabbitmq-plugins enable rabbitmq_management
然后通过浏览器访问
http://localhost:15672
输入用户名和密码访问web管理界面了。
注意:必须开启web管理接口才能通过浏览器访问rabbitmq管理端界面,在linux环境下必须开启15762和5672端口,如果是用腾讯云或者阿里云需要在安全组里自定义添加,guest账号只能通过本机访问。
二、RabbitMQ的五种模式
1、普通模式(又叫推送模式)
生产者直接将消息推送到指定队列,消费者消费指定队列上的消息,一个队列对应着一个消费者。
生产者代码:
**
* @author wangdong
*/
public class RabbitUtil {
public static final String RABBITMQ_USER = "wangdong";
public static final String RABBITMQ_PASSWORD = "wangdong";
public static final Integer RABBITMQ_PORT = 5672;
public static final String RABBITMQ_HOST = "134.175.217.237";
public static Connection getRabbitConnection(){
ConnectionFactory factory = new ConnectionFactory();
factory.setUsername(RABBITMQ_USER);
factory.setPassword(RABBITMQ_PASSWORD);
factory.setVirtualHost("/");
factory.setHost(RABBITMQ_HOST);
factory.setPort(RABBITMQ_PORT);
Connection conn = null;
try {
conn = factory.newConnection();
} catch (Exception e) {
e.printStackTrace();
}
return conn;
}
}
//1、简单模式 -- 推送模式
public static void producer1() throws Exception {
//1.获得连接
Connection connection = RabbitUtil.getRabbitConnection();
//2.建立通道
Channel channel = connection.createChannel();
//3.声明队列
channel.queueDeclare("test1",false,false,false,null);
//4.创建消息
String s = "我是推送模式";
//5.发送消息
channel.basicPublish("","test1",null,s.getBytes());
System.out.println("sent"+":"+s);
//5.关闭通道 关闭连接
channel.close();
connection.close();
}
消费者代码:
//1、简单模式 -- 推送模式
public static void consumer1() throws Exception{
//1、获取连接
Connection connection = RabbitUtil.getRabbitConnection();
//2、声明通道
Channel channel = connection.createChannel();
//3、声明队列
channel.queueDeclare("test1", false, false, false, null);
//4、定义队列的消费者
QueueingConsumer queueingConsumer = new QueueingConsumer(channel);
//5、监听队列
/*
true:表示自动确认,只要消息从队列中获取,无论消费者获取到消息后是否成功消费,都会认为消息已经成功消费
false:表示手动确认,消费者获取消息后,服务器会将该消息标记为不可用状态,等待消费者的反馈,
如果消费者一直没有反馈,那么该消息将一直处于不可用状态,并且服务器会认为该消费者已经挂掉,不会再给其
发送消息,直到该消费者反馈。
*/
channel.basicConsume("test1",true,queueingConsumer);
//6、获取消息
while (true){
QueueingConsumer.Delivery delivery = queueingConsumer.nextDelivery();
String message = new String(delivery.getBody());
System.out.println(" [x] Received '" + message + "'");
}
}
2、工作队列模式(竞争模式)
一个消费者将信息推送到队列上,然后多个消费者共同消费,谁的效率高,消费的消息越多。注意:必须在服务端加上channel.basicQos(1); 指该消费者在接收到队列里的消息但没有返回确认结果之前,它不会将新的消息分发给它。否则则消费者消费的消息均分。
生产者代码:
//2、工作队列模式
public static void producer2() throws Exception{
Connection connection = RabbitUtil.getRabbitConnection();
Channel channel = connection.createChannel();
channel.queueDeclare("test2",false,false,false,null);
for (int i = 0; i < 1000 ; i++) {
String msg = "我是工作队列模式"+i;
channel.basicPublish("","test2",null,msg.getBytes());
System.out.println("[x] Sent'"+msg+"'");
//Thread.sleep(i*10);
}
}
消费者1代码:
//2、工作队列模式
public static void consumer2 () throws Exception {
Connection connection = RabbitUtil.getRabbitConnection();
Channel channel = connection.createChannel();
channel.queueDeclare("test2",false,false,false,null);
channel.basicQos(1);
QueueingConsumer queueingConsumer = new QueueingConsumer(channel);
channel.basicConsume("test2",false,queueingConsumer);
while (true){
QueueingConsumer.Delivery delivery = queueingConsumer.nextDelivery();
String msg = new String(delivery.getBody());
System.out.println(" [x] Received '" + msg + "'");
Thread.sleep(10);
channel.basicAck(delivery.getEnvelope().getDeliveryTag(),false);
}
}
消费者2代码:
public static void consumer3() throws Exception {
Connection connection = RabbitUtil.getRabbitConnection();
Channel channel = connection.createChannel();
channel.queueDeclare("test2",false,false,false,null);
channel.basicQos(1);
QueueingConsumer queueingConsumer = new QueueingConsumer(channel);
channel.basicConsume("test2",false,queueingConsumer);
while (true){
QueueingConsumer.Delivery delivery = queueingConsumer.nextDelivery();
String msg = new String(delivery.getBody());
System.out.println(" [x] Received '" + msg + "'");
Thread.sleep(1000);
channel.basicAck(delivery.getEnvelope().getDeliveryTag(),false);
}
}
3、发布/订阅模式
一个生成者将消息推送到交换机中,然后交换机将消息推送到多个队列中,由多个消费者消费。生产者生产多少消息,各个队列中就会有多少消息。
生产者代码:
//3、发布/订阅模式
public static void producer3() throws Exception{
//1、创建连接
Connection connection = RabbitUtil.getRabbitConnection();
//2、创建通道
Channel channel = connection.createChannel();
//申明交换机
channel.exchangeDeclare("fanout_name","fanout");
//创建消息
String msg = "我是消息订阅模式";
//发送消息到交换机
channel.basicPublish("fanout_name", "", null, msg.getBytes());
System.out.println("[x] Sent'" + msg + "'");
//关闭通道和连接
channel.close();
connection.close();
}
消费者1代码:
public static void consumer4() throws Exception{
//1、获取连接
Connection connection = RabbitUtil.getRabbitConnection();
//2、声明通道
Channel channel = connection.createChannel();
//3、声明队列
channel.queueDeclare("test3", false, false, false, null);
//4、绑定队列到交换机
channel.queueBind("test3","fanout_name","");
//同一时刻服务器只会发送一条消息给消费者
channel.basicQos(1);
//5、定义队列的消费者
QueueingConsumer queueingConsumer = new QueueingConsumer(channel);
//6、监听队列,手动返回完成状态
channel.basicConsume("test3",false,queueingConsumer);
//6、获取消息
while (true){
QueueingConsumer.Delivery delivery = queueingConsumer.nextDelivery();
String message = new String(delivery.getBody());
System.out.println(" 消费者1:" + message + "'");
//消费者1接收一条消息后休眠10毫秒
Thread.sleep(10);
//返回确认状态
channel.basicAck(delivery.getEnvelope().getDeliveryTag(),false);
}
}
消费者2代码:
public static void consumer5() throws Exception{
//1、获取连接
Connection connection = RabbitUtil.getRabbitConnection();
//2、声明通道
Channel channel = connection.createChannel();
//3、声明队列
channel.queueDeclare("test4", false, false, false, null);
//4、绑定队列到交换机
channel.queueBind("test4","fanout_name","");
//同一时刻服务器只会发送一条消息给消费者
channel.basicQos(1);
//5、定义队列的消费者
QueueingConsumer queueingConsumer = new QueueingConsumer(channel);
//6、监听队列,手动返回完成状态
channel.basicConsume("test4",false,queueingConsumer);
//6、获取消息
while (true){
QueueingConsumer.Delivery delivery = queueingConsumer.nextDelivery();
String message = new String(delivery.getBody());
System.out.println(" 消费者2:" + message + "'");
//消费者1接收一条消息后休眠10毫秒
Thread.sleep(10);
//返回确认状态
channel.basicAck(delivery.getEnvelope().getDeliveryTag(),false);
}
}
4、路由模式
生产者将信息推送到交换机时候需要指定一个路由键,然后消费者绑定队列的时候需要指定一个绑定键,只有这两个建对应上了交换机的内容才会到对应的列上被消费者消费。就相当与指定的消息才会到指定的队列中被指定的消费者消费。
生产者代码:
public static void produce4()throws Exception {
Connection connection = RabbitUtil.getRabbitConnection();
Channel channel = connection.createChannel();
channel.exchangeDeclare("direct_exchange","direct");
//路由键为wangdong1、wangdong2、wangdong3、wangdong4
List<String> key = new ArrayList<String>(){};
key.add("update");
key.add("delete");
key.add("select");
key.add("add");
for (int i = 0; i < key.size(); i++) {
String msg = "我是路由模式"+"key:"+key.get(i);
channel.basicPublish("direct_exchange",key.get(i),null,msg.getBytes());
System.out.println("[x] Sent'" + msg + "'");
}
channel.close();
connection.close();
}
消费者1代码:
public static void consumer6() throws Exception{
Connection connection = RabbitUtil.getRabbitConnection();
Channel channel = connection.createChannel();
channel.queueDeclare("test5",false,false,false,null);
channel.queueBind("test5","direct_exchange","update");
channel.queueBind("test5","direct_exchange","delete");
channel.queueBind("test5","direct_exchange","add");
channel.basicQos(1);
QueueingConsumer queueingConsumer = new QueueingConsumer(channel);
channel.basicConsume("test5",false,queueingConsumer);
while (true){
QueueingConsumer.Delivery delivery = queueingConsumer.nextDelivery();
String msg = new String(delivery.getBody());
System.out.println("消费者1:"+msg);
channel.basicAck(delivery.getEnvelope().getDeliveryTag(),false);
}
}
消费者2代码:
public static void consumer7() throws Exception{
Connection connection = RabbitUtil.getRabbitConnection();
Channel channel = connection.createChannel();
channel.queueDeclare("test6",false,false,false,null);
channel.queueBind("test6","direct_exchange","select");
channel.basicQos(1);
QueueingConsumer queueingConsumer = new QueueingConsumer(channel);
channel.basicConsume("test6",false,queueingConsumer);
while (true){
QueueingConsumer.Delivery delivery = queueingConsumer.nextDelivery();
String msg = new String(delivery.getBody());
System.out.println("消费者2:"+msg);
channel.basicAck(delivery.getEnvelope().getDeliveryTag(),false);
}
}
当我的路由键为wangdong1/wangdong2/wangdong3/wangdong4。消费者1的绑定键为wangdong1/wangdong2/wangdong3。消费者2的绑定键为wangdong4的时候,这个时候消费者1和消费者2都能接手到路由键wangdong4这个消息。不明所以,请大声赐教!!!
5、通配符模式
生产者发送消息到交换机,type=topic,交换机根据绑定队列的routing key的值进行通配符匹配;符号#:匹配一个或者多个词lazy.# 可以匹配lazy.irs或者lazy.irs.cor
符号*:只能匹配一个词lazy.* 可以匹配lazy.irs或者lazy.co
相当于路由模式的一个扩展吧
生产者代码:
//5、通配符模式
public static void producer5() throws Exception{
Connection connection = RabbitUtil.getRabbitConnection();
Channel channel = connection.createChannel();
channel.exchangeDeclare("topic_name","topic");
List<String> topicKey = new ArrayList<String>();
topicKey.add("select.00000");
topicKey.add("update.111111");
topicKey.add("update.222222");
topicKey.add("update.333333");
for (int i = 0; i < topicKey.size(); i++) {
String msg = "我是通配符模式"+"key:"+topicKey.get(i);
channel.basicPublish("topic_name",topicKey.get(i),null,msg.getBytes());
System.out.println("[x] Sent'" + msg + "'");
}
channel.close();
connection.close();
}
消费者1代码:
//5、通配符模式
public static void consumer8() throws Exception{
Connection connection = RabbitUtil.getRabbitConnection();
Channel channel = connection.createChannel();
channel.queueDeclare("test7",false,false,false,null);
channel.queueBind("test7","topic_name","update.*");
channel.basicQos(1);
QueueingConsumer queueingConsumer = new QueueingConsumer(channel);
channel.basicConsume("test7",false,queueingConsumer);
while (true){
QueueingConsumer.Delivery delivery = queueingConsumer.nextDelivery();
String msg = new String(delivery.getBody());
System.out.println("消费者1:"+msg);
channel.basicAck(delivery.getEnvelope().getDeliveryTag(),false);
}
}
消费者2代码:
public static void consumer9() throws Exception{
Connection connection = RabbitUtil.getRabbitConnection();
Channel channel = connection.createChannel();
channel.queueDeclare("test8",false,false,false,null);
channel.queueBind("test8","topic_name","select.#");
channel.basicQos(1);
QueueingConsumer queueingConsumer = new QueueingConsumer(channel);
channel.basicConsume("test8",false,queueingConsumer);
while (true){
QueueingConsumer.Delivery delivery = queueingConsumer.nextDelivery();
String msg = new String(delivery.getBody());
System.out.println("消费者2:"+msg);
channel.basicAck(delivery.getEnvelope().getDeliveryTag(),false);
}
}
后三种模式可统称路由模式,普通模式、工作队列模式、路由模式。交换机有五种类型:Direct exchange(直连交换机)、Fanout exchange(扇型交换机)、Topic exchange(主题交换机)、Headers exchange(头交换机)、默认存在的交换机、Dead Letter Exchange(死信交换机)
路由模式有的是Direct exchange(直连交换机);
订阅/发布模式用的是Fanout exchange(扇型交换机);
通配符模式用的是Topic exchange(主题交换机);
head模式有的是Headers exchange(头交换机);