Redis實戰(4)-資料結構List實戰之佇列特性實現訊息多執行緒 廣播通知
概述:本系列博文所涉及的相關內容來源於debug親自錄製的實戰課程:快取中介軟體Redis技術入門與應用場景實戰(SpringBoot2.x + 搶紅包系統設計與實戰),感興趣的小夥伴可以點選自行前往學習(畢竟以視訊的形式來掌握技術 會更快!) 原文連結:Redis實戰(4)-資料結構List實戰之佇列特性實現訊息多執行緒 廣播通知 ,文章所屬技術專欄:快取中介軟體Redis技術入門與實戰
摘要:電商平臺的管理後端一般有兩大角色的使用者可以使用,一個是系統管理員,一個是平臺的賣家/商家,對於商家而言,管理自個兒的商品是日常工作中再為普通不過的事情了,而對於系統管理員而言,有時候需要釋出一些活動公告通知商家進行報名參加,本文我們將基於List的佇列特性實現公告訊息的廣播通知功能!
內容:在上篇文章中我們介紹了Redis的資料結構~列表List,簡單介紹了其基本特性及其在電商應用後端管理平臺下如何實現“商家”新增商品時的有序儲存,以及如何以有序列表的形式進行展示!(文章連結:Redis實戰(3)-資料結構List實戰一之商品資訊的有序儲存)
在其中,我們給大家展示了列表List在儲存和獲取資料時的流程圖,不曉得大夥兒還記不記得,如下圖所示:
從該圖中可以看出,當我們往Redis的列表List中新增資料時,資料的流動是具有“先進先出”的特性,即所謂的“FIFO”(有點佇列Queue的特性!)的,而且資料是緊湊、一個挨著一個儲存的!
即當我們在往快取Redis的列表List新增資料時,可以採用“LPush 即從左邊的方向新增”的方式往快取Redis的List中新增,然後再採用“LPop 即從左邊的方向彈出資料”或者“RPop 即從右邊的方向彈出資料”的方式獲取這一有序儲存的列表資料!
知道了列表List的資料儲存和讀取流程,其實我們也就幾乎知曉了在實際的專案實戰開發中的程式碼實現了。
下面我們以“電商應用~平臺管理員在平臺釋出活動公告資訊之後,除了將公告資訊塞入資料庫DB之外,同時以LPush的方式將其塞入快取Redis的列表List中,並在介面的另一端開啟定時檢測的方式,隨時檢測快取中指定的列表Redis是否有通告資訊過來,如果有,則採取RPop的方式彈出該公告資訊,並以郵件的形式傳送給商戶!”,如下圖所示:
下面,我們就進入程式碼實戰環節吧!
(1)首先,當然是需要來個“通告資訊表”啦,其完整的DDL(即資料定義語言)如下所示:
CREATE TABLE `notice` ( `id` int(11) NOT NULL AUTO_INCREMENT,`title` varchar(255) CHARACTER SET utf8mb4 DEFAULT NULL COMMENT '通告標題',`content` varchar(255) CHARACTER SET utf8mb4 DEFAULT NULL COMMENT '內容',`is_active` tinyint(4) DEFAULT '1',PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=18 DEFAULT CHARSET=utf8 COMMENT='通告';
(2)然後,當然是需要開發一個Controller啦(上文我們已經開發過了)!在該Controller中我們需要開設一個請求方法,給平臺管理員新增“通告資訊”,該請求方法在接收到公告資訊之後需要將其塞入資料庫DB中,同時也需要往快取Redis的列表List中LPush一條公告資訊,準備被監聽檢測!其完整的原始碼如下所示:
//平臺傳送通知給到各位商戶
@RequestMapping(value = "/notice/put",method = RequestMethod.POST,consumes = MediaType.APPLICATION_JSON_UTF8_VALUE)
public BaseResponse putNotice(@RequestBody @Validated Notice notice,BindingResult result){
String checkRes=ValidatorUtil.checkResult(result);
if (StrUtil.isNotBlank(checkRes)){
return new BaseResponse(StatusCode.Fail.getCode(),checkRes);
}
BaseResponse response=new BaseResponse(StatusCode.Success);
try {
log.info("--平臺傳送通知給到各位商戶:{}",notice);
listService.pushNotice(notice);
}catch (Exception e){
response=new BaseResponse(StatusCode.Fail.getCode(),e.getMessage());
}
return response;
}
(3)緊接著,我們需要開發Controller對應的Service,其職責當然是用來處理真正的業務邏輯,即“新增完成通告資訊”時,它負責將該商品資訊新增進DB資料庫,並新增進快取Redis的列表List中,其完整的原始碼如下所示:
@Autowired
private NoticeMapper noticeMapper;
//建立通告
@Transactional(rollbackFor = Exception.class)
public void pushNotice(Notice notice) throws Exception{
if (notice!=null){
notice.setId(null);
//TODO:將通告資訊塞入資料庫DB中
noticeMapper.insertSelective(notice);
final Integer id=notice.getId();
if (id>0){
//TODO:塞入List列表中(佇列),準備被拉取非同步通知至不同的商戶的郵箱 - applicationEvent&Listener;Rabbitmq;jms
ListOperations<String,Notice> listOperations=redisTemplate.opsForList();
listOperations.leftPush(Constant.RedisListNoticeKey,notice);
}
}
}
(4)之後,我們需要建立一個“定時任務排程器”,用於“近實時”的檢測快取Redis中的列表List是否有通知公告資訊,如果有,則將其RPop取出來,然後採取多執行緒的形式將其傳送給“平臺的商家”,讓他們趕緊報名參加相關的活動!其完整的原始碼如下所示:
/**
* Redis列表-佇列的消費者監聽器
* @Author:debug (SteadyJack)
* @Link: weixin-> debug0868 qq-> 1948831260
* @Date: 2019/10/30 14:51
**/
@Component
@EnableScheduling
public class ListListenerScheduler {
private static final Logger log=
LoggerFactory.getLogger(ListListenerScheduler.class);
private static final String listenKey= Constant.RedisListNoticeKey;
@Autowired
private RedisTemplate redisTemplate;
@Autowired
private UserMapper userMapper;
@Autowired
private EmailService emailService;
//TODO:近實時的定時任務檢測
//@Scheduled(cron = "0/10 * * * * ?")
@Scheduled(cron = "0/59 * * * * ?")
public void schedulerListenNotice(){
log.info("----定時任務排程佇列監聽、檢測通告訊息,監聽list中的資料");
ListOperations<String,Notice> listOperations=redisTemplate.opsForList();
Notice notice=listOperations.rightPop(listenKey);
while (notice!=null){
//TODO:傳送給到所有的商戶的郵箱
this.noticeUser(notice);
notice=listOperations.rightPop(listenKey);
}
}
//TODO:傳送通知給到不同的商戶
@Async("threadPoolTaskExecutor")
private void noticeUser(Notice notice){
if (notice!=null){
//TODO:查詢獲取所有商戶資訊
List<User> list=userMapper.selectList();
//TODO:執行緒池/多執行緒觸發群發郵件
try {
if (list!=null && !list.isEmpty()){
ExecutorService executorService=Executors.newFixedThreadPool(4);
List<NoticeThread> threads= Lists.newLinkedList();
list.forEach(user -> {
threads.add(new NoticeThread(user,notice,emailService));
});
executorService.invokeAll(threads);
}
}catch (Exception e){
log.error("近實時的定時任務檢測-傳送通知給到不同的商戶-法二-執行緒池/多執行緒觸發-發生異常:",e.fillInStackTrace());
}
}
}
}
(5)至此,我們的程式碼實戰就完畢了,最後我們就基於Postman進入測試環節吧,幾張圖加以概括吧:
最後,上一下郵箱看看吧,可以發現確實收到了郵件:
好了,本篇文章我們就介紹到這裡了,建議各位小夥伴一定要照著文章提供的樣例程式碼擼一擼,只有擼過才能知道這玩意是咋用的,否則就成了“空談者”!
對Redis相關技術棧以及實際應用場景實戰感興趣的小夥伴可以前往Debug搭建的技術社群的課程中心進行學習觀看:http://www.fightjava.com/web/index/course/detail/12 !
其他相關的技術,感興趣的小夥伴可以關注底部debug的技術公眾號,或者加debug的微信(debug0868),拉你進“微信版”的真正技術交流群!一起學習、共同成長!
補充:
1、本文涉及到的相關的原始碼可以到此地址,check出來進行檢視學習:https://gitee.com/steadyjack/SpringBootRedis
2、目前debug已將本文所涉及的內容整理錄製成視訊教程,感興趣的小夥伴可以前往觀看學習:http://www.fightjava.com/web/index/course/detail/12
3、關注一下debug的技術微信公眾號,最新的技術文章、課程以及技術專欄將會第一時間在公眾號釋出哦!