SpringBoot+Redis发布订阅模式消息队列的实现
1、前言
一般来说,消息队列有两种场景,一种是发布/订阅模式,一种是生产者/消费者模式。功能强大的Redis不仅能做缓存支持,在消息队列方面同样支持,而且两种模式都支持。
定义: 生产者消费者模式:生产者生产消息放到队列里,多个消费者同时监听队列,谁先抢到消息谁就会从队列中取走消息;即对于每个消息只能被最多一个消费者拥有。
发布者订阅者模式:发布者生产消息放到队列里,多个监听队列的消费者都会收到同一份消息;即正常情况下每个消费者收到的消息应该都是一样的。
关于Redis其他特点以及功能在这里不再啰嗦,有兴趣的可以自行去研究,下面直接进入此次正题
第一步:创建一个SpringBoot工程,引入如下jar包
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!-- 开启web-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- redis-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson</artifactId>
<version>3.7.0</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jta-atomikos</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>1.1.1</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.10</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.40</version>
</dependency>
<dependency>
<groupId>com.oracle</groupId>
<artifactId>ojdbc6</artifactId>
<version>11.2.0.3</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
<!--Jedis-->
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>2.9.0</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.alibaba/fastjson -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.46</version>
</dependency>
</dependencies>
2、修改application.yml配置文件
server:
port: 8080
spring:
redis:
host: xxx.xx.xxx.xxx(自己redis服务ip)
port: 6379
primary:
datasource:
url: jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf-8
username: xxx(数据库用户名)
password: xxxxxx(密码)
driver-class-name: com.mysql.jdbc.Driver
#oracle
# url: jdbc:oracle:thin:@localhost:1521:orcl
# username: xxxx
# password: xxxxxx
# driver-class-name: oracle.jdbc.OracleDriver
# max-idle: 10
# max-wait: 10000
# min-idle: 5
# initial-size: 5
secondary:
datasource:
url: jdbc:mysql://localhost:3306/xxl-kkk?useUnicode=true&characterEncoding=utf-8
username: xxxx
password: xxxxx
driver-class-name: com.mysql.jdbc.Driver
因为此处为配的是主从库,所以有两个数据源,如果不需要可以直接去掉一个。
3、新建一个config,进行Redis相关bean的注入
@SpringBootConfiguration
public class RedisConfig {
@Bean
public StringRedisTemplate getRedisTemplate(RedisConnectionFactory redisConnectionFactory){
return new StringRedisTemplate(redisConnectionFactory);
}
@Bean(name="redisTemplate")
public RedisTemplate<?,?> template(RedisConnectionFactory connectionFactory){
RedisTemplate<?,?> template=new RedisTemplate<>();
template.setKeySerializer(new StringRedisSerializer());
template.setValueSerializer(new JdkSerializationRedisSerializer());
template.setConnectionFactory(connectionFactory);
return template;
}
}
两个Redis实例选一个就好,这里我用的是 RedisTemplate
4、创建一个消息实体
public class Mqmessage implements Serializable {
private String id;
private String content;
public Mqmessage(String id, String content) {
this.id = id;
this.content = content;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
@Override
public String toString() {
return "Mqmessage{" +
"id='" + id + '\'' +
", content='" + content + '\'' +
'}';
}
}
5、创建发布者
这里,我将发布者包装成一个Service,方便后面的测试,当然也可以直接一个main方法也是可以的,我这里图方便,就包装一个service
import java.util.logging.Logger;
@Service
public class RedisProducerServiceImpl implements RedisProducerService {
private static Logger logger= Logger.getLogger(RedisProducerServiceImpl.class.getName());
@Autowired
RedisTemplate<String,String> redisTemplate;
/**
* 发布消息
* @param chanal
* @param mqmessage
*/
@Override
public void sendMessage(String chanal, String mqmessage) {
redisTemplate.convertAndSend(chanal,mqmessage);
}
}
发布消息使用**redisTemplate.convertAndSend(chanal,mqmessage);**此处省略了接口层RedisProducerService 的代码,比较简单,就是一个方法,就不贴出来了。
6、创建监听,当发布者发布消息,就进行监听并消费
在开始创建的config中加入如下bean,
@Bean
public RedisMessageListenerContainer container(RedisConnectionFactory connectionFactory,
MessageListenerAdapter adapter,MessageListenerAdapter adapter1){
RedisMessageListenerContainer container=new RedisMessageListenerContainer();
container.setConnectionFactory(connectionFactory);
container.addMessageListener(adapter,new PatternTopic("wang"));
container.addMessageListener(adapter1,new PatternTopic("cheng"));
return container;
}
@Bean
public MessageListenerAdapter adapter(RedisLinterMeaage meaage){
MessageListenerAdapter messageListenerAdapter=new MessageListenerAdapter(meaage,"redisLinterMeaage");
// messageListenerAdapter.setSerializer(new JdkSerializationRedisSerializer());
return messageListenerAdapter;
}
@Bean
public MessageListenerAdapter adapter1(RedisLinterMeaage meaage){
MessageListenerAdapter messageListenerAdapter=new MessageListenerAdapter(meaage,"redisLinterMeaage1");
// messageListenerAdapter.setSerializer(new JdkSerializationRedisSerializer());
return messageListenerAdapter;
}
7、创建订阅者,并设置两个topic
public class RedisLinterMeaage {
private static Logger logger=Logger.getLogger(RedisLinterMeaage.class.getName());
public void redisLinterMeaage(String jsonMsg){
logger.info("开始消费【wang】的消息");
try{
Mqmessage mqmessage=JSONObject.parseObject(jsonMsg,Mqmessage.class);
System.out.println(mqmessage.getContent()+",,"+mqmessage.getId());
logger.info("消费完毕");
}catch (Exception e){
logger.info("消费失败");
}
}
public void redisLinterMeaage1(String jsonMsg){
logger.info("开始消费【cheng】的消息");
try{
Mqmessage mqmessage=JSONObject.parseObject(jsonMsg,Mqmessage.class);
System.out.println(mqmessage.getContent()+",,"+mqmessage.getId());
logger.info("消费完毕");
}catch (Exception e){
logger.info("消费失败");
}
}
}
在第6步中指定了两个Topic,并写入监听,所以我在订阅者中也加了两个方法,分别对应一个Topic。
8、新建一个Controller,进行消息发布
@RestController
public class HelloWorldController {
@Autowired
private RedisProducerService redisProducerService;
@RequestMapping("/getAllList")
public Object getAllList(){
Mqmessage mqmessage=new Mqmessage("1000","测试的");
Mqmessage mqmessage1=new Mqmessage("10001","成的消息");
try{
redisProducerService.sendMessage("wang", JSON.toJSONString(mqmessage));
redisProducerService.sendMessage("cheng",JSON.toJSONString(mqmessage1));
return “发布消息成功”;
}catch (Exception e){
throw new RuntimeException(e.getMessage());
}
}
}
至此,Redis的消息队列发布订阅模式代码完毕,启动服务,访问localhost:8080/getAllList,就可看到控制台输出的信息,如下图
如有错误还请指正,毕竟还是只小菜鸟,需要努力飞翔。