RabbitMq的基本使用
简介
首先RabbitMq是什么?
RabbitMq是一个用Erlang编写的实现了高级消息队列协议的开源消息代理软件
消息队列又是什么?
大白话说就是一个装了消息的队列,一个人在消息队列的一边放入消息,另外一个人在对列的另一边取消息来作出相应的行为。通过消息队列,两人之间的交互是异步的。
高级消息队列协议又是什么?
高级消息队列协议,英文名Advanced Message Queuing Protocol。说白了就是一套公开的消息队列的协议,用来统一规范消息队列的开发。与此类似,Java也有一套消息队列的标准叫JMS,但是由于JMS是Java专属的,无法跨语言。
RabbitMq安装
这里直接用Docker来安装RabbitMq,使用Arch来安装相对较麻烦,需要修改一些文件的权限,当然你不会使用docker也没有关系,只需要安装好doker,配置好一个docker镜像,然后执行一下的指令即可(Docker具体教程正在路上。。。)
$ sudo docker pull docker.io/rabbitmq:3.8-management # 拉取rabbitmq镜像
$ sudo docker images # 查看rabbitmq的镜像号,我这里是镜像号的前三个数字是800
$ sudo docker run --name rabbitmq -d -p 15672:15672 -p 5672:5672 800 # 端口15672用于开启一个网页的控制端,一个则是RabbitMq使用的端口,根据上面查到的镜像号换掉800
RabbitMq的基本组件
看一下这张图(自己画的,有点简陋)
publisher:消息的发布者
exchange:消息的路由器,每个路由器有一个routing key,不同的exchange与queue有不同的绑定规则
- direct:一对一的方式,可绑定多个queue,每次发送只能发送到routing key相同的queue
- fanout:一对多,直接发送到所有与该exchange绑定的queue
- topic:一对多,直接按照一定规则匹配对绑定的queue进行发送比如excahnge的routing key为news.* ,queue有名为news.usa news.china mail.usa 三个queue,那么news.usa news.china就成功匹配了,***** 是指模糊匹配一个单词,你还可以使用**#,#**表示模糊匹配所有
- header:根据header进行发送
queue(exchange后面连接的部分):队列
channel:复用连接的信道
receiver:消息的接收者
broker:指整个RabbitMq服务器,即exchange+queue
vhost:上图没有指出,RabbitMq内部可以划分出独立的host,每个host有自己的exchange和queue,可以通过vhost实现一定的权限控制
使用控制台
RabbitMq提供了一个控制台网页供操作
地址是:http://127.0.0.1:15672/
默认用户名:guest
默认密码:guest
你可以通过这个控制台来使用模拟使用RabbitMq,创建不同的exchanges和queues,将他们相互绑定,验证上述的exchange转发规则
SpringBoot 使用 RabbitMq
开启一个新的项目
引入SpringWeb和RabbitMq
spring.rabbitmq.host=localhost
spring.rabbitmq.username=guest
spring.rabbitmq.password=guest
配置一下application.propertities,当然上面的配置都是默认配置,不写问题也不大
声明exchange和queue
使用单元测试,测试一下exchange和queue的声明
@SpringBootTest
class MqApplicationTests {
@Autowired
AmqpAdmin amqpAdmin; //自动注入一个AmqpAdmin用来声明exchange和queue
@Test
void test(){
//声明一个direct exchange
amqpAdmin.declareExchange(new DirectExchange("direct.news.china" ));
//声明一个fanout exchange
amqpAdmin.declareExchange(new FanoutExchange("fanout.news"));
//声明一个topic exchange
amqpAdmin.declareExchange(new TopicExchange("topic.mail"));
//声明一个名为news.china的queue
amqpAdmin.declareQueue(new Queue("news.china"));
//声明一个名为news.usa的queue
amqpAdmin.declareQueue(new Queue("news.usa"));
//声明一个名为mail.china的queue
amqpAdmin.declareQueue(new Queue("mail.china"));
//声明一个名为mail.usa的ququ
amqpAdmin.declareQueue(new Queue("mail.usa"));
}
}
代码执行成功之后,查看网页控制台,可发现上面声明的exchange和queue已经出现
绑定exchange和queue
可以使用BindBuilder创建Binding,也可以直接new
接收@Test
void binding() {
//绑定队列news.china 和 交换器direct.news.china , 并且赋予一个routin key:new.china,之后exchange会根据消息的routing key来转发
Binding binding1 = BindingBuilder.bind(new Queue("news.china")).to(new DirectExchange("direct.news.china")).with("news.china");
//同上
Binding binding2 = BindingBuilder.bind(new Queue("news.usa")).to(new DirectExchange("direct.news.china")).with("news.usa");
//绑定队列news.china 到 交换器topic.mail , 并且以new.*作为routing key 匹配
//当topic.mail接受到这些消息的时候会往news.china转发:消息routing key为 news.global , news.univers , news.china , news.china
Binding binding3 = BindingBuilder.bind(new Queue("news.china")).to(new TopicExchange("topic.mail")).with("news.*");
//同上
Binding binding4 = BindingBuilder.bind(new Queue("news.usa")).to(new TopicExchange("topic.mail")).with("news.*");
//同上
//不过当topic.ma接收接收到这些消息的时候会往news.china转发: 消息routing key为 mail.global , mail.china , mail.usa
Binding binding5 = BindingBuilder.bind(new Queue("mail.china")).to(new TopicExchange("topic.mail")).with("mail.*");
//同上
Binding binding6 = BindingBuilder.bind(new Queue("mail.usa")).to(new TopicExchange("topic.mail")).with("mail.*");
amqpAdmin.declareBinding(binding1);
amqpAdmin.declareBinding(binding2);
amqpAdmin.declareBinding(binding3);
amqpAdmin.declareBinding(binding4);
amqpAdmin.declareBinding(binding5);
amqpAdmin.declareBinding(binding6);
//绑定队列news.usa和交换器fanout.news , fanout.new接收到所有消息都会玩new.usa转发
amqpAdmin.declareBinding(new Binding("news.usa", Binding.DestinationType.QUEUE, "fanout.news", "fanout.news", null));
//同上
amqpAdmin.declareBinding(new Binding("news.china", Binding.DestinationType.QUEUE, "fanout.news", "fanout.news", null));
}
发送消息
@SpringBootTest
public class SendTest {
@Autowired
AmqpTemplate amqpTemplate;
@Test
void directSend() {
//向news.china发送一则消息,routing key 为 news.china
amqpTemplate.convertAndSend("direct.news.china", "news.china", "hello world");
//同上,不过routing key为news.usa
amqpTemplate.convertAndSend("direct.news.china", "news.usa", "hello world");
}
}
@SpringBootTest
public class SendTest {
@Autowired
AmqpTemplate amqpTemplate;
@Test
void topicSend() {
//topic.mail发送一则消息,routing key 为 news.weather , 由于topic.mail与 队列news.china news.usa 通过news.*进行了模糊匹配
//news.weather匹配news.*,所以这则消息会被发送到这两个队列
amqpTemplate.convertAndSend("topic.mail", "news.weather", "today is rainy");
amqpTemplate.convertAndSend("topic.mail", "mail.work", "ddl is tomorrow");
}
}
@SpringBootTest
public class SendTest {
@Autowired
AmqpTemplate amqpTemplate;
@Test
void fanoutSend() {
//fanout直接将接收到的所有消息发送到与之绑定的队列,所以routing key用test也无所谓
amqpTemplate.convertAndSend("fanout.news", "test", "get message");
}
}
接收消息
@SpringBootTest
public class ReceiveTest {
@Autowired
AmqpTemplate amqpTemplate;
//首先向fanout.news发数据,保证队列中有数据可取
@Test
void fanoutSend() {
amqpTemplate.convertAndSend("fanout.news", "test", "get message");
}
@Test
void receive(){
//接收一条news.china中的数据
Object object = amqpTemplate.receiveAndConvert("news.china");
System.out.println("Objecct类型:"+object.getClass());
System.out.println("Object内容:"+object);
}
}
运行结果:
Objecct类型:class java.lang.String
Object内容:get message
其他
上面只是RabbitMq最基本的使用,有关SpringBoot的更详细的集成和使用以及RabbitMq的Ack机制,将在后续说明.