iT邦幫忙

2025 iThome 鐵人賽

DAY 3
0
Software Development

事件驅動電力交易平台:Spring Boot 實戰系列 第 3

Day 3|事件驅動實作:從 Order Service 發送 RabbitMQ 訂單事件

  • 分享至 

  • xImage
  •  

在這篇文章中,我將分享我如何在 order-service 中設定 RabbitMQ,並在使用者掛買單時,發送一筆 OrderCreateEvent 到指定的佇列中,作為事件驅動架構的第一步。


RabbitMQ 基礎設定
首先要先在build.gradle中加入spring-boot對於rabbimq的相關依賴用以支援 Spring Boot 自動配置 RabbitMQ 元件與模板

implementation 'org.springframework.boot:spring-boot-starter-amqp'

然後我們必須在程式中配置我們要使用的RabbitMQ相關設定來建立我們傳遞訊息所使用的頻道,在 Spring 框架中,我們不需要手動建立或管理 RabbitMQ 這類元件的生命週期。這是因為 Spring 採用了 控制反轉(IoC) 和 相依注入(DI) 的原則,也就是「你只要說出你要什麼,Spring 就會幫你準備好」。像是這段設定檔:

@Configuration
public class RabbitMQConfig {

  @Bean
  public MessageConverter jsonMessageConverter() {
    return new Jackson2JsonMessageConverter();
  }

  @Bean
  public TopicExchange orderExchange() {
    return new TopicExchange(ORDER_EXCHANGE);
  }

  @Bean
  public Queue orderCreateQueue() {
    return new Queue(ORDER_CREATE_QUEUE);
  }

  @Bean
  public Binding bindOrderCreateQueue(Queue orderCreateQueue, TopicExchange orderExchange) {
    return BindingBuilder.bind(orderCreateQueue).to(orderExchange).with(ORDER_CREATE_ROUTING_KEY);
  }
}

@Configuration 的意思是:這是一個「配置類別」,Spring 啟動時會掃描它,就像掃描設定檔一樣。
@Bean 則表示:這個方法的回傳值要交給 Spring 託管,變成一個可被「注入使用」的元件(也叫 Bean)。
這樣一來,我們就不需要自己 new TopicExchange(...) 或寫連線邏輯,而是透過 @Autowired 把需要的元件直接拿來用,像是自動配對好的插頭一樣。

這段設定的目的是讓 Spring 在啟動時,掃描這些標記後:

  1. 自動建立對應的 Exchange、Queue、Binding 實例
  2. 放入 IoC 容器中供其他地方(如 RabbitTemplate)使用
  3. 無需手動維護 RabbitMQ 連線與配置流程,減少樣板程式碼
    這也讓我的 PlaceBuyOrderService 可以輕鬆注入 RabbitTemplate,並專注在事件本身的內容建構與業務處理,不需要關心任何訊息通道的細節。
    而其中ORDER_CREATE_QUEUE, ORDER_EXCHANGE 這些常數都來自我專案中抽出的共用模組 eap-common 中的 RabbitMQConstants.java,統一管理 Exchange 與 Routing Key 名稱,避免我的微服務在設定時出現錯誤。
    以上的做法不管是使用@configuration或是抽出共用模組,主要目的除了程式碼更乾淨,也讓我們在測試或切換環境時可以更彈性地替換實作,因為你只需要去修改config類別中的內容而不用去調整你全部使用到rabbitmq的相關方法,這就是 Spring 的「鬆耦合設計」帶來的好處。

發送事件:PlaceBuyOrderService
在使用者送出掛買單時,我會組成一筆事件物件並發送到 RabbitMQ:

@Service
public class PlaceBuyOrderService {

  @Autowired
  private RabbitTemplate rabbitTemplate;

  public void execute(PlaceBuyOrderReq request) {
    OrderCreateEvent event = OrderCreateEvent.builder()
        .orderId(UUID.randomUUID())
        .userId(request.getBidder())
        .price(request.getBidPrice())
        .amount(request.getAmount())
        .orderType(OrderType.BUY.name())
        .createdAt(LocalDateTime.now())
        .build();

    rabbitTemplate.convertAndSend(ORDER_EXCHANGE, ORDER_CREATE_ROUTING_KEY, event);
  }
}

同樣的這邊也使用了@Autowired來讓spring生成rabbitTemplate物件來幫我們進行發送事件的功能,有興趣的可以參考spring.doc中對於該物件的詳細方法(RabbitTemplate (Spring AMQP 3.2.6 API)),他類似於JdbcTemplate,hibernate會使用的session一樣目的是讓使用者專注於業務邏輯,而不必煩惱連線、序列化、訊息確認等細節。
這筆事件會被 wallet-service 接收,進行餘額驗證、資產鎖定與扣款處理。


事件物件:OrderCreateEvent

@Data
@Builder
public class OrderCreateEvent {
  private UUID orderId;
  private UUID userId;
  private Integer price;
  private Integer amount;
  private String orderType; // "BUY" or "SELL"
  private LocalDateTime createdAt;
}

我使用 @Builder 與 @Data 等lombok提供的方便方法搭配 Serializable 進行資料建構與傳輸。


這樣就完成了從 REST API → 事件構建 → RabbitMQ 發送的流程。下一篇我將介紹 wallet-service 中如何接收這筆事件並執行資產鎖定邏輯,正式實現事件驅動的第二步!


上一篇
Day 2|從 API 開始設計:OpenAPI 驅動式開發與契約生成實作
下一篇
Day 4|事件驅動實作:Wallet Service 接收訂單事件並執行資產鎖定
系列文
事件驅動電力交易平台:Spring Boot 實戰9
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言