文章出處

 分布式環境下的統一配置框架,已經有不少了,比如百度的disconf,阿里的diamand。今天來看下spring cloud對應的解決方案:

如上圖,從架構上就可以看出與disconf之類的有很大不同,主要區別在于:

  • 配置的存儲方式不同
    • disconf是把配置信息保存在mysql、zookeeper中,而spring cloud config是將配置保存在git/svn上 (即:配置當成源代碼一樣管理)
  • 配置的管理方式不同
    • spring cloud config沒有類似disconf的統一管理界面,既然把配置都當成git之類的源碼來看待了,git的管理界面,就是配置的管理界面
  • 配置變化的通知機制不同
    • disconf中配置變化后,依賴zk的事件watcher來通知應用,而spring cloud config則是依賴git每次push后,觸發webhook回調,最終觸發spring cloud bus(消息總線),然后由消息總線通知相關的應用。

另外,spring cloud config server本身也是一個微服務,跟其它的微服務一樣,也可以注冊到eureka server上,讓其它使用方從注冊中心來發現,單純從解決的問題/場景來看,disconf與spring cloud config server是高度重合的,很難說哪個好,那個差,只是設計哲學不同。

但有一點,從配置變化的通知機制上看,如果有100個應用節點,都依賴于統一配置,如果修改了配置,只想讓某幾個節點"灰度"更新配置,spring cloud config server更容易做到,這一點相對disconf更靈活(后面會詳細講解)。

使用步驟:

一、在git/svn上創建一個配置項目(用于保存配置文件)

https://github.com/yjmyzz/spring-cloud-config-repository 這個為例,上面就放了幾個配置文件(推薦用新的yml格式,對中文支持更好碼)。

application.yml里的內容如下:

demo:
  title: "default title"  

其它幾個文件application_xxx.yml,里面的xxx,代表不同的profile.

 

二、創建config-server微服務

2.1 添加依賴項

dependencies {
    compile 'org.springframework.cloud:spring-cloud-starter-eureka'
    compile 'org.springframework.cloud:spring-cloud-config-server'
    compile 'org.springframework.boot:spring-boot-starter-actuator'
}  

關鍵是第2個依賴項

2.2 application.yml

spring:
  application:
    name: config-server
  profiles:
      active: server1
  cloud:
    config:
      server:
        git:
          uri: https://github.com/yjmyzz/spring-cloud-config-repository
#          username: *****
#          password: *****

eureka:
  instance:
    prefer-ip-address: true
    instance-id: ${spring.application.name}:${server.port}
  client:
    service-url:
      defaultZone: http://yjmyzz:123456@server1:8100/eureka,http://yjmyzz:123456@server2:8200/eureka

management:
  security:
    enabled: false

注意上面的cloud.config.server這段,里面配置了git配置項目的位置。另外:config-server服務本身也需要HA,所以本示例中起了2個實例,分別對應server1、server2 這二個profile,用不同的端口,在本機跑2個實例,以模擬高可用。

application-server1.yml

server:
  port: 8004

application-server2.yml

server:
  port: 8005

2.3 main入口類

package com.cnblogs.yjmyzz.spring.cloud.study.config;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.config.server.EnableConfigServer;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;

/**
 * Created by yangjunming on 2017/7/5.
 */
@SpringBootApplication
@EnableEurekaClient
@EnableConfigServer
public class ConfigServer {

    public static void main(String[] args) {
        SpringApplication.run(ConfigServer.class, args);

    }
}

關鍵是@EnableConfigServer 這個注解。 

2.4 跑起來看看

可以看到2個config-server已經注冊到eureka上了,然后單獨瀏覽一下: http://localhost:8004/application-dev.yml

已經把git上的application-dev.yml的內容輸出了。

三、使用config-server

3.1 在之前的service-provider中添加依賴項

compile 'org.springframework.cloud:spring-cloud-starter-config'

3.2 創建一個簡單的配置類

package com.cnblogs.yjmyzz.spring.cloud.study.config;

import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;

/**
 * Created by yangjunming on 2017/7/5.
 */
@Component
@Data
@ConfigurationProperties(prefix = "demo")
public class DemoConfig {

    private String title;
}  

 然后找一個示例服務,使用這個配置:

package com.cnblogs.yjmyzz.spring.cloud.study.service.impl;

import com.cnblogs.yjmyzz.spring.cloud.study.api.UserService;
import com.cnblogs.yjmyzz.spring.cloud.study.config.DemoConfig;
import com.cnblogs.yjmyzz.spring.cloud.study.dto.UserDTO;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service("userService")
public class UserServiceImpl implements UserService {

    @Autowired
    DemoConfig config;

    @Override
    public UserDTO findUser(Integer userId) {
        UserDTO user = new UserDTO();
        user.setUserId(userId);
        user.setUserName("菩提樹下的楊過(" + config.getTitle() + ")");
        return user;
    }
}

3.3 添加bootstrap.yml 配置文件

spring:
  application:
    name: application
  cloud:
    config:
      profile: dev
      label: master
      discovery:
        enabled: true
        service-id: config-server

eureka:
  instance:
    prefer-ip-address: true
  client:
    service-url:
      defaultZone: http://yjmyzz:123456@server1:8100/eureka,http://yjmyzz:123456@server2:8200/eureka

注意spring.cloud這一節的內容,里面指定了profile為dev,讀取的git配置文件分支為master,同時允許從eureka上自動發現config-server這個實例。另外 spring.applicatin.name 即為配置文件的名稱(即:application_xxx.yml) 

3.4 跑起來看看

說明已經從config-server取到了配置。  

 

四、配置更新

4.1 Controller上添加@RefreshScope

@RestController
@RefreshScope
public class UserController {

    @Autowired
    private UserService userService;

    @GetMapping("/user/{id}")
    public UserDTO findUser(@PathVariable Integer id) {
        return userService.findUser(id);
    }
}

這個注解,根據源碼上的說法:Beans annotated this way can be refreshed at runtime and any components that are using them will get a new instance on the next method call, fully initialized and injected with all dependencies. 使用該注解后,可以在運行時直接刷新Bean,并在下次方法調用時,得到一個全新的實例。  

4.2 手動刷新/refresh

可以嘗試把git配置項目里的application-dev.yml修改下內容,再瀏覽剛才的http://localhost:8001/user/1 發現內容并沒有變化。

http://localhost:8001/refresh 手動向這個地址,發一個post請求(可以用postman或 curl -d '' http://localhost:8001/refresh),可以看到
點擊看原圖

說明demo.title這個配置項被刷新了,再瀏覽http://localhost:8001/user/1  可以看到有變化了
點擊看原圖

但是這樣顯然不是個辦法,比如有10個service-provider組成的集群,如果要1臺臺手動刷新,太low了(除了做配置灰度更新,可以先刷新1臺這種場景外)

4.3 集成spring cloud bus來批量刷新

spring cloud bus目前僅支持rabbitmq 及 kafka,我們以kafka為例,先在service-provider的application.yml里,加入下面的配置

點擊看原圖

然后依賴項里,加入:

compile 'org.springframework.cloud:spring-cloud-starter-bus-kafka'

注:關于kafka的環境搭建,網上有很多資料,大家可以參考下。

配置好這些后,本機啟動kafka,然后再重啟service-provider,就會多出一個/bus/refresh的端點,即:http://xxx:port/bus/refresh ,只要向集群中的任何一臺機器的/bus/refresh發起post請求,就會同步刷新其它所有節點。原理大致就是,這臺機器會發一條消息到kafka中,然后其它機器都是掛在消息總線上的,也會監聽到該消息,然后刷新各自的配置。

最后一個問題:就算有/bus/refresh,也需要有人或系統觸發。這個很好解決,github或gitlab上一般都有webhook功能,可以配置在代碼push時,觸發一些地址的回調。
點擊看原圖

這樣,只要配置的代碼提交了,就會觸發自動刷新。

注:低版本的spring-cloud-dependencies有一個嚴重bug,調用/bus/refresh后,會導致所有服務節點,從eureka server的實例列表中永久下線,無法自動恢復,除非再次訪問某個服務的/health端點,建議使用Dalston.SR2 或以上版本。

示例源代碼: https://github.com/yjmyzz/spring-cloud-demo


文章列表


不含病毒。www.avast.com
arrow
arrow
    全站熱搜
    創作者介紹
    創作者 大師兄 的頭像
    大師兄

    IT工程師數位筆記本

    大師兄 發表在 痞客邦 留言(0) 人氣()