最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

redis做服務(wù)間通信工具的項(xiàng)目示例

 更新時(shí)間:2023年08月14日 09:00:04   作者:在下uptown  
Redis是一種高效的服務(wù)間通信工具,它以鍵值對的形式存儲(chǔ)數(shù)據(jù),并支持多種數(shù)據(jù)類型和豐富的操作,本文主要介紹了redis做服務(wù)間通信工具的項(xiàng)目示例,感興趣的可以了解一下

前言

先說一下為什么要有這個(gè)東西,用消息中間件的好處就不用說了,日常開發(fā)中還是有很多場景需要用到消息傳遞的,消息的topic如何管理,如何約束topic,重要的topic消費(fèi)記錄、歷史消息等就是這個(gè)sdk需要做的。

本質(zhì)上只是一層對消息中間件的封裝。這次只是拋磚引玉只引入redis的三種消息類型,pubsub、queue以及stream。

擴(kuò)展其他中間件按著代碼思路一樣。望各路大佬賜教

架構(gòu)設(shè)計(jì)

一個(gè)消息服務(wù)sdk首先需要具備兩個(gè)能力,即生產(chǎn)和消費(fèi),這兩個(gè)功能離不開校驗(yàn)topic合法性,我們姑且簡單點(diǎn)陪在mysql數(shù)據(jù)庫中,但不可能每次校驗(yàn)topic是否合法都要去查詢數(shù)據(jù)庫,這里借鑒kafka存放topic信息的思想,找一個(gè)redis的key存放所有的topic列表。

定義一個(gè)核心service接口。

public?interface?MessageHubService?{
????/**
?????*?生產(chǎn)消息
?????*/
????void?producer(MessageForm?messageForm);
????/**
?????*?消費(fèi)消息
?????*/
????void?consumer(ConsumerAdapterForm?adapterForm);
????/**
?????*?檢查topic、type合法性
?????*/
????void?checkTopic(String?topic,?String?type);
}

方法入?yún)⒔y(tǒng)一使用MessageForm類,里面定義一些基礎(chǔ)的信息,比如哪個(gè)消息topic,哪個(gè)消息類型等等。

@Data
public class MessageForm {
    // 消息組件類型
    private String type;
    // 消息主題
    private String topic;
    private String message = "";
    // 消費(fèi)者組
    private String group = "UPTOWN";
}

自從之前文章中說的文件夾改造之后特別喜歡三層結(jié)構(gòu),即service、baseServiceImpl、customizeServiceImpl。

大體就是service定義接口參數(shù)、返回類型標(biāo)準(zhǔn)化接口,baseServiceImpl實(shí)現(xiàn)service基礎(chǔ)接口實(shí)現(xiàn),做一些統(tǒng)一的攔截處理,比如校驗(yàn)topic合法等操作,customizeServiceImpl屬于具體實(shí)現(xiàn)類extends baseServiceImpl實(shí)現(xiàn)具體邏輯。

topic白名單通過Timer維護(hù),定義一個(gè)Timer通過lua腳本隔一段時(shí)間刷新到redis中。

基礎(chǔ)類baseServiceImpl實(shí)現(xiàn)

@Service
public?class?MessageHubServiceImpl?implements?MessageHubService,?ApplicationContextAware?{
????@Resource
????protected?StringRedisTemplate?stringRedisTemplate;
????public?Map<String,?MessageHubService>?messageHubServiceMap?=?new?ConcurrentHashMap<>();
????private?ApplicationContext?applicationContext;
????@PostConstruct
????public?void?init()?{
????????messageHubServiceMap.put(TopicTypeConstants.REDIS_PUBSUB_TYPE,?applicationContext.getBean(RedisPubSubProcessor.class));
????????messageHubServiceMap.put(TopicTypeConstants.REDIS_STREAM_TYPE,?applicationContext.getBean(RedisQueueProcessor.class));
????????messageHubServiceMap.put(TopicTypeConstants.REDIS_QUEUE_TYPE,?applicationContext.getBean(RedisStreamProcessor.class));
????}
????public?void?checkTopic(String?topic,?String?type)?{
????????if?(!messageHubServiceMap.containsKey(type))?{
????????????throw?new?MatrixException("消息類型不支持");
????????}
????????List<String>?whiteTopicList?=?stringRedisTemplate.opsForList().range(TopicTypeConstants.WHITE_TOPIC,?0,?-1);
????????if?((!ObjectUtils.isEmpty(whiteTopicList)?&&?!whiteTopicList.contains(topic))?||?ObjectUtils.isEmpty(whiteTopicList))?{
????????????throw?new?MatrixException("當(dāng)前topic未配置");
????????}
????}
????@Override
????public?void?producer(MessageForm?messageForm)?{
????????this.checkTopic(messageForm.getTopic(),?messageForm.getType());
????????this.messageHubServiceMap.get(messageForm.getType()).producer(messageForm);
????}
????/**
?????*?消費(fèi)者創(chuàng)建通過注解,已校驗(yàn)topic合法性
?????*/
????@Override
????public?void?consumer(ConsumerAdapterForm?messageForm)?{
????????this.messageHubServiceMap.get(messageForm.getType()).consumer(messageForm);
????}
????@Override
????public?void?setApplicationContext(ApplicationContext?applicationContext)?throws?BeansException?{
????????this.applicationContext?=?applicationContext;
????}
}

具體自定義實(shí)現(xiàn)類

@Scope(proxyMode?=?ScopedProxyMode.TARGET_CLASS)
@Service("redisPubSubProcessor")
public?class?RedisPubSubProcessor?extends?MessageHubServiceImpl?{
????@Override
????public?void?producer(MessageForm?messageForm)?{
????????//?具體生產(chǎn)邏輯
????}
????@Override
????public?void?consumer(ConsumerAdapterForm?messageForm)?{
????????//?具體消費(fèi)邏輯
????}
}

代碼非常清晰了,整體滿足service、baseServiceImpl、customizeServiceImpl三層結(jié)構(gòu)。

生產(chǎn)者邏輯

生產(chǎn)者API做的比較簡單,只是提供一個(gè)API調(diào)用,在調(diào)用前做一些校驗(yàn)工作,僅僅的是一條命令,不做發(fā)送失敗的重試等操作。

消費(fèi)者邏輯

消費(fèi)者的話還是定義一個(gè)注解,還是通過借助SpringBoot生命周期掃描注解的方式在后臺建立常駐線程的方式。

@Slf4j
@Component
public?class?ConsumerConfig?implements?DisposableBean,?SmartInstantiationAwareBeanPostProcessor?{
????@Resource(name?=?"messageHubServiceImpl")
????MessageHubService?messageHubService;
????@Bean(name?=?"redisPubSubConsumerMap")
????public?Map<String,?MessageListenerAdapter>?redisPubSubConsumerMap()?{
????????return?new?ConcurrentHashMap<>();
????}
????@Override
????public?void?destroy()?throws?Exception?{
????}
????@Override
????public?Object?getEarlyBeanReference(Object?bean,?String?beanName)?throws?BeansException?{
????????Method[]?methods?=?ReflectionUtils.getAllDeclaredMethods(bean.getClass());
????????for?(Method?method?:?methods)?{
????????????MessageHub?annotation?=?AnnotationUtils.findAnnotation(method,?MessageHub.class);
????????????if?(annotation?==?null)?{
????????????????continue;
????????????}
????????????String?resolveTopic?=?annotation.topic();
????????????try?{
????????????????messageHubService.checkTopic(resolveTopic,?annotation.type());
????????????}?catch?(Exception?e)?{
????????????????throw?new?Error(e.getMessage());
????????????}
????????????ConsumerAdapterForm?adapterForm?=?new?ConsumerAdapterForm();
????????????adapterForm.setBean(bean);
????????????adapterForm.setInvokeMethod(method);
????????????adapterForm.setTopic(resolveTopic);
????????????adapterForm.setType(annotation.type());
????????????adapterForm.setGroup(annotation.group());
????????????messageHubService.consumer(adapterForm);
????????}
????????return?bean;
????}
}

這里依靠spring生命周期,拿到所有的bean,根據(jù)注解標(biāo)注的方法去走不同的邏輯生成常駐線程,監(jiān)聽到消息之后回調(diào)到標(biāo)注了注解的方法里。
具體的消費(fèi)邏輯就不贅述了,感興趣的可以看下源碼:gitee.com/atuptown/up…
Topic守護(hù)線程

@Slf4j
@Service
public?class?TopicReloadTask?extends?TimerTask?{
????@Resource
????StringRedisTemplate?stringRedisTemplate;
????@Resource
????EntityManager?entityManager;
????public?final?String?TOPIC_SQL?=?"?select?*?from?MESSAGEHUB_TOPIC?";
????public?final?String?LUA_SCRIPT?=
????????????????"redis.call('del',?'MESSAGEHUB_TOPIC')"?+
????????????????"local?topics?=?KEYS?"?+
????????????????"for?i,?v?in?pairs(topics)?do?"?+
????????????????"??redis.call('lpush',?'MESSAGEHUB_TOPIC',?v)?"?+
????????????????"end";
????@Override
????public?void?run()?{
????????try?{
????????????List<String>?topics?=?this.getQueryResult(TOPIC_SQL,?MessageHubTopicBean.class).stream().map(MessageHubTopicBean::getTopic).collect(Collectors.toList());
????????????if?(!ObjectUtils.isEmpty(topics))?{
????????????????DefaultRedisScript<Long>?redisScript?=?new?DefaultRedisScript<>(LUA_SCRIPT,?Long.class);
????????????????Long?result?=?stringRedisTemplate.execute(redisScript,?topics);
????????????????log.info("reload?topic?finish");
????????????}
????????}?catch?(Throwable?t)?{
????????????log.error("messagehub?topic?reload?error",?t);
????????}
????}
????private?<T>?List<T>?getQueryResult(String?sql,?Class<T>?clazz)?{
????????Query?dataQuery?=?entityManager.createNativeQuery(sql,?clazz);
????????List<T>?result?=?new?ArrayList<>();
????????List<Object>?list?=?dataQuery.getResultList();
????????for?(Object?o?:?list)?{
????????????result.add((T)?o);
????????}
????????return?result;
????}
}

定義一個(gè)timer任務(wù),隔一段時(shí)間將mysql中的topic白名單通過lua腳本的方式刷新到指定的reids topic key中。還有一些可以優(yōu)化的地方,比如同步topic的操作只需要一個(gè)服務(wù)即可,所以可以使用@ConditionalOnProperty注解判斷是否需要進(jìn)行同步topic。

git地址:https://gitee.com/atuptown/uptown-messagehub

到此這篇關(guān)于redis做服務(wù)間通信工具的項(xiàng)目示例的文章就介紹到這了,更多相關(guān)redis 服務(wù)間通信 內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • IDEA 卡住buid(編譯)不動(dòng)的解決辦法

    IDEA 卡住buid(編譯)不動(dòng)的解決辦法

    這篇文章主要介紹了IDEA 卡住buid(編譯)不動(dòng)的解決辦法(適用于maven和gradle)及定位思路,本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2020-10-10
  • java網(wǎng)上商城項(xiàng)目第1篇之用戶注冊模塊

    java網(wǎng)上商城項(xiàng)目第1篇之用戶注冊模塊

    這篇文章主要為大家詳細(xì)介紹了java網(wǎng)上商城項(xiàng)目第1篇之用戶注冊模塊,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2016-12-12
  • 詳解SpringBoot如何自定義注解

    詳解SpringBoot如何自定義注解

    注解,也叫元數(shù)據(jù),一種代碼級別的說明,它是JDK1.5及以后版本引入的一個(gè)特性,與類、接口、枚舉是在同一個(gè)層次,本文給大家詳細(xì)介紹了SpringBoot如何自定義注解,文中通過代碼講解的非常詳細(xì),需要的朋友可以參考下
    2024-08-08
  • SpringAOP核心對象的創(chuàng)建圖解

    SpringAOP核心對象的創(chuàng)建圖解

    這篇文章主要介紹了SpringAOP核心對象的創(chuàng)建詳解,通過使用AOP,我們可以將橫切關(guān)注點(diǎn)(如日志記錄、性能監(jiān)控、事務(wù)管理等)從業(yè)務(wù)邏輯中分離出來,使得代碼更加模塊化、可維護(hù)性更高,需要的朋友可以參考下
    2023-10-10
  • SpringBoot+BootStrap多文件上傳到本地實(shí)例

    SpringBoot+BootStrap多文件上傳到本地實(shí)例

    這篇文章主要介紹了SpringBoot+BootStrap多文件上傳到本地實(shí)例,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-03-03
  • SpringBoot如何訪問本地圖片

    SpringBoot如何訪問本地圖片

    這篇文章主要介紹了SpringBoot如何訪問本地圖片問題,具有很好的參考價(jià)值,希望對大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2024-08-08
  • Idea跑的項(xiàng)目沒問題將程序install成jar包運(yùn)行報(bào)錯(cuò)空指針的問題

    Idea跑的項(xiàng)目沒問題將程序install成jar包運(yùn)行報(bào)錯(cuò)空指針的問題

    這篇文章主要介紹了Idea跑的項(xiàng)目沒問題,將程序install成jar包運(yùn)行報(bào)錯(cuò)空指針的問題,本文通過圖文并茂的形式給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2020-06-06
  • JDK23中instanceof的使用小結(jié)

    JDK23中instanceof的使用小結(jié)

    JDK23新增JEP455預(yù)覽特性,使instanceof支持所有原始類型,但需手動(dòng)開啟預(yù)覽屬性,下面就來具體介紹一下,感興趣的可以了解一下
    2025-08-08
  • mysql數(shù)據(jù)庫忘記密碼時(shí)如何修改

    mysql數(shù)據(jù)庫忘記密碼時(shí)如何修改

    本文主要介紹了mysql數(shù)據(jù)庫忘記密碼時(shí)如何修改的步驟方法,具有很好的參考價(jià)值,下面跟著小編一起來看下吧
    2017-02-02
  • Java后臺處理Json格式數(shù)據(jù)的方法

    Java后臺處理Json格式數(shù)據(jù)的方法

    這篇文章主要介紹了Java后臺處理Json格式數(shù)據(jù)的方法的相關(guān)資料,非常不錯(cuò)具有參考借鑒價(jià)值,需要的朋友可以參考下
    2016-06-06

最新評論

和龙市| 榕江县| 漳州市| 象州县| 措美县| 嘉义市| 西丰县| 曲阳县| 凉城县| 上栗县| 亳州市| 光山县| 商丘市| 岳阳县| 乐业县| 东乌珠穆沁旗| 道孚县| 内江市| 和平区| 米林县| 临武县| 贵南县| 永定县| 广西| 湖北省| 环江| 富平县| 闸北区| 泉州市| 钦州市| 西平县| 垣曲县| 颍上县| 宜川县| 吉林市| 揭西县| 三江| 绍兴市| 蓬溪县| 霍城县| 禹城市|