springcloud(九):配置中心和消息總線(配置中心終結版)

Spring Cloud Bus
Spring cloud bus通過輕量消息代理連接各個分佈的節點。這會用在廣播狀態的變化(例如配置變化)或者其他的消息指令。Spring bus的一個核心思想是通過分佈式的啓動器對spring boot應用進行擴展,也可以用來建立一個多個應用之間的通信頻道。目前唯一實現的方式是用AMQP消息代理作爲通道,同樣特性的設置(有些取決於通道的設置)在更多通道的文檔中。

Spring cloud bus被國內很多都翻譯爲消息總線,也挺形象的。大家可以將它理解爲管理和傳播所有分佈式項目中的消息既可,其實本質是利用了MQ的廣播機制在分佈式的系統中傳播消息,目前常用的有Kafka和RabbitMQ。利用bus的機制可以做很多的事情,其中配置中心客戶端刷新就是典型的應用場景之一,我們用一張圖來描述bus在配置中心使用的機制。

根據此圖我們可以看出利用Spring Cloud Bus做配置更新的步驟:

1、提交代碼觸發post給客戶端A發送bus/refresh
2、客戶端A接收到請求從Server端更新配置並且發送給Spring Cloud Bus
3、Spring Cloud bus接到消息並通知給其它客戶端
4、其它客戶端接收到通知,請求Server端獲取最新配置
5、全部客戶端均獲取到最新的配置
改進版本
在上面的流程中,我們已經到達了利用消息總線觸發一個客戶端bus/refresh,而刷新所有客戶端的配置的目的。但這種方式並不優雅。原因如下:

打破了微服務的職責單一性。微服務本身是業務模塊,它本不應該承擔配置刷新的職責。
破壞了微服務各節點的對等性。
有一定的侷限性。例如,微服務在遷移時,它的網絡地址常常會發生變化,此時如果想要做到自動刷新,那就不得不修改WebHook的配置。
這時Spring Cloud Bus做配置更新步驟如下:

1、提交代碼觸發post請求給bus/refresh
2、server端接收到請求併發送給Spring Cloud Bus
3、Spring Cloud bus接到消息並通知給其它客戶端
4、其它客戶端接收到通知,請求Server端獲取最新配置
5、全部客戶端均獲取到最新的配置
這樣的話我們在server端的代碼做一些改動,來支持bus/refresh

1、添加依賴

<dependencies>
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-config-server</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-bus-amqp</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-eureka</artifactId>
    </dependency>
</dependencies>

需要多引入spring-cloud-starter-bus-amqp包,增加對消息總線的支持

2、配置文件

server:
  port: 8001
spring:
  application:
    name: spring-cloud-config-server
  cloud:
    config:
      server:
        git:
          uri: https://github.com/ityouknow/spring-cloud-starter/     # 配置git倉庫的地址
          search-paths: config-repo                             # git倉庫地址下的相對地址,可以配置多個,用,分割。
          username: username                                        # git倉庫的賬號
          password: password                                    # git倉庫的密碼
  rabbitmq:
    host: 192.168.0.6
    port: 5672
    username: admin
    password: 123456
eureka:
  client:
    serviceUrl:
      defaultZone: http://localhost:8000/eureka/   ## 註冊中心eurka地址
management:
  security:
     enabled: false

配置文件增加RebbitMq的相關配置,關閉安全驗證。這樣server端代碼就改造完成了。

3、測試
依次啓動spring-cloud-eureka、spring-cloud-config-server、spring-cloud-config-client項目,改動spring-cloud-config-client項目端口爲8003、8004依次啓動。測試環境準備完成。

按照上面的測試方式,訪問server端和三個客戶端測試均可以正確返回信息。同樣修改neo-config-dev.properties中neo.hello的值爲hello im dev update並提交到代碼庫中。在win下使用下面命令來模擬webhook觸發server端bus/refresh.

curl -X POST http://localhost:8001/bus/refresh

其它
局部刷新
某些場景下(例如灰度發佈),我們可能只想刷新部分微服務的配置,此時可通過/bus/refresh端點的destination參數來定位要刷新的應用程序。

例如:/bus/refresh?destination=customers:8000,這樣消息總線上的微服務實例就會根據destination參數的值來判斷是否需要要刷新。其中,customers:8000指的是各個微服務的ApplicationContext ID。

destination參數也可以用來定位特定的微服務。例如:/bus/refresh?destination=customers:**,這樣就可以觸發customers微服務所有實例的配置刷新。

跟蹤總線事件
一些場景下,我們可能希望知道Spring Cloud Bus事件傳播的細節。此時,我們可以跟蹤總線事件(RemoteApplicationEvent的子類都是總線事件)。

跟蹤總線事件非常簡單,只需設置spring.cloud.bus.trace.enabled=true,這樣在/bus/refresh端點被請求後,訪問/trace端點就可獲得類似如下的結果:

{
  "timestamp": 1495851419032,
  "info": {
    "signal": "spring.cloud.bus.ack",
    "type": "RefreshRemoteApplicationEvent",
    "id": "c4d374b7-58ea-4928-a312-31984def293b",
    "origin": "stores:8002",
    "destination": "*:**"
  }
  },
  {
  "timestamp": 1495851419033,
  "info": {
    "signal": "spring.cloud.bus.sent",
    "type": "RefreshRemoteApplicationEvent",
    "id": "c4d374b7-58ea-4928-a312-31984def293b",
    "origin": "spring-cloud-config-client:8001",
    "destination": "*:**"
  }
  },
  {
  "timestamp": 1495851422175,
  "info": {
    "signal": "spring.cloud.bus.ack",
    "type": "RefreshRemoteApplicationEvent",
    "id": "c4d374b7-58ea-4928-a312-31984def293b",
    "origin": "customers:8001",
    "destination": "*:**"
  }
}

這個日誌顯示了customers:8001發出了RefreshRemoteApplicationEvent事件,廣播給所有的服務,被customers:9000和stores:8081接受到了。想要對接受到的消息自定義自己的處理方式的話,可以添加@EventListener註解的AckRemoteApplicationEvent和SentApplicationEvent類型到你自己的應用中。或者到TraceRepository類中,直接處理數據。

這樣,我們就可清晰地知道事件的傳播細節。

/bus/refresh BUG
/bus/refresh 有一個很嚴重的BUG,一直沒有解決:對客戶端執行/bus/refresh之後,掛到總線的上的客戶端都會從Eureka註冊中心撤銷登記;如果對server端執行/bus/refresh,server端也會從Eureka註冊中心撤銷登記。再用白話解釋一下,就是本來人家在Eureka註冊中心註冊的好好的,只要你對着它執行一次/bus/refresh,立刻就會從Euraka中掛掉。

其實這個問題挺嚴重的,本來你利用/bus/refresh給所有的節點來更新配置信息呢,結果把服務從Euraka中給搞掉了,那麼如果別人需要調用客戶端的服務的時候就直接歇菜了。不知道國內有童鞋公司在生產中用到這個功能沒有,用了不就很慘烈。在網上搜索了一下,國內網友和國外網友都遇到過很多次,但是一直沒有解決,很幸運就是我在寫這篇文章的前三天,Netflix修復了這個問題,使用Spring Cloud最新版本的包就可以解決這個問題。由此也可以發現Spring Cloud還在快速的發展中,最新的版本可能也會有一些不穩定性,可見路漫漫而修遠兮。

在pom中使用Spring Cloud的版本,解決這個bug.

<properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
    <java.version>1.8</java.version>
    <spring-cloud.version>Dalston.SR1</spring-cloud.version>
</properties>

主要是這句:<spring-cloud.version>Dalston.SR1</spring-cloud.version>

整體架構如下:
springcloud(九):配置中心和消息總線(配置中心終結版)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章