1. 程式人生 > >Spring Boot 2.0 整合攜程Apollo配置中心

Spring Boot 2.0 整合攜程Apollo配置中心

Apollo(阿波羅)是攜程框架部門研發的分散式配置中心,能夠集中化管理應用不同環境、不同叢集的配置,配置修改後能夠實時推送到應用端,並且具備規範的許可權、流程治理等特性,適用於微服務配置管理場景。

服務端基於Spring Boot和Spring Cloud開發,打包後可以直接執行,不需要額外安裝Tomcat等應用容器。

Java客戶端不依賴任何框架,能夠運行於所有Java執行時環境,同時對Spring/Spring Boot環境也有較好的支援。

.Net客戶端不依賴任何框架,能夠運行於所有.Net執行時環境。

如果想要深入瞭解,可以到github上參見Apollo配置中心

,官網的介紹很詳細。本章主要講述Spring Boot 2.0 整合Apollo配置中心。

一、Apollo配置中心服務端(來源於官網)

本文的重點在於Apollo在客戶端的使用,所以Apollo服務端使用的是官網提供的 Quick Start(針對本地測試使用),後續文章會專門講述Apollo服務端在分散式環境下的部署。

1.1 準備工作

  1. Java
    Apollo服務端要求Java 1.8+,客戶端要求Java 1.7+,筆者本地使用的是Java 1.8。
  2. MySQL
    Apollo的表結構對timestamp使用了多個default宣告,所以需要5.6.5以上版本。筆者本地使用的是8.0.13版本
  3. 下載 Quick Start
    官網為我們準備了 Quick Start 安裝包。大家只需要下載到本地,就可以直接使用,免去了編譯、打包過程。大家可以到github下載,也可以通過百度雲盤下載

1.2 安裝步驟

1.2.1 建立資料庫

Apollo服務端共需要兩個資料庫:ApolloPortalDB和ApolloConfigDB,官網把資料庫、表的建立和樣例資料都分別準備了sql檔案(在下載的 Quick Start 安裝包的sql目錄下),只需要匯入資料庫即可。

1.2.1.1 建立ApolloPortalDB

通過各種Mysql客戶端(Navicat,DataGrip等)匯入sql/apolloportaldb.sql

即可
下面以MySQL原生客戶端為例:

source /your_local_path/sql/apolloportaldb.sql

匯入成功後,可以通過執行以下sql語句來驗證:

select `Id`, `AppId`, `Name` from ApolloPortalDB.App;
Id AppId Name
1 SampleApp Sample App
1.2.1.2 建立ApolloConfigDB

通過各種Mysql客戶端(Navicat,DataGrip等)匯入sql/apolloconfigdb.sql即可
下面以MySQL原生客戶端為例:

source /your_local_path/sql/apolloconfigdb.sql

匯入成功後,可以通過執行以下sql語句來驗證:

select `NamespaceId`, `Key`, `Value`, `Comment` from ApolloConfigDB.Item;
NamespaceId Key Value Comment
1 timeout 100 sample

1.2.2 配置資料庫連線資訊

Apollo服務端需要知道如何連線到你前面建立的資料庫,所以需要編輯demo.sh,修改ApolloPortalDB和ApolloConfigDB相關的資料庫連線串資訊。

#apollo config db info
apollo_config_db_url=jdbc:mysql://localhost:3306/ApolloConfigDB?characterEncoding=utf8
apollo_config_db_username=使用者名稱
apollo_config_db_password=密碼(如果沒有密碼,留空即可)

# apollo portal db info
apollo_portal_db_url=jdbc:mysql://localhost:3306/ApolloPortalDB?characterEncoding=utf8
apollo_portal_db_username=使用者名稱
apollo_portal_db_password=密碼(如果沒有密碼,留空即可)

1.3 啟動Apollo配置中心

1.3.1 確保埠未被佔用

Quick Start指令碼會在本地啟動3個服務,分別使用8070, 8080, 8090埠,請確保這3個埠當前沒有被使用。例如,在Linux/Mac下,可以通過如下命令檢查:

lsof -i:8080

在windows下,可以通過如下命令檢查:

netstat -aon|findstr "8080"

1.3.2 執行啟動指令碼

在Quick Start目錄下執行如下命令:

./demo.sh start

當看到如下輸出後,就說明啟動成功了!

==== starting service ====
Service logging file is ./service/apollo-service.log
Started [10768]
Waiting for config service startup.......
Config service started. You may visit http://localhost:8080 for service status now!
Waiting for admin service startup....
Admin service started
==== starting portal ====
Portal logging file is ./portal/apollo-portal.log
Started [10846]
Waiting for portal startup......
Portal started. You can visit http://localhost:8070 now!

1.3.3 異常排查

如果啟動遇到了異常,可以分別檢視service和portal目錄下的log檔案排查問題。

注: 在啟動apollo-configservice的過程中會在日誌中輸出eureka註冊失敗的資訊,如com.sun.jersey.api.client.ClientHandlerException: java.net.ConnectException: Connection refused。需要注意的是,這個是預期的情況,因為apollo-configservice需要向Meta Server(它自己)註冊服務,但是因為在啟動過程中,自己還沒起來,所以會報這個錯。後面會進行重試的動作,所以等自己服務起來後就會註冊正常了。

1.4 使用Apollo配置中心

1.4.1 檢視樣例配置

  1. 瀏覽器訪問http://localhost:8070
    登入介面
    Quick Start集成了Spring Security簡單認證,更多資訊可以參考Portal 實現使用者登入功能
  2. 輸入使用者名稱apollo,密碼admin登入
    專案介面
    配置中心中包含一個預設的專案SampleApp
  3. 點選SampleApp進入配置介面,可以看到當前有一個配置timeout=100
    專案配置詳情
    如果提示系統出錯,請重試或聯絡系統負責人,請稍後幾秒鐘重試一下,因為通過Eureka註冊的服務有一個重新整理的延時。

1.4.2 新增專案配置

我們的客戶端使用apollo需要新增相關的專案配置。

  1. 點選新建專案
    專案資訊

    • 應用ID:這個ID是應用的唯一標識
    • 應用名稱:應用的名稱,會展示在配置中心的首頁上

    點選提交,建立完成
    應用配置介面

  2. 新增配置資訊
    點選新增配置,填寫配置資訊配置資訊點選提交,此時配置還未生效。未釋出的配置

  3. 釋出配置
    點擊發布,配置立刻生效
    生效的配置

  4. 回滾
    如果配置做了修改之後,發現配置更改錯誤,這個時候可以使用回滾功能,回到上一個配置
    回滾配置

二、Apollo配置中心客戶端

我們客戶端基於Spring Boot 2.0搭建,開發工具是InteIIij IDEA。新建一個專案,專案名稱為apollo-client

2.1 客戶端搭建

  1. 新增Apollo客戶端依賴
    <dependency>
         <groupId>com.ctrip.framework.apollo</groupId>
         <artifactId>apollo-client</artifactId>
         <version>1.1.1</version>
    </dependency>
    
    目前最新版本為1.1.1
  2. 新增配置資訊
    # 應用ID(在Apollo服務端新增專案新增的應用ID)
    app.id=testclient
    # apollo-configservice地址
    apollo.meta=http://127.0.0.1:8080
    
  3. 開啟Apollo客戶端
    在專案的啟動類上新增@EnableApolloConfig註解
  4. 新增一個測試介面
  @RequestMapping("/index")
  public String hello(){
    return "hello man";
  }
  1. 啟動服務測試
    在Apollo配置中心中,我們對該專案有一條配置server.port = 9000,啟動服務,訪問http://localhost:9000/index,返回hello man。證明,客戶端是從服務端獲取的配置。

2.2 客戶端用法

在上一節,我們簡單的搭建了客戶端,成功的使用服務端配置。Apollo為我們提供的使用方式有很多種,下面只介紹Spring Boot 2.0環境下的使用方式。

2.2.1 Spring Placeholder的使用

Spring應用通常會使用Placeholder來注入配置,使用的格式形如${someKey:someDefaultValue},如${timeout:100}。冒號前面的是key,冒號後面的是預設值(建議在實際使用時儘量給出預設值,以免由於key沒有定義導致執行時錯誤)。Apollo從v0.10.0開始的版本支援placeholder在執行時自動更新。如果需要關閉placeholder在執行時自動更新功能,可以通過以下兩種方式關閉:

  1. 通過設定System Property apollo.autoUpdateInjectedSpringProperties,如啟動時傳入-Dapollo.autoUpdateInjectedSpringProperties=false
  2. 通過設定META-INF/app.properties中的apollo.autoUpdateInjectedSpringProperties=false
2.2.1.1 Java Config使用方式
  1. 新建配置類JavaConfigBean如下:
    /**
     * Java Config方式
     *
     * @author simon
     * @create 2018-11-02 15:00
     **/
    @Configuration
    public class JavaConfigBean {
      @Value("${timeout:20}")
      private int timeout;
    
      public int getTimeout() {
        return timeout;
      }
    }
    
  2. 新增訪問端點
      //1.Java Config方式
      @Autowired
      JavaConfigBean javaConfigBean;
    
      @RequestMapping("/index1")
      public String hello1(){
        return javaConfigBean.getTimeout()+"";
      }
    
  3. 測試
    瀏覽器訪問http://127.0.0.1:8080/index1,正確返回配置的值
2.2.1.2 ConfigurationProperties使用方式

Spring Boot提供了@ConfigurationProperties把配置注入到bean物件中。Apollo也支援這種方式,下面的例子會把redis.cache.expireSecondsredis.cache.commandTimeout分別注入到SampleRedisConfigexpireSecondscommandTimeout欄位中。

  1. 新增配置類SampleRedisConfig如下:
    /**
     * ConfigurationProperties使用方式
     *
     * @author simon
     * @create 2018-11-02 9:30
     **/
    @Configuration
    @ConfigurationProperties(prefix = "redis.cache")
    public class SampleRedisConfig {
      private int expireSeconds;
      private int commandTimeout;
    
      public void setExpireSeconds(int expireSeconds) {
        this.expireSeconds = expireSeconds;
      }
    
      public void setCommandTimeout(int commandTimeout) {
        this.commandTimeout = commandTimeout;
      }
    
      public int getExpireSeconds() {
        return expireSeconds;
      }
    
      public int getCommandTimeout() {
        return commandTimeout;
      }
    }
    
  2. 新增訪問端點
    //2. ConfigurationProperties使用方式
      @Autowired
      SampleRedisConfig sampleRedisConfig;
    
      @RequestMapping("/index2")
      public String hello2(){
        return sampleRedisConfig.getCommandTimeout()+"--"+sampleRedisConfig.getExpireSeconds();
      }
    
  3. 測試
    瀏覽器訪問http://127.0.0.1:8080/index2,正確返回配置的值

注: @ConfigurationProperties如果需要在Apollo配置變化時自動更新注入的值,需要配合使用EnvironmentChangeEventRefreshScope。這個我會在後續文章中詳細描述。

2.2.2 Spring Annotation支援

Apollo同時還增加了幾個新的Annotation來簡化在Spring環境中的使用。

  • @ApolloConfig用來自動注入Config物件
  • @ApolloConfigChangeListener用來自動註冊ConfigChangeListener
  • @ApolloJsonValue用來把配置的json字串自動注入為物件
2.2.2.1 @ApolloConfig的使用
  1. 新增訪問端點
    // 3. @ApolloConfig使用
      @ApolloConfig
      private Config config;
    
      @RequestMapping("/index3")
      public String hello3(){
        Set <String> propertyNames = config.getPropertyNames();
        propertyNames.forEach(key -> {
          System.err.println(key+"="+config.getIntProperty(key,0));
        });
        return propertyNames.toString();
      }
    
  2. 測試
    瀏覽器訪問http://127.0.0.1:8080/index3,正確列印配置的值
    redis.cache.commandTimeout=3000
    redis.cache.expireSeconds=20
    server.port=800
    timeout=200
    
2.2.2.2 @ApolloConfigChangeListener的使用
  1. 新增以下程式碼
    @ApolloConfigChangeListener
      private void someOnChange(ConfigChangeEvent changeEvent) {
        //update injected value of batch if it is changed in Apollo
        if (changeEvent.isChanged("timeout")) {
          System.out.println(config.getIntProperty("timeout", 0));
        }
      }
    
  2. 測試
    在Apollo服務端修改timeout配置的值為300,釋出後,控制檯列印300
2.2.2.3 @ApolloJsonValue的使用
  1. 新增User如下:
    /**
     * 使用者
     *
     * @author simon
     * @create 2018-11-02 16:41
     **/
    public class User {
      private String username;
      private String password;
    
      public String getUsername() {
        return username;
      }
    
      public void setUsername(String username) {
        this.username = username;
      }
    
      public String getPassword() {
        return password;
      }
    
      public void setPassword(String password) {
        this.password = password;
      }
    }
    
  2. 服務端新增配置
    jsonBeanProperty=[ { "username": "john", "password": "1234" }, { "username": "simon", "password": "222132" } ]
  3. 客戶端獲取配置
    //4. @ApolloJsonValue使用
    @ApolloJsonValue("${jsonBeanProperty:[]}")
    private List<User> anotherJsonBeans;
    
    @RequestMapping("/index4")
    public void hello4(){
      anotherJsonBeans.forEach(item -> {
        System.err.println(item.getUsername()+"--"+item.getPassword());
      });
    }
    
  4. 測試
    瀏覽器訪問http://127.0.0.1:8080/index4,正確列印配置的值

原始碼下載