基於Spring Cloud搭建分散式配置中心

vipshop_fin_dev發表於2018-07-20

一.Spring Cloud簡介

先來看看官網對spring cloud的定義:

Spring Cloud offers a simple and accessible programming model to the most common distributed system patterns, helping developers build resilient, reliable, and coordinated applications.

再來看看整個spring cloud的architecture
這裡寫圖片描述
可以看出spring cloud的意義在於推出了一個基於spring的全鏈路方案,包括gateway,tracing,microservices,configCenter,service registry等等,中小型公司在基於spring cloud和springBoot的基礎上進行搭建系統的話,只要稍作定製化便能很快的搭建起一個能支撐一定量qps和資料的系統

二.基於Spring Cloud搭建分散式配置中心

(1)首先簡單瀏覽一下我們要搭建的config center的architecture:
這裡寫圖片描述
1.git repository作為配置的儲存地
2.Config Server設定兩臺作為高可用
3.eureka server設定兩臺註冊中心作為高可用
4.config client作為客戶端獲取配置

(2)下面看看關鍵程式碼:

1.配置eureka註冊中心

EurekaServiceApplication:

package com.andrew.eureka.server;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;

@EnableEurekaServer
@SpringBootApplication
public class EurekaServiceApplication {
    public static void main(String[] args) {
        SpringApplication.run(EurekaServiceApplication.class, args);
    }
}

application-peer1.properties配置:

server.port=8761

eureka.client.register-with-eureka=true
eureka.client.fetch-registry=true
eureka.client.service-url.defaultZone=http://peer2:8762/eureka
eureka.instance.appname=eureka-server
eureka.instance.hostname=peer1

logging.level.com.netflix.eureka=OFF
logging.level.com.netflix.discovery=OFF

application-peer2.properties配置:

server.port=8762

eureka.client.register-with-eureka=true
eureka.client.fetch-registry=true
eureka.client.service-url.defaultZone=http://peer1:8761/eureka
eureka.instance.appname=eureka-server
eureka.instance.hostname=peer2

logging.level.com.netflix.eureka=OFF
logging.level.com.netflix.discovery=OFF

通過mvn clean install命令打包成jar之後,跑命令

java -jar eureka-service-0.0.1-SNAPSHOT.jar --spring.profiles.active=peer1 
java -jar eureka-service-0.0.1-SNAPSHOT.jar --spring.profiles.active=peer2

輸入http://localhost:8761/之後,可以看到
這裡寫圖片描述

裡面的available-replicas可以看到擁有peer2這個備份service

2.配置config server

ConfigServerApplication:

package com.andrew.config.server;

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

@EnableConfigServer
@SpringBootApplication
@EnableEurekaClient
public class ConfigServerApplication {
    public static void main(String[] args) {
        new SpringApplicationBuilder(ConfigServerApplication.class).run(args);
    }
}

每個eureka client都必須有@EnableEurekaClient這個註解,每個config server必須有@EnableConfigServer這個註解

application.yml配置檔案:

spring:
  application:
    name: config-server
  cloud:
    config:
      server:
        git:
          uri: F:\\public-projects\\spring-cloud\\spring-cloud-config\\gs-centralized-configuration-master\\config

eureka:
  client:
    service-url:
      defaultZone: http://localhost:8761/eureka,http://localhost:8762/eureka

注意spring cloud使用git做配置的儲存倉庫,這裡我是用了本地的git倉庫,一般來說是通過遠端倉庫作為配置的儲存
跑下面兩個命令起兩個config server:

java -jar config-server-0.0.1-SNAPSHOT.jar --server.port=8888
java -jar config-server-0.0.1-SNAPSHOT.jar --server.port=8889

這裡寫圖片描述

可以看到兩個config server已經註冊到eureka server上了

3.配置config client

package com.andrew.config.client;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@EnableEurekaClient
@SpringBootApplication
public class ConfigClientApplication {

    public static void main(String[] args) {
        new SpringApplicationBuilder(ConfigClientApplication.class).run(args);
    }
}

@RefreshScope
@RestController
class MessageRestController {

    @Value("${message:Hello default}")
    private String message;

    @RequestMapping("/message")
    String getMessage() {
        return this.message;
    }
}

@RefreshScope是因為config client只會在第一次初始化bean的時候獲取一次配置,後面如果需要更新的話,需要設定這個註解在controller上面,並且引入spring actuator的包,通過傳送post請求來更新bean裡面的值
@Value註解表示要獲取的是key為message的值,預設值是Hello default

配置application.properties:

management.endpoints.web.exposure.include=*

spring.application.name=config-client
spring.cloud.config.discovery.enabled=true
spring.cloud.config.discovery.serviceId=config-server
eureka.client.service-url.defaultZone=http://localhost:8761/eureka,http://localhost:8762/eureka

spring.cloud.config.discovery.enabled=true能夠讓config client自動去發現config server
spring.cloud.config.discovery.serviceId=config-server是根據configServer在配置檔案裡面的applicationName設定,configClient根據serviceId去探測configServer

PS:每個configClient會在第一次初始化之後從eureka service獲取到註冊在上面的伺服器資訊,後面即使eureka service down掉,仍然能夠根據本地快取去訪問到config server

執行下面命令啟動config-client:

java -jar config-client-0.0.1-SNAPSHOT.jar --server.port=8081

這裡寫圖片描述
可以看到config-client已經註冊上去

4.Test Application

applicationContext.properties檔案:

hello=helloWorld
message = congratulation success

在瀏覽器輸入:http://localhost:8081/message 可以看到結果是:

這裡寫圖片描述

當我們修改檔案內容為

hello=helloWorld
message = haha we change the value

git commit之後,通過postman傳送post請求到http://localhost:8081/actuator/refresh,再去呼叫http://localhost:8081/message ,可以看到新的配置值:
這裡寫圖片描述
通過這我們就可以成功獲取到更新後的配置

5.優點和缺點

優點:
1.eureka作為註冊中心,不像zk需要選舉master,eureka是peer形式的,那麼即使出現網路變化導致註冊中心例項少於一定數量,eureka也可以保證一直的提供服務
2.當網路變化的時候,eureka會有一個Self Preservation Mode的機制,如果15%的註冊例項在短時間內出現down的情況(通過heartbeat確認),那麼eureka不會移除例項的註冊資訊,而是等到網路恢復之後仍然以這個例項資訊去提供給呼叫方

具體可以通過:https://github.com/Netflix/eureka/wiki/Server-Self-Preservation-Mode這裡瞭解

缺點:
1.需要自己定製化一個流程審批介面,沒有一個完整的許可權控制流程
2.缺少一個介面去維護管理配置
3.不像zk是使用觀察者模式,每個client可以去監聽配置的變化,每次更新都需要主動去傳送一次post請求更新或者必須通過git webhook加上訊息佇列做熱更新,需要另外做一個封裝的api使得更新配置對上層無感知
4.全程都是用http去實現,不如zk通過tcp長連線來的更有效率
5.基於git倉庫儲存配置,從檔案系統在io上效能沒有從mysql讀快,並且可以在檔案系統前加入redis之類的快取機制提供更強的效能

6.附記

demo程式碼:https://github.com/AndrewHuangMiao/spring-cloud-config-eureka
業界基於springboot和springcloud做了定製化之後的使用:https://github.com/ctripcorp/apollo/

7.參考:

https://spring.io/guides/gs/service-registration-and-discovery/
https://spring.io/guides/gs/centralized-configuration/
https://www.aliyun.com/jiaocheng/820688.html

written by:黃文嶽

相關文章