通過前面一篇集中式緩存的使用教程,我們已經(jīng)了解了Redis的核心功能:作為K、V存儲的高性能緩存。
接下來我們來看看Redis的一些其他強大用法!
發(fā)布訂閱模式
什么是發(fā)布訂閱模式?
在發(fā)布訂閱模式中有個重要的角色,一個是發(fā)布者Publisher,另一個訂閱者Subscriber。本質(zhì)上來說,發(fā)布訂閱模式就是一種生產(chǎn)者消費者模式,Publisher負責(zé)生產(chǎn)消息,而Subscriber則負責(zé)消費它所訂閱的消息。這種模式被廣泛的應(yīng)用于軟硬件的系統(tǒng)設(shè)計中。比如:配置中心的一個配置修改之后,就是通過發(fā)布訂閱的方式傳遞給訂閱這個配置的訂閱者來實現(xiàn)自動刷新的。
不就是觀察者模式嗎?
看到這里,學(xué)過設(shè)計模式的同學(xué)可能很容易將它與設(shè)計模式中的觀察者模式聯(lián)系起來,你會覺得發(fā)布訂閱模式中的兩個概念與觀察者模式中的兩個概念似乎干的是一樣的事情?所以:Publisher就是觀察者模式中的Subject?Subscriber就是觀察者模式中的Observer?
重要區(qū)別在哪里?
從這兩種模式的角色任務(wù)來說確實是非常相似的,但從實現(xiàn)架構(gòu)上來說有一個核心不同點!
我們通過下面的圖示來理解,就很清晰了:
可以看到這里有一個非常大的區(qū)別就是:發(fā)布訂閱模式在兩個角色中間是一個中間角色來過渡的,發(fā)布者并不直接與訂閱者產(chǎn)生交互。
回想一下生產(chǎn)者消費者模式,這個中間過渡區(qū)域?qū)?yīng)的就是是緩沖區(qū)。因為這個緩沖區(qū)的存在,發(fā)布者與訂閱者的工作就可以實現(xiàn)更大程度的解耦。發(fā)布者不會因為訂閱者處理速度慢,而影響自己的發(fā)布任務(wù),它只需要快速生產(chǎn)即可。而訂閱者也不用太擔(dān)心一時來不及處理,因為有緩沖區(qū)在,可以一點點排隊來完成(也就是我們常說的“削峰填谷”效果)。
而我們所熟知的RabbitMQ、Kafka、RocketMQ這些中間件的本質(zhì)其實就是實現(xiàn)發(fā)布訂閱模式中的這個中間緩沖區(qū)。而Redis也提供了簡單的發(fā)布訂閱實現(xiàn),當(dāng)我們有一些簡單需求的時候,也是可以一用的!如果你已經(jīng)理解了這個概念,那么就進入下一節(jié),一起來做個例子吧!
動手試一試
下面的動手任務(wù),我們將在Spring Boot應(yīng)用中,通過接口的方式實現(xiàn)一個消息發(fā)布者的角色,然后再寫一個Service來實現(xiàn)消息的訂閱(把接口傳過來的消息內(nèi)容打印處理)。
第一步:創(chuàng)建一個基礎(chǔ)的Spring Boot應(yīng)用
第二步:pom.xml
中加入必須的幾個依賴:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
</dependency>
第三步:創(chuàng)建一個接口,用來發(fā)送消息。
@SpringBootApplication
public class Chapter55Application {
private static String CHANNEL = "didispace";
public static void main(String[] args) {
SpringApplication.run(Chapter55Application.class, args);
}
@RestController
static class RedisController {
private RedisTemplate<String, String> redisTemplate;
public RedisController(RedisTemplate<String, String> redisTemplate) {
this.redisTemplate = redisTemplate;
}
@GetMapping("/publish")
public void publish(@RequestParam String message) {
// 發(fā)送消息
redisTemplate.convertAndSend(CHANNEL, message);
}
}
}
這里為了簡單實現(xiàn),公用CHANNEL名稱字段,我都寫在了應(yīng)用主類里。
第四步:繼續(xù)應(yīng)用主類里實現(xiàn)消息訂閱,打接收到的消息打印處理
@Slf4j
@Service
static class MessageSubscriber {
public MessageSubscriber(RedisTemplate redisTemplate) {
RedisConnection redisConnection = redisTemplate.getConnectionFactory().getConnection();
redisConnection.subscribe(new MessageListener() {
@Override
public void onMessage(Message message, byte[] bytes) {
// 收到消息的處理邏輯
log.info("Receive message : " + message);
}
}, CHANNEL.getBytes(StandardCharsets.UTF_8));
}
}
第五步:驗證結(jié)果
- 啟動應(yīng)用Spring Boot主類
- 通過curl或其他工具調(diào)用接口
curl localhost:8080/publish?message=hello
- 觀察控制臺,可以看到打印了收到的message參數(shù)
2021-06-19 16:22:30.935 INFO 34351 --- [ioEventLoop-4-2] .c.Chapter55Application$MessageSubscribe : Receive message : hello
注:本文轉(zhuǎn)載自“程序猿DD”,如有侵權(quán),請聯(lián)系刪除!