RabbitMQ是由erlang语言开发,基于AMQP(Advanced Message Queue 高级消息队列协议)协议实现的消息队列,它是一种应用程序之间的通信方法,消息队列在分布式系统开发中应用非常广泛。
MQ全称为Message Queue,即消息队列。“消息队列”是在消息的传输过程中保存消息的容器。它是典型的:生产者、消费者模型。生产者不断向消息队列中生产消息,消费者不断的从队列中获取消息。因为消息的生产和消费都是异步的,而且只关心消息的发送和接收,没有业务逻辑的侵入,这样就实现了生产者和消费者的解耦。
使用消息队列,异步处理请求,将不必同步处理且耗时的操作,交由消息队列,并通知消息接收方进行异步处理,减少了应用程序的响应时间。解决高并发环境下,大量的同步请求堵塞。
生产方通过MQ与消费方交互,不必在服务器内部提供接口,解耦应用程序。
JMS是定义了统一的接口,来对消息操作进行统一;AMQP是通过规定协议来统一数据交互的格式
JMS限定了必须使用Java语言;AMQP只是协议,不规定实现方式,因此是跨语言的。
JMS规定了两种消息模型;而AMQP的消息模型更加丰富
组成部分:
生产者将消息发送到交换器;队列根据routing_key接收消息,消费者消费queue接收到的消息。生产者也可以直接将消息发送给queue,而不设定Exchange以及路由键;
生产者将消息发送到交换器;队列接收所有消息,消费者消费指定queue接收到的消息;
生产者将消息发送到交换器;队列根据routing_key接收消息,没有对应的routing_key则消息丢失;消费者消费queue接收到的消息;
生产者将带有header消息发送到交换器;队列根据交换机接收消息,并根据header消息与队列匹配;消费者消费queue接收到的消息;
简要说一下,rabbitmq安装在阿里云服务器docker容器中的连接问题:
需要在阿里云安全组里配置5672/15672端口 并且在容器中安装rabbitmq的需要对5672以及15672端口映射,这样外访的浏览器才可以通过映射的端口访问到docker容器中rabbitmq。
docker安装rabbitmq配置端口的示例:
docker run -d --hostname my-rabbit --name some-rabbit -p 8080:15672 -p 5672:5672 rabbitmq:3-management
浏览器登录web界面:http://ip地址:8080/
idea中配置连接:用5672端口 并且需要web页面里新添加一个root用户,tag为administrator,virtual-host设置为 /
原因是:默认的guest用户,只允许loclhost的地址访问,不允许外访
spring.rabbitmq.host=ip地址
spring.rabbitmq.port=5672
spring.rabbitmq.username=root
spring.rabbitmq.password=root
spring.rabbitmq.virtual-host=/
配置消息队列与交换机
@Configuration
public class DirectConfig {
//消息队列
@Bean
public Queue directQueue(){
return new Queue("soul_direct");
}
//交换机
@Bean
public DirectExchange directExchange(){
return new DirectExchange("soul_directExchange",true,false);
}
//队列绑定
@Bean
public Binding directBinding(){
return BindingBuilder.bind(directQueue()).to(directExchange()).with("soul_direct_routingKey");
}
}
消息接收
@Component
public class MessageReceive {
@RabbitListener(queues = "soul_direct")
public void directReceive(String mes){
System.out.println("\n");
System.out.println("\n");
System.out.println("mes=" +mes);
System.out.println("\n");
System.out.println("\n");
}
}
测试:
@Autowired
RabbitTemplate rabbitTemplate;
@Test
void contextLoads() {
//直接发给队列
rabbitTemplate.convertAndSend("soul_direct","hello! direct");
}
@Test
void test1() {
//交给交换机路由给队列
rabbitTemplate.convertAndSend("soul_directExchange","soul_direct_routingKey","hello! direct1");
}
//测试结果:
mes=hello! direct
-------------------------------------------------
mes=hello! direct1
2、fanout模式(广播到两个队列输出)
@Configuration
public class FanoutConfig {
//队列
@Bean
public Queue fanoutQueueOne(){
return new Queue("fanout_queueOne");
}
@Bean
public Queue fanoutQueueTwo(){
return new Queue("fanout_queueTwo");
}
//交换机
@Bean
public FanoutExchange fanoutExchange(){
return new FanoutExchange("soul_fanoutExchange",true,false);
}
//队列绑定交换机 不需要添加路由键
@Bean
public Binding bindingOne(){
return BindingBuilder.bind(fanoutQueueOne()).to(fanoutExchange());
}
@Bean
public Binding bindingTwo(){
return BindingBuilder.bind(fanoutQueueTwo()).to(fanoutExchange());
}
}
@RabbitListener(queues = "fanout_queueOne")
public void fanoutReceiveOne(String mes){
System.out.println("\n");
System.out.println("\n");
System.out.println("fanout_queueOne:mes=" +mes);
System.out.println("\n");
System.out.println("\n");
}
@RabbitListener(queues = "fanout_queueTwo")
public void fanoutReceiveTwo(String mes){
System.out.println("\n");
System.out.println("\n");
System.out.println("fanout_queueTwo:mes=" +mes);
System.out.println("\n");
System.out.println("\n");
}
测试:
@Test
void test2() {
//交给交换机处理
rabbitTemplate.convertAndSend("soul_fanoutExchange",null,"Live like a dog");
}
//测试结果
fanout_queueTwo:mes=Live like a dog
fanout_queueOne:mes=Live like a dog
3、topic(订阅 匹配)
@Configuration
public class TopicConfig {
@Bean
public Queue queue1(){
return new Queue("dog1");
}
@Bean
public Queue queue2(){
return new Queue("dog2");
}
@Bean
public Queue queue3(){
return new Queue("dog3");
}
@Bean
public TopicExchange topicExchange(){
return new TopicExchange("soul_topicExchange");
}
@Bean
public Binding binding(){
return BindingBuilder.bind(queue1()).to(topicExchange()).with("dog1.#");
}
@Bean
public Binding binding1(){
return BindingBuilder.bind(queue2()).to(topicExchange()).with("dog2.#");
}
@Bean
public Binding binding2(){
return BindingBuilder.bind(queue3()).to(topicExchange()).with("#.dog3.#");
}
}
@RabbitListener(queues = "dog1")
public void topic1(String mes){
System.out.println("dog1:mes=" +mes);
}
@RabbitListener(queues = "dog2")
public void topic2(String mes){
System.out.println("dog2:mes=" +mes);
}
@RabbitListener(queues = "dog3")
public void topic3(String mes){
System.out.println("dog3:mes=" +mes);
}
测试:
@Test
void test3() {
//交给交换机处理
rabbitTemplate.convertAndSend("soul_topicExchange","dog1.dog3","Live like a dog");
}
//结果
dog3:mes=Live like a dog
dog1:mes=Live like a dog
4、header
@Configuration
public class HeaderConfig {
@Bean
HeadersExchange headersExchange() {
return new HeadersExchange("soul_headersExchange", true, false);
}
@Bean
Queue queueName() {
return new Queue("name-queue");
}
@Bean
Queue queueAge() {
return new Queue("age-queue");
}
@Bean
Binding bindingName() {
Map map = new HashMap<>();
map.put("name", "Live like a dog");
return BindingBuilder.bind(queueName()).to(headersExchange()).whereAny(map).match();
}
//whereAny和where是用来匹配的
@Bean
Binding bindingAge() {
return BindingBuilder.bind(queueAge()).to(headersExchange()).where("age").exists();
}
}
@RabbitListener(queues = "name-queue")
public void header1(String mes){
System.out.println("name-queue:mes=" +mes);
}
@RabbitListener(queues = "age-queue")
public void header2(String mes){
System.out.println("age-queue:mes=" +mes);
}
测试:
@Test
void test4() {
Message message = MessageBuilder.withBody("Live like a dog".getBytes()).setHeader("name","Live like a dog").build();
//交给交换机处理
rabbitTemplate.convertAndSend("soul_headersExchange","dog1.dog3",message);
}
//结果:
name-queue:mes=Live like a dog