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

SpringBoot實(shí)現(xiàn)發(fā)布/訂閱廣播消息的示例代碼

 更新時間:2026年05月28日 08:42:16   作者:希望永不加班  
在 RabbitMQ 的五大工作模式中,發(fā)布/訂閱(Publish/Subscribe)廣播模式是分布式系統(tǒng)中非常核心的通信方式,本文給大家介紹了SpringBoot實(shí)現(xiàn)發(fā)布/訂閱廣播消息的示例代碼,需要的朋友可以參考下

引言

在 RabbitMQ 的五大工作模式中,發(fā)布/訂閱(Publish/Subscribe)廣播模式是分布式系統(tǒng)中非常核心的通信方式。

我們?nèi)粘J褂玫钠胀c(diǎn)對點(diǎn)隊(duì)列,一條消息只會被一個消費(fèi)者消費(fèi)(競爭消費(fèi));而廣播模式可以實(shí)現(xiàn)一條消息、多服務(wù)、多消費(fèi)者同時接收,完美實(shí)現(xiàn)一對多通知。

像 緩存刷新、配置更新、全局通知、多節(jié)點(diǎn)日志同步、服務(wù)狀態(tài)廣播 等場景,全部依賴 Fanout 廣播模式。

一、什么是 MQ 廣播(發(fā)布/訂閱)模式?

1. 核心定義

廣播模式基于 FanoutExchange(扇形交換機(jī)) 實(shí)現(xiàn),核心邏輯:

生產(chǎn)者發(fā)送一條消息到 Fanout 交換機(jī),所有綁定該交換機(jī)的隊(duì)列,都會完整收到這條消息。

不管路由鍵是什么、不管隊(duì)列名稱,只要完成綁定,就會無條件廣播投遞。

2. 核心特性

  • 無視routingKey,路由鍵傳空、傳任意值都不生效
  • 純廣播、全量投遞、一對多分發(fā)
  • 每條消息獨(dú)立進(jìn)入每一個綁定隊(duì)列
  • 天然支持多服務(wù)、多節(jié)點(diǎn)同步通知
  • 無匹配規(guī)則,綁定即接收

3. 適用業(yè)務(wù)場景

  • 分布式緩存全局刷新(多節(jié)點(diǎn)統(tǒng)一清空緩存)
  • 系統(tǒng)配置動態(tài)推送、熱更新
  • 全站公告、全局消息推送
  • 微服務(wù)多節(jié)點(diǎn)日志采集、鏈路追蹤
  • 服務(wù)上下線、狀態(tài)同步廣播
  • 多端消息同步(PC/APP/小程序)

二、四大交換機(jī)模式核心對比

交換機(jī)類型匹配規(guī)則消費(fèi)模式核心場景
Direct(直連)完全匹配 routingKey點(diǎn)對點(diǎn)競爭消費(fèi)訂單、支付、任務(wù)處理
Topic(主題)通配符模糊匹配選擇性多消費(fèi)日志分級、消息訂閱
Fanout(廣播)無視路由鍵,全部投遞全員訂閱消費(fèi)緩存刷新、全局通知
Headers匹配消息頭參數(shù)自定義匹配極少使用

三、關(guān)鍵認(rèn)知誤區(qū)

1:同一個隊(duì)列多消費(fèi)者可以實(shí)現(xiàn)廣播

絕對錯誤!

同一個隊(duì)列下的多個消費(fèi)者,默認(rèn)是競爭消費(fèi),一條消息只會被一個消費(fèi)者消費(fèi)。

廣播必備條件:每個消費(fèi)者對應(yīng)一個獨(dú)立隊(duì)列,全部綁定同一個 Fanout 交換機(jī)。

2:Fanout 交換機(jī)需要配置路由鍵

Fanout 交換機(jī)底層邏輯直接忽略 routingKey,無論發(fā)送時傳什么值,都不會影響廣播效果。

3:廣播消息天然可靠、不會丟失

默認(rèn)非持久化、自動ACK 場景下,廣播消息極易丟失,生產(chǎn)必須做持久化+手動ACK

四、SpringBoot 完整實(shí)現(xiàn)

1. 基礎(chǔ)依賴

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-amqp</artifactId>
</dependency>

2. 生產(chǎn)級配置文件

spring:
  rabbitmq:
    host: 127.0.0.1
    port: 5672
    username: guest
    password: guest
    virtual-host: /
    listener:
      simple:
        # 手動ACK 保證廣播消息不丟
        acknowledge-mode: manual
        # 限制預(yù)取數(shù),防止單節(jié)點(diǎn)消息堆積
        prefetch: 5
        # 開啟消費(fèi)重試
        retry:
          enabled: true
          max-attempts: 3
          initial-interval: 1000

3. 廣播交換機(jī)、隊(duì)列、綁定配置類

import org.springframework.amqp.core.Binding;
import org.springframework.amqp.core.BindingBuilder;
import org.springframework.amqp.core.FanoutExchange;
import org.springframework.amqp.core.Queue;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class FanoutBroadcastConfig {
    // 廣播交換機(jī)名稱
    public static final String FANOUT_EXCHANGE = "system.fanout.broadcast.exchange";
    // 三個獨(dú)立消費(fèi)者隊(duì)列
    public static final String QUEUE_CACHE_REFRESH = "queue.cache.refresh";
    public static final String QUEUE_NOTICE = "queue.system.notice";
    public static final String QUEUE_LOG = "queue.log.collect";
    // 聲明 Fanout 廣播交換機(jī):持久化、不自動刪除
    @Bean
    public FanoutExchange fanoutExchange() {
        return new FanoutExchange(FANOUT_EXCHANGE, true, false);
    }
    // 隊(duì)列1:緩存刷新隊(duì)列
    @Bean
    public Queue cacheRefreshQueue() {
        return new Queue(QUEUE_CACHE_REFRESH, true);
    }
    // 隊(duì)列2:系統(tǒng)通知隊(duì)列
    @Bean
    public Queue noticeQueue() {
        return new Queue(QUEUE_NOTICE, true);
    }
    // 隊(duì)列3:日志采集隊(duì)列
    @Bean
    public Queue logQueue() {
        return new Queue(QUEUE_LOG, true);
    }
    // 全部綁定到廣播交換機(jī)
    @Bean
    public Binding bindingCacheRefresh(Queue cacheRefreshQueue, FanoutExchange fanoutExchange) {
        return BindingBuilder.bind(cacheRefreshQueue).to(fanoutExchange);
    }
    @Bean
    public Binding bindingNotice(Queue noticeQueue, FanoutExchange fanoutExchange) {
        return BindingBuilder.bind(noticeQueue).to(fanoutExchange);
    }
    @Bean
    public Binding bindingLog(Queue logQueue, FanoutExchange fanoutExchange) {
        return BindingBuilder.bind(logQueue).to(fanoutExchange);
    }
}

4. 廣播消息生產(chǎn)者

import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class BroadcastProducer {
    @Autowired
    private RabbitTemplate rabbitTemplate;
    @GetMapping("/send/broadcast")
    public String sendBroadcastMsg(@RequestParam String content) {
        // Fanout廣播:路由鍵傳空字符串
        rabbitTemplate.convertAndSend(
                FanoutBroadcastConfig.FANOUT_EXCHANGE,
                "",
                content
        );
        return "? 廣播消息發(fā)送成功:" + content;
    }
}

5. 多消費(fèi)者實(shí)現(xiàn)(全員接收)

消費(fèi)者1:緩存刷新消費(fèi)者

import com.rabbitmq.client.Channel;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;
import java.io.IOException;
@Component
public class CacheRefreshConsumer {
    @RabbitListener(queues = FanoutBroadcastConfig.QUEUE_CACHE_REFRESH)
    public void consume(String msg, Message message, Channel channel) throws IOException {
        try {
            System.out.println("【緩存服務(wù)】接收廣播消息:" + msg);
            // 執(zhí)行緩存刷新業(yè)務(wù)邏輯
            channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
        } catch (Exception e) {
            // 消費(fèi)失敗,重回隊(duì)列重試
            channel.basicNack(message.getMessageProperties().getDeliveryTag(), false, true);
        }
    }
}

消費(fèi)者2:系統(tǒng)通知消費(fèi)者

@Component
public class SystemNoticeConsumer {
    @RabbitListener(queues = FanoutBroadcastConfig.QUEUE_NOTICE)
    public void consume(String msg, Message message, Channel channel) throws IOException {
        try {
            System.out.println("【通知服務(wù)】接收廣播消息:" + msg);
            // 執(zhí)行消息推送業(yè)務(wù)
            channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
        } catch (Exception e) {
            channel.basicNack(message.getMessageProperties().getDeliveryTag(), false, true);
        }
    }
}

消費(fèi)者3:日志采集消費(fèi)者

@Component
public class LogCollectConsumer {
    @RabbitListener(queues = FanoutBroadcastConfig.QUEUE_LOG)
    public void consume(String msg, Message message, Channel channel) throws IOException {
        try {
            System.out.println("【日志服務(wù)】接收廣播消息:" + msg);
            // 執(zhí)行日志采集業(yè)務(wù)
            channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
        } catch (Exception e) {
            channel.basicNack(message.getMessageProperties().getDeliveryTag(), false, true);
        }
    }
}

五、測試效果

訪問接口:

http://localhost:8080/send/broadcast?content=全局緩存刷新通知

控制臺輸出:

【緩存服務(wù)】接收廣播消息:全局緩存刷新通知
【通知服務(wù)】接收廣播消息:全局緩存刷新通知
【日志服務(wù)】接收廣播消息:全局緩存刷新通知

一條消息,多服務(wù)同時消費(fèi),廣播生效!

六、總結(jié)

1. 必須開啟持久化

交換機(jī)、隊(duì)列全部設(shè)置持久化,防止重啟丟失廣播配置。

2. 強(qiáng)制手動ACK

廣播場景多為重要通知、緩存同步,自動ACK會導(dǎo)致業(yè)務(wù)未執(zhí)行完成消息丟失。

3. 每個服務(wù)獨(dú)立隊(duì)列

不同微服務(wù)必須使用獨(dú)立隊(duì)列,避免競爭消費(fèi),保證廣播全覆蓋。

4. 廣播消息建議做冪等

MQ 重試、網(wǎng)絡(luò)抖動會導(dǎo)致廣播消息重復(fù)推送,核心業(yè)務(wù)必須基于消息ID做冪等防重。

5. 禁止設(shè)置復(fù)雜路由鍵

Fanout 無視路由鍵,統(tǒng)一傳空字符串,保持代碼規(guī)范。

寫在最后

廣播發(fā)布訂閱模式是微服務(wù)分布式通信的重要基石,區(qū)別于傳統(tǒng)的點(diǎn)對點(diǎn)任務(wù)消費(fèi),它主打全局通知、多節(jié)點(diǎn)同步、狀態(tài)廣播,是緩存刷新、配置熱更新、系統(tǒng)公告等場景的最優(yōu)解。

很多開發(fā)者一直混淆“競爭消費(fèi)”和“廣播消費(fèi)”的本質(zhì),導(dǎo)致線上通知不全、同步失效等隱性問題。吃透 Fanout 交換機(jī)的底層原理與落地規(guī)范,能幫你徹底解決分布式多節(jié)點(diǎn)同步難題。

以上就是SpringBoot實(shí)現(xiàn)發(fā)布/訂閱廣播消息的示例代碼的詳細(xì)內(nèi)容,更多關(guān)于SpringBoot發(fā)布/訂閱廣播消息的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • Apache?Commons?Imaging處理圖像實(shí)例深究

    Apache?Commons?Imaging處理圖像實(shí)例深究

    這篇文章主要為大家介紹了Apache?Commons?Imaging處理圖像的實(shí)例探索深究,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-12-12
  • Java中的Callable實(shí)現(xiàn)多線程詳解

    Java中的Callable實(shí)現(xiàn)多線程詳解

    這篇文章主要介紹了Java中的Callable實(shí)現(xiàn)多線程詳解,接口Callable中有一個call方法,其返回值類型為V,這是一個泛型,值得關(guān)注的是這個call方法有返回值,這意味著線程執(zhí)行完畢后可以將處理結(jié)果返回,需要的朋友可以參考下
    2023-08-08
  • java中新生代和老生代的關(guān)系說明

    java中新生代和老生代的關(guān)系說明

    這篇文章主要介紹了java中新生代和老生代的關(guān)系說明,具有很好的參考價(jià)值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2025-07-07
  • java8 對象轉(zhuǎn)Map時重復(fù) key Duplicate key xxxx的解決

    java8 對象轉(zhuǎn)Map時重復(fù) key Duplicate key xxxx的解決

    這篇文章主要介紹了java8 對象轉(zhuǎn)Map時重復(fù) key Duplicate key xxxx的解決方案,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-09-09
  • 淺談java實(shí)現(xiàn)背包算法(0-1背包問題)

    淺談java實(shí)現(xiàn)背包算法(0-1背包問題)

    本篇文章主要介紹了淺談java實(shí)現(xiàn)背包算法(0-1背包問題) ,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2017-08-08
  • Java實(shí)現(xiàn)字符串切割的方法詳解

    Java實(shí)現(xiàn)字符串切割的方法詳解

    這篇文章主要為大家介紹了一些Java中切割字符串的小技巧,可以把性能提升5~10倍。文中的示例代碼講解詳細(xì),快跟隨小編一起學(xué)習(xí)一下
    2022-03-03
  • java解析xml常用的幾種方式總結(jié)

    java解析xml常用的幾種方式總結(jié)

    這篇文章主要介紹了java解析xml常用的幾種方式總結(jié),有需要的朋友可以參考一下
    2013-11-11
  • java中json-diff簡單使用及對象是否一致詳解

    java中json-diff簡單使用及對象是否一致詳解

    這篇文章主要為大家介紹了java中json-diff簡單使用及對象是否一致對比詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-03-03
  • SpringBoot中的PropertySource原理詳解

    SpringBoot中的PropertySource原理詳解

    這篇文章主要介紹了SpringBoot中的PropertySource原理詳解,PropertySource?是一個非常重要的概念,它允許您在應(yīng)用程序中定義屬性,并將這些屬性注入到?Spring?環(huán)境中,需要的朋友可以參考下
    2023-07-07
  • java代理 jdk動態(tài)代理應(yīng)用案列

    java代理 jdk動態(tài)代理應(yīng)用案列

    java代理有jdk動態(tài)代理、cglib代理,這里只說下jdk動態(tài)代理,jdk動態(tài)代理主要使用的是java反射機(jī)制,需要了解的朋友可以參考下
    2012-11-11

最新評論

随州市| 惠安县| 蕉岭县| 汝阳县| 水富县| 平南县| 依安县| 湾仔区| 彭阳县| 望江县| 大竹县| 营口市| 左贡县| 洛阳市| 竹溪县| 祁东县| 云浮市| 商都县| 阜阳市| 诏安县| 虹口区| 长汀县| 忻州市| 巴林左旗| 垫江县| 柞水县| 青川县| 呼图壁县| 双辽市| 白朗县| 江安县| 九江市| 黎城县| 湄潭县| 紫金县| 三河市| 阳东县| 丰宁| 安龙县| 左云县| 长顺县|