集成之前首先安装rabbitmq及环境。
1、由于rabbitmq是用erlang语言开发,因此需要erlang环境,安装Erlang环境并配置环境变量(本人是windows环境)
下载地址:https://www.erlang.org/downloads
2、配置环境变量
新建系统变量,变量名为:ERLANG_HOME,变量值为erlang的安装目录,我的为D:\RabbitMq\Erlang\erl10.6,然后找到Path,并将%ERLANG_HOME%/bin加入。
3、下载并安装rabbitmq
下载地址:http://www.rabbitmq.com/download.html
4、安装管理页面插件。
win+R打开命令行,cd到安装路径的sbin目录下,输入:rabbitmq-plugins enable rabbitmq_management,待安装结束,进入到sbin目录下,双击rabbitmq-server.bat,然后打开浏览器输入:http://localhost:15672,默认用户名和密码都为guest,登录即可看到管理页面。
5、核心介绍
rabbitmq不同于其他消息中间件,其核心部件为交换器(exchange),他有好多交换器,本文只对direct exchange和topic exchange做记录(因为我就使用了这两种。。。)
(1)Direct Exchange可以简单理解为点对点发送
(2)Topic Exchange它的路由键和绑定值之间是有规则的,即有通配符来匹配,*代表一个,#代表零个或多个,后面的实例一目了然。
6、编码
消息中间件作为中间者,需要两个角色,需要消息的生产者发送消息到中间件,再由交换器分发至不同的队列,然后由消费者进行消息的接受消费。因此需要创建两个不同的项目分别模拟。因为rabbitmq能够友好的和spring相融,在此使用springboot项目。
Message_producer代表生产者,Message_consumer代表消费者
首先设定一个业务场景:假如在一个项目中能够产生三种日志记录,一种是登录日志,记为log.info,一种为错误日志,记为log.error,还有一种为其他日志,记为log.logs,即记录所有的日志信息,现在想通过消息中间件将这三种不同的日志分别存至不同的日志系统中,该怎么实现呢?接下来,听我慢慢道来。。。
首先添加依赖``
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
其次,在项目application.properties中配置基本信息
#rabbitmq配置
spring.rabbitmq.host=127.0.0.1
spring.rabbitmq.port=5672
spring.rabbitmq.username=guest
spring.rabbitmq.password=guest
由于业务需求,我们在此以Topic Exchange为例来编写代码,这种的搞懂了,Direct Exchange还会难吗?
首先来编写消息生产者的代码:
(1)创建RabbitMq配置类RabbitMqTopicConfig
package com.example.examonlineweb.config;
import org.springframework.amqp.core.Binding;
import org.springframework.amqp.core.BindingBuilder;
import org.springframework.amqp.core.Queue;
import org.springframework.amqp.core.TopicExchange;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* @author caojia
* @time 下午 5:31
* 主题交换器配置类
*/
@Configuration
public class RabbitMqTopicConfig {
/**
* 队列的名字
* @return
*/
@Bean
public Queue logInfoQueue(){
return new Queue("log.info", true);
}
@Bean
public Queue logErrorQueue(){
return new Queue("log.error", true);
}
@Bean
public Queue logsQueue(){
return new Queue("log.logs", true);
}
/**
* 交换器的名字
* @return
*/
@Bean
public TopicExchange getExchange(){
return new TopicExchange("log.topic");
}
/**
* 将队列和路由键绑定
* @return
*/
@Bean
public Binding TestBingding01(){
return BindingBuilder.bind(logInfoQueue()).to(getExchange()).with("*.log.info");
}
@Bean
public Binding TestBingding02(){
return BindingBuilder.bind(logErrorQueue()).to(getExchange()).with("*.log.error");
}
@Bean
public Binding TestBingding03(){
return BindingBuilder.bind(logsQueue()).to(getExchange()).with("*.log.*");
}
}
注意上面的路由键,.log.info匹配的是像user.log.info类型的路由,.log.*匹配则更加灵活,其匹配所有路由键。
配置就是这个简单,然后写一个接口来进行测试
package com.example.examonlineweb.controller.rabbitmq;
import org.springframework.amqp.core.AmqpTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* @author caojia
* @time 下午 4:21
* 消息生产者测试
*/
@RestController
public class TestRabbitMqDirectController {
@Autowired
private AmqpTemplate amqpTemplate;
@RequestMapping("/receive")
public void send(){
//向交换器为log.topic并且路由键匹配为*.log.info的队列发送msg
amqpTemplate.convertAndSend("log.topic","user.log.info","发送用于记录用户日志的消息");
}
}
注意,发送消息的方法已经封装为AmqpTemplate这样的模版,直接使用就行了。
最后,启动生产者项目Message_producer,调用send()方法,然后,启动消费者项目Message_consumer,就可以看到控制台打印出接受到的消息,说明测试成功。
上面是配置类的方式,还可以使用配置文件的方式,道理相同。
在Message_producer和Message_consumer两个项目中分别自定义配置
#rabbitmq配置
spring.rabbitmq.host=127.0.0.1
spring.rabbitmq.port=5672
spring.rabbitmq.username=guest
spring.rabbitmq.password=guest
然后在Message_consumer项目中添加配置
#rabbitmq自定义配置
mq.config.exchange_name=log.topic
mq.config.queue_name.info=log.info
mq.config.queue_name.error=log.error
mq.config.queue_name.logs=log.logs
通过名称可以看到,分别为交换器名称和三个队列的名称。
然后添加处理消息的方法,此处注意,几个特别重要的注解
package com.caojiawangduocongdemo.rabbitmqConfig.configProperties;
import org.springframework.amqp.core.ExchangeTypes;
import org.springframework.amqp.rabbit.annotation.Exchange;
import org.springframework.amqp.rabbit.annotation.Queue;
import org.springframework.amqp.rabbit.annotation.QueueBinding;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Service;
/**
* @author caojia
* @time 下午 9:22
*/
@Configuration
public class LogInfoConsumer {
/**
* 处理消息的方法
* @param msg
*/
@RabbitListener(bindings = @QueueBinding(
value=@Queue(value = "${mq.config.queue_name.info}"),
exchange = @Exchange(value = "${mq.config.exchange_name}",type = ExchangeTypes.TOPIC),
key = "*.log.info"
))
public void process(String msg){
System.out.println("loginfo..."+msg);
}
}
package com.caojiawangduocongdemo.rabbitmqConfig.configProperties;
import org.springframework.amqp.core.ExchangeTypes;
import org.springframework.amqp.rabbit.annotation.Exchange;
import org.springframework.amqp.rabbit.annotation.Queue;
import org.springframework.amqp.rabbit.annotation.QueueBinding;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Service;
/**
* @author caojia
* @time 下午 9:41
* 此方式是配置文件的方式,将rabbit配置信息配置在配置文件
*/
@Configuration
public class LogErrorConsumer {
/**
* 处理消息的方法
* @param msg
*/
@RabbitListener(bindings = @QueueBinding(
value=@Queue(value = "${mq.config.queue_name.error}"),
exchange = @Exchange(value = "${mq.config.exchange_name}",type = ExchangeTypes.TOPIC),
key = "*.log.error"
))
public void process(String msg){
System.out.println("logerror..."+msg);
}
}
package com.caojiawangduocongdemo.rabbitmqConfig.configProperties;
import org.springframework.amqp.core.ExchangeTypes;
import org.springframework.amqp.rabbit.annotation.Exchange;
import org.springframework.amqp.rabbit.annotation.Queue;
import org.springframework.amqp.rabbit.annotation.QueueBinding;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Service;
/**
* @author caojia
* @time 下午 9:41
*/
@Configuration
public class LogsConsumer {
/**
* 处理消息的方法
* @param msg
*/
@RabbitListener(bindings = @QueueBinding(
value=@Queue(value = "${mq.config.queue_name.logs}"),
exchange = @Exchange(value = "${mq.config.exchange_name}",type = ExchangeTypes.TOPIC),
key = "*.log.*"
))
public void process(String msg){
System.out.println("alllogs..."+msg);
}
}
这样消息消费者就简单的完成了,最后,由生产者发消息,在Message_producer项目中方便起见,用测试类来测试,代码如下:
package com.example.examonlineweb;
import com.example.examonlineweb.entity.Permission;
import com.example.examonlineweb.entity.Role;
import com.example.examonlineweb.entity.User;
import com.example.examonlineweb.service.UserService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.amqp.core.AmqpTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import java.util.Set;
/**
* 用户角色权限单元测试
*/
@RunWith(SpringRunner.class)
@SpringBootTest
public class UserServiceImplTest {
@Autowired
private AmqpTemplate amqpTemplate;
@Test
public void msgSend(){
//向交换器为log.topic并且路由键匹配为*.log.info的队列发消息,这样,log.info,log.logs两个队列将会接收到消息。
amqpTemplate.convertAndSend("log.topic", "user.log.info", "发送消息");
}
}
这样,springboot集成rabbitmq就这样完成了,上述就是本人在学习rabbitmq的过程中的笔记,希望可以和大家一起探讨,共同进步,不喜勿喷,感谢!!