一般来说,消息队列有两种场景,一种是发布/订阅模式,一种是生产者/消费者模式。功能强大的Redis不仅能做缓存支持,在消息队列方面同样支持,而且两种模式都支持。
定义: 生产者消费者模式:生产者生产消息放到队列里,多个消费者同时监听队列,谁先抢到消息谁就会从队列中取走消息;即对于每个消息只能被最多一个消费者拥有。
发布者订阅者模式:发布者生产消息放到队列里,多个监听队列的消费者都会收到同一份消息;即正常情况下每个消费者收到的消息应该都是一样的。
关于Redis其他特点以及功能在这里不再啰嗦,有兴趣的可以自行去研究,下面直接进入此次正题
org.springframework.boot
spring-boot-starter-test
test
org.springframework.boot
spring-boot-starter-web
org.springframework.boot
spring-boot-starter-data-redis
org.redisson
redisson
3.7.0
org.springframework.boot
spring-boot-starter-jta-atomikos
org.mybatis.spring.boot
mybatis-spring-boot-starter
1.1.1
com.alibaba
druid
1.1.10
org.springframework.boot
spring-boot-starter-data-jpa
org.springframework.boot
spring-boot-starter-jdbc
mysql
mysql-connector-java
5.1.40
com.oracle
ojdbc6
11.2.0.3
org.springframework.boot
spring-boot-configuration-processor
true
redis.clients
jedis
2.9.0
com.alibaba
fastjson
1.2.46
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
因为此处为配的是主从库,所以有两个数据源,如果不需要可以直接去掉一个。
@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
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 + '\'' +
'}';
}
}
这里,我将发布者包装成一个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 redisTemplate;
/**
* 发布消息
* @param chanal
* @param mqmessage
*/
@Override
public void sendMessage(String chanal, String mqmessage) {
redisTemplate.convertAndSend(chanal,mqmessage);
}
}
发布消息使用**redisTemplate.convertAndSend(chanal,mqmessage);**此处省略了接口层RedisProducerService 的代码,比较简单,就是一个方法,就不贴出来了。
在开始创建的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;
}
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。
@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,就可看到控制台输出的信息,如下图
如有错误还请指正,毕竟还是只小菜鸟,需要努力飞翔。