Nepxion Discovery【探索】微服務企業級解決方案

Alt text

Nepxion Discovery【探索】微服務企業級解決方案】

Nepxion Discovery【探索】使用指南,基於Spring Cloud Greenwich版、Finchley版和Hoxton版而製做,對於Edgware版,使用者須要自行修改。使用指南主要涉及的功能包括:前端

  • 基於Header傳遞的全鏈路灰度路由,網關爲路由觸發點。採用配置中心配置路由規則映射在網關過濾器中植入Header信息而實現,路由規則傳遞到全鏈路服務中。路由方式主要包括版本和區域的匹配路由、版本和區域的權重路由、基於機器IP地址和端口的路由
  • 基於規則訂閱的全鏈路灰度發佈。採用配置中心配置灰度規則映射在全鏈路服務而實現,全部服務都訂閱某個共享配置。發佈方式主要包括版本和區域的匹配發布、版本和區域的權重發布
  • 全鏈路服務隔離。包括註冊隔離、消費端隔離和提供端服務隔離,示例僅提供基於Group隔離。除此以外,不在本文介紹內的,還包括:
    • 註冊隔離:黑/白名單的IP地址的註冊隔離、最大註冊數限制的註冊隔離
    • 消費端隔離:黑/白名單的IP地址的消費端隔離
  • 全鏈路服務限流熔斷降級權限,集成阿里巴巴Sentinel,有機整合灰度路由,擴展LimitApp的機制,經過動態的Http Header方式實現組合式防禦機制,包括基於服務名、基於灰度組、基於灰度版本、基於灰度區域、基於機器地址和端口等防禦機制,支持自定義任意的業務參數組合實現該功能。支持原生的流控規則、降級規則、受權規則、系統規則、熱點參數流控規則
  • 全鏈路灰度調用鏈。包括Header方式和日誌方式,Header方式框架內部集成,日誌方式經過MDC輸出(需使用者自行集成)
  • 同城雙活多機房切換支持。它包含在「基於Header傳遞的全鏈路灰度路由」裏
  • 數據庫灰度發佈。內置簡單的數據庫灰度發佈策略,它不在本文的介紹範圍內
  • 灰度路由和發佈的自動化測試
  • Docker容器化和Kubernetes平臺的無縫支持部署

[Nacos] 阿里巴巴中間件部門開發的新一代集服務註冊發現中心和配置中心爲一體的中間件。它是構建以「服務」爲中心的現代應用架構 (例如微服務範式、雲原生範式) 的服務基礎設施,支持幾乎全部主流類型的「服務」的發現、配置和管理,更敏捷和容易地構建、交付和管理微服務平臺java

[Sentinel] 阿里巴巴中間件部門開發的新一代以流量爲切入點,從流量控制、熔斷降級、系統負載保護等多個維度保護服務的穩定性的分佈式系統的流量防衛兵。它承接了阿里巴巴近10年的雙十一大促流量的核心場景,例如秒殺(即突發流量控制在系統容量能夠承受的範圍)、消息削峯填谷、集羣流量控制、實時熔斷下游不可用應用等git

[Spring Cloud Alibaba] 阿里巴巴中間件部門開發的Spring Cloud加強套件,致力於提供微服務開發的一站式解決方案。此項目包含開發分佈式應用微服務的必需組件,方便開發者經過Spring Cloud編程模型輕鬆使用這些組件來開發分佈式應用服務。依託Spring Cloud Alibaba,只須要添加一些註解和少許配置,就能夠將Spring Cloud應用接入阿里微服務解決方案,經過阿里中間件來迅速搭建分佈式應用系統github

示例以Nacos爲服務註冊中心和配置中心(使用者可自行換成其它服務註冊中心和配置中心),集成Spring Cloud Alibaba,經過Gateway和Zuul調用兩個版本或者區域的服務,模擬網關灰度路由和服務灰度權重的功能spring

若是使用者須要更強大的功能,請參考源碼主頁。規則策略不少,請使用者選擇最適合本身業務場景的方式docker

目錄

請聯繫我

微信、公衆號和文檔數據庫

Alt textAlt textAlt text

相關連接

源碼主頁

源碼主頁編程

指南主頁

指南主頁json

文檔主頁

文檔主頁後端

相關圖示

部署架構拓撲圖

Alt text

服務治理架構圖

Alt text

灰度方式區別圖

Alt text

環境搭建

  • 下載代碼
    • Git clone https://github.com/Nepxion/DiscoveryGuide.git
  • 代碼導入IDE
  • 下載Nacos服務器
  • 啓動Nacos服務器
    • Windows環境下,運行bin目錄下的startup.cmd
    • Linux環境下,運行bin目錄下的startup.sh

啓動服務

  • 在IDE中,啓動四個應用服務和兩個網關服務,以下:
類名 微服務 服務端口 版本 區域
DiscoveryGuideServiceA1.java A1 3001 1.0 dev
DiscoveryGuideServiceA2.java A2 3002 1.1 qa
DiscoveryGuideServiceB1.java B1 4001 1.0 qa
DiscoveryGuideServiceB2.java B2 4002 1.1 dev
DiscoveryGuideGateway.java Gateway 5001 1.0
DiscoveryGuideZuul.java Zuul 5002 1.0

注:啓動不分先後次序

環境驗證

  • 導入Postman的測試腳本,腳本地址

  • 在Postman中執行目錄結構下 」Nepxion「 -> 」Discovery指南網關接口「 -> 」Gateway網關調用示例「,調用地址爲http://localhost:5001/discovery-guide-service-a/invoke/gateway,相關的Header值已經預設,供開發者修改。測試經過Spring Cloud Gateway網關的調用結果,結果爲以下格式:
gateway -> discovery-guide-service-a[192.168.0.107:3001][V=1.0][R=dev][G=discovery-guide-group] 
-> discovery-guide-service-b[192.168.0.107:4001][V=1.0][R=qa][G=discovery-guide-group]
  • 在Postman中執行目錄結構下 」Nepxion「 -> 」Discovery指南網關接口「 -> 」Zuul網關調用示例「,調用地址爲http://localhost:5002/discovery-guide-service-a/invoke/zuul,相關的Header值已經預設,供開發者修改。測試經過Zuul網關的調用結果,結果爲以下格式:
zuul -> discovery-guide-service-a[192.168.0.107:3001][V=1.0][R=dev][G=discovery-guide-group] 
-> discovery-guide-service-b[192.168.0.107:4001][V=1.0][R=qa][G=discovery-guide-group]
  • 上述步驟在下面每次更改規則策略的時候執行,並驗證結果和規則策略的指望值是否相同

基於Header傳遞方式的網關灰度路由策略

灰度路由架構圖

多版本灰度路由架構圖

Alt text

多區域灰度路由架構圖

Alt text

多IP和端口灰度路由架構圖

Alt text

配置網關灰度路由策略

在Nacos配置中心,增長網關灰度路由策略

版本匹配灰度路由策略

增長Spring Cloud Gateway的基於版本匹配路由的灰度策略,Group爲discovery-guide-group,Data Id爲discovery-guide-gateway,策略內容以下,實現從Spring Cloud Gateway發起的調用都走版本爲1.0的服務:

<?xml version="1.0" encoding="UTF-8"?>
<rule>
    <strategy>
        <version>1.0</version>
    </strategy>
</rule>

Alt text

每一個服務調用的版本均可以自行指定,見下面第二條。當全部服務都選同一版本的時候,能夠簡化成下面第一條

1. <version>1.0</version>
2. <version>{"discovery-guide-service-a":"1.0", "discovery-guide-service-b":"1.0"}</version>

若是上述表達式還未知足需求,也能夠採用通配符(具體詳細用法,參考Spring AntPathMatcher)

* - 表示調用範圍爲全部服務的全部版本
1.* - 表示調用範圍爲全部服務的1開頭的全部版本

或者

"discovery-guide-service-b":"1.*;1.2.?"

表示discovery-guide-service-b服務的版本調用範圍是1開頭的全部版本,或者是1.2開頭的全部版本(末尾必須是1個字符)

版本權重灰度路由策略

增長Spring Cloud Gateway的基於版本權重路由的灰度策略,Group爲discovery-guide-group,Data Id爲discovery-guide-gateway,策略內容以下,實現從Spring Cloud Gateway發起的調用1.0版本流量調用爲90%,1.1流量調用爲10%:

<?xml version="1.0" encoding="UTF-8"?>
<rule>
    <strategy>
        <version-weight>1.0=90;1.1=10</version-weight>
    </strategy>
</rule>

Alt text

每一個服務調用的版本權重均可以自行指定,見下面第二條。當全部服務都選相同版本權重的時候,能夠簡化成下面第一條

1. <version-weight>1.0=90;1.1=10</version-weight>
2. <version-weight>{"discovery-guide-service-a":"1.0=90;1.1=10", "discovery-guide-service-b":"1.0=90;1.1=10"}</version-weight>

區域匹配灰度路由策略

增長Zuul的基於區域匹配路由的灰度策略,Group爲discovery-guide-group,Data Id爲discovery-guide-zuul,策略內容以下,實現從Zuul發起的調用都走區域爲dev的服務:

<?xml version="1.0" encoding="UTF-8"?>
<rule>
    <strategy>
        <region>dev</region>
    </strategy>
</rule>

Alt text

每一個服務調用的區域均可以自行指定,見下面第二條。當全部服務都選同一區域的時候,能夠簡化成下面第一條

1. <region>dev</region>
2. <region>{"discovery-guide-service-a":"dev", "discovery-guide-service-b":"dev"}</region>

若是上述表達式還未知足需求,也能夠採用通配符(具體詳細用法,參考Spring AntPathMatcher)

* - 表示調用範圍爲全部服務的全部區域
d* - 表示調用範圍爲全部服務的d開頭的全部區域

或者

"discovery-guide-service-b":"d*;q?"

表示discovery-guide-service-b服務的區域調用範圍是d開頭的全部區域,或者是q開頭的全部區域(末尾必須是1個字符)

區域權重灰度路由策略

增長Zuul的基於區域權重路由的灰度策略,Group爲discovery-guide-group,Data Id爲discovery-guide-zuul,策略內容以下,實現從Zuul發起的調用dev區域流量調用爲85%,qa區域流量調用爲15%:

<?xml version="1.0" encoding="UTF-8"?>
<rule>
    <strategy>
        <region-weight>dev=85;qa=15</region-weight>
    </strategy>
</rule>

Alt text

每一個服務調用的區域權重均可以自行指定,見下面第二條。當全部服務都選相同區域權重的時候,能夠簡化成下面第一條

1. <region-weight>dev=85;qa=15</region-weight>
2. <region-weight>{"discovery-guide-service-a":"dev=85;qa=15", "discovery-guide-service-b":"dev=85;qa=15"}</region-weight>

經過其它方式設置網關灰度路由策略

除了上面經過配置中心發佈灰度規路由則外,還有以下三種方式:

經過前端傳入灰度路由策略

經過前端(Postman)方式傳入灰度路由策略,來代替配置中心方式,傳遞全鏈路路由策略。注意:當配置中心和界面都配置後,以界面傳入優先

  • 版本匹配策略,Header格式以下任選一個:
1. n-d-version=1.0
2. n-d-version={"discovery-guide-service-a":"1.0", "discovery-guide-service-b":"1.0"}
  • 版本權重策略,Header格式以下任選一個:
1. n-d-version-weight=1.0=90;1.1=10
2. n-d-version-weight={"discovery-guide-service-a":"1.0=90;1.1=10", "discovery-guide-service-b":"1.0=90;1.1=10"}
  • 區域匹配策略,Header格式以下任選一個:
1. n-d-region=qa
2. n-d-region={"discovery-guide-service-a":"qa", "discovery-guide-service-b":"qa"}
  • 區域權重策略,Header格式以下任選一個:
1. n-d-region-weight=dev=99;qa=1
2. n-d-region-weight={"discovery-guide-service-a":"dev=99;qa=1", "discovery-guide-service-b":"dev=99;qa=1"}
  • 機器IP地址和端口策略:
n-d-address={"discovery-guide-service-a":"127.0.0.1:3001", "discovery-guide-service-b":"127.0.0.1:4002"}

Alt text

Alt text

當外界傳值Header的時候,網關也設置並傳遞同名的Header,須要決定哪一個Header傳遞到後邊的服務去。須要經過以下開關作控制:

# 當外界傳值Header的時候,網關也設置並傳遞同名的Header,須要決定哪一個Header傳遞到後邊的服務去。若是下面開關爲true,以網關設置爲優先,不然之外界傳值爲優先。缺失則默認爲true
spring.application.strategy.gateway.header.priority=false
# 當以網關設置爲優先的時候,網關未配置Header,而外界配置了Header,仍舊忽略外界的Header。缺失則默認爲true
spring.application.strategy.gateway.original.header.ignored=true

# 當外界傳值Header的時候,網關也設置並傳遞同名的Header,須要決定哪一個Header傳遞到後邊的服務去。若是下面開關爲true,以網關設置爲優先,不然之外界傳值爲優先。缺失則默認爲true
spring.application.strategy.zuul.header.priority=false
# 當以網關設置爲優先的時候,網關未配置Header,而外界配置了Header,仍舊忽略外界的Header。缺失則默認爲true
spring.application.strategy.zuul.original.header.ignored=true

經過業務參數在網關過濾器中自定義灰度路由策略

經過網關過濾器傳遞Http Header的方式傳遞全鏈路灰度路由策略。下面代碼只適用於Zuul和Spring Cloud Gateway網關,Service微服務不須要加該方式

  • 內置策略解析映射到過濾器的自定義方式

增長Spring Cloud Gateway的解析策略,Group爲discovery-guide-group,Data Id爲discovery-guide-gateway,或者,增長Spring Cloud Gateway的解析策略,Group爲discovery-guide-group,Data Id爲discovery-guide-zuul,策略內容見下面XML內容,它所表達的功能邏輯:

1. 當外部調用帶有的Http Header中的值a=1同時b=2
   <condition>節點中header="a=1;b=2"對應的version-id="version-route1",找到下面
   <route>節點中id="version-route1" type="version"的那項,那麼路由即爲:
   {"discovery-guide-service-a":"1.1", "discovery-guide-service-b":"1.1"}

2. 當外部調用帶有的Http Header中的值a=1
   <condition>節點中header="a=1"對應的version-id="version-route2",找到下面
   <route>中id="version-route2" type="version"的那項,那麼路由即爲:
   {"discovery-guide-service-a":"1.0", "discovery-guide-service-b":"1.1"}

3. 當外部調用帶有的Http Header中的值都不命中,找到上面
   <strategy>節點中的全局缺省路由,那麼路由即爲:
   {"discovery-guide-service-a":"1.0", "discovery-guide-service-b":"1.0"}

4. 策略解析總共支持5種,能夠單獨一項使用,也能夠多項疊加使用:
   1)version 版本路由
   2)region 區域路由
   3)address 機器地址路由
   4)version-weight 版本權重路由
   5)region-weight 區域權重路由
<?xml version="1.0" encoding="UTF-8"?>
<rule>
    <!-- 基於Http Header傳遞的策略路由,全局缺省路由 -->
    <strategy>
        <version>{"discovery-guide-service-a":"1.0", "discovery-guide-service-b":"1.0"}</version>
    </strategy>

    <!-- 基於Http Header傳遞的策略路由,客戶定製化控制,跟業務參數綁定。若是不命中,則執行上面的全局缺省路由 -->
    <strategy-customization>
        <conditions>
            <condition id="condition1" header="a=1" version-id="version-route2"/>
            <condition id="condition2" header="a=1;b=2" version-id="version-route1"/>
        </conditions>

        <routes>
            <route id="version-route1" type="version">{"discovery-guide-service-a":"1.1", "discovery-guide-service-b":"1.1"}</route>
            <route id="version-route2" type="version">{"discovery-guide-service-a":"1.0", "discovery-guide-service-b":"1.1"}</route>
        </routes>
    </strategy-customization>
</rule>

Alt text

  • 用戶覆蓋過濾器的自定義方式

繼承GatewayStrategyRouteFilter或者ZuulStrategyRouteFilter,覆蓋掉以下方法中的一個或者多個,經過@Bean方式覆蓋框架內置的過濾類

protected String getRouteVersion();

protected String getRouteRegion();

protected String getRouteAddress();

GatewayStrategyRouteFilter示例

在代碼里根據不一樣的Header選擇不一樣的路由路徑

// 適用於A/B Testing或者更根據某業務參數決定灰度路由路徑。能夠結合配置中心分別配置A/B兩條路徑,能夠動態改變並通知
// 當Header中傳來的用戶爲張三,執行一條路由路徑;爲李四,執行另外一條路由路徑
public class MyGatewayStrategyRouteFilter extends DefaultGatewayStrategyRouteFilter {
    private static final String DEFAULT_A_ROUTE_VERSION = "{\"discovery-guide-service-a\":\"1.0\", \"discovery-guide-service-b\":\"1.1\"}";
    private static final String DEFAULT_B_ROUTE_VERSION = "{\"discovery-guide-service-a\":\"1.1\", \"discovery-guide-service-b\":\"1.0\"}";

    @Value("${a.route.version:" + DEFAULT_A_ROUTE_VERSION + "}")
    private String aRouteVersion;

    @Value("${b.route.version:" + DEFAULT_B_ROUTE_VERSION + "}")
    private String bRouteVersion;

    @Override
    public String getRouteVersion() {
        String user = strategyContextHolder.getHeader("user");

        if (StringUtils.equals(user, "zhangsan")) {
            return aRouteVersion;
        } else if (StringUtils.equals(user, "lisi")) {
            return bRouteVersion;
        }

        return super.getRouteVersion();
    }
}

在配置類裏@Bean方式進行過濾類建立,覆蓋框架內置的過濾類

@Bean
public GatewayStrategyRouteFilter gatewayStrategyRouteFilter() {
    return new MyGatewayStrategyRouteFilter();
}

ZuulStrategyRouteFilter示例

在代碼中 ,根據不一樣的Header選擇不一樣的路由路徑

// 適用於A/B Testing或者更根據某業務參數決定灰度路由路徑。能夠結合配置中心分別配置A/B兩條路徑,能夠動態改變並通知
// 當Header中傳來的用戶爲張三,執行一條路由路徑;爲李四,執行另外一條路由路徑
public class MyZuulStrategyRouteFilter extends DefaultZuulStrategyRouteFilter {
    private static final String DEFAULT_A_ROUTE_VERSION = "{\"discovery-guide-service-a\":\"1.0\", \"discovery-guide-service-b\":\"1.1\"}";
    private static final String DEFAULT_B_ROUTE_VERSION = "{\"discovery-guide-service-a\":\"1.1\", \"discovery-guide-service-b\":\"1.0\"}";

    @Value("${a.route.version:" + DEFAULT_A_ROUTE_VERSION + "}")
    private String aRouteVersion;

    @Value("${b.route.version:" + DEFAULT_B_ROUTE_VERSION + "}")
    private String bRouteVersion;

    @Override
    public String getRouteVersion() {
        String user = strategyContextHolder.getHeader("user");

        if (StringUtils.equals(user, "zhangsan")) {
            return aRouteVersion;
        } else if (StringUtils.equals(user, "lisi")) {
            return bRouteVersion;
        }

        return super.getRouteVersion();
    }
}

在配置類裏@Bean方式進行過濾類建立,覆蓋框架內置的過濾類

@Bean
public ZuulStrategyRouteFilter zuulStrategyRouteFilter() {
    return new MyZuulStrategyRouteFilter();
}

經過業務參數在策略類中自定義灰度路由策略

經過策略方式自定義灰度路由策略。下面代碼既適用於Zuul和Spring Cloud Gateway網關,也適用於Service微服務,同時全鏈路中網關和服務都必須加該方式

// 實現了組合策略,版本路由策略+區域路由策略+IP和端口路由策略+自定義策略
public class MyDiscoveryEnabledStrategy extends DefaultDiscoveryEnabledStrategy {
    private static final Logger LOG = LoggerFactory.getLogger(MyDiscoveryEnabledStrategy.class);

    // 對Rest調用傳來的Header參數(例如:mobile)作策略
    @Override
    public boolean apply(Server server) {
        String mobile = strategyContextHolder.getHeader("mobile");
        String serviceId = pluginAdapter.getServerServiceId(server);
        String version = pluginAdapter.getServerVersion(server);
        String region = pluginAdapter.getServerRegion(server);

        LOG.info("負載均衡用戶定製觸發:mobile={}, serviceId={}, version={}, region={}", mobile, serviceId, version, region);

        if (StringUtils.isNotEmpty(mobile)) {
            // 手機號以移動138開頭,路由到1.0版本的服務上
            if (mobile.startsWith("138") && StringUtils.equals(version, "1.0")) {
                return true;
                // 手機號以聯通133開頭,路由到2.0版本的服務上
            } else if (mobile.startsWith("133") && StringUtils.equals(version, "1.1")) {
                return true;
            } else {
                // 其它狀況,直接拒絕請求
                return false;
            }
        }

        return true;
    }
}

在配置類裏@Bean方式進行策略類建立

@Bean
public DiscoveryEnabledStrategy discoveryEnabledStrategy() {
    return new MyDiscoveryEnabledStrategy();
}

在網關和服務中支持基於Rest Header傳遞的自定義灰度路由策略,除此以外,服務還支持基於Rpc方法參數傳遞的自定義灰度路由策略,它包括接口名、方法名、參數名或參數值等多種形式。下面的示例表示在服務中同時開啓基於Rest Header傳遞和Rpc方法參數傳遞的自定義組合式灰度路由策略

// 實現了組合策略,版本路由策略+區域路由策略+IP和端口路由策略+自定義策略
public class MyDiscoveryEnabledStrategy implements DiscoveryEnabledStrategy {
    private static final Logger LOG = LoggerFactory.getLogger(MyDiscoveryEnabledStrategy.class);

    @Autowired
    private PluginAdapter pluginAdapter;

    @Autowired
    private ServiceStrategyContextHolder serviceStrategyContextHolder;

    @Override
    public boolean apply(Server server) {
        boolean enabled = applyFromHeader(server);
        if (!enabled) {
            return false;
        }

        return applyFromMethod(server);
    }

    // 根據Rest調用傳來的Header參數(例如:mobile),選取執行調用請求的服務實例
    private boolean applyFromHeader(Server server) {
        String mobile = serviceStrategyContextHolder.getHeader("mobile");
        String serviceId = pluginAdapter.getServerServiceId(server);
        String version = pluginAdapter.getServerVersion(server);
        String region = pluginAdapter.getServerRegion(server);

        LOG.info("負載均衡用戶定製觸發:mobile={}, serviceId={}, version={}, region={}", mobile, serviceId, version, region);

        if (StringUtils.isNotEmpty(mobile)) {
            // 手機號以移動138開頭,路由到1.0版本的服務上
            if (mobile.startsWith("138") && StringUtils.equals(version, "1.0")) {
                return true;
                // 手機號以聯通133開頭,路由到2.0版本的服務上
            } else if (mobile.startsWith("133") && StringUtils.equals(version, "1.1")) {
                return true;
            } else {
                // 其它狀況,直接拒絕請求
                return false;
            }
        }

        return true;
    }

    // 根據RPC調用傳來的方法參數(例如接口名、方法名、參數名或參數值等),選取執行調用請求的服務實例
    // 本示例只做用在discovery-guide-service-a服務上
    @SuppressWarnings("unchecked")
    private boolean applyFromMethod(Server server) {
        Map<String, Object> attributes = serviceStrategyContextHolder.getRpcAttributes();
        String serviceId = pluginAdapter.getServerServiceId(server);
        String version = pluginAdapter.getServerVersion(server);
        String region = pluginAdapter.getServerRegion(server);

        LOG.info("負載均衡用戶定製觸發:attributes={}, serviceId={}, version={}, region={}", attributes, serviceId, version, region);

        if (attributes.containsKey(ServiceStrategyConstant.PARAMETER_MAP)) {
            Map<String, Object> parameterMap = (Map<String, Object>) attributes.get(ServiceStrategyConstant.PARAMETER_MAP);
            String value = parameterMap.get("value").toString();
            if (StringUtils.isNotEmpty(value)) {
                // 輸入值包含dev,路由到dev區域的服務上
                if (value.contains("dev") && StringUtils.equals(region, "dev")) {
                    return true;
                    // 輸入值包含qa,路由到qa區域的服務上
                } else if (value.contains("qa") && StringUtils.equals(region, "qa")) {
                    return true;
                } else {
                    // 其它狀況,直接經過請求
                    return true;
                }
            }
        }

        return true;
    }
}

須要經過以下開關開啓該功能

# 啓動和關閉路由策略的時候,對RPC方式的調用攔截。缺失則默認爲false
spring.application.strategy.rpc.intercept.enabled=true

配置前端灰度&網關灰度路由組合式策略

當前端(例如:APP)和後端微服務同時存在多個版本時,能夠執行「前端灰度&網關灰度路由組合式策略」

例如:前端存在1.0和2.0版本,微服務存在1.0和2.0版本,因爲存在版本不兼容的狀況(前端1.0版本只能調用微服務的1.0版本,前端2.0版本只能調用微服務的2.0版本),那麼前端調用網關時候,能夠經過Header傳遞它的版本號給網關,網關根據前端版本號,去路由對應版本的微服務

該場景能夠用「經過業務參數在網關過濾器中自定義灰度路由策略」的方案來解決,以下:

<?xml version="1.0" encoding="UTF-8"?>
<rule>
    <strategy-customization>
        <conditions>
            <condition id="condition1" header="app-version=1.0" version-id="version-route1"/>
            <condition id="condition2" header="app-version=2.0" version-id="version-route2"/>
        </conditions>

        <routes>
            <route id="version-route1" type="version">{"discovery-guide-service-a":"1.0", "discovery-guide-service-b":"1.0"}</route>
            <route id="version-route2" type="version">{"discovery-guide-service-a":"1.1", "discovery-guide-service-b":"1.1"}</route>
        </routes>
    </strategy-customization>
</rule>

上述配置,模擬出全鏈路中,兩條獨立不受干擾的調用路徑:

1. APP v1.0 -> 網關 -> A服務 v1.0 -> B服務 v1.0
2. APP v1.1 -> 網關 -> A服務 v1.1 -> B服務 v1.1

基於訂閱方式的全鏈路灰度發佈規則

在Nacos配置中心,增長全鏈路灰度發佈規則
注意:該功能和網關灰度路由和灰度權重功能會疊加,爲了避免影響演示效果,請先清除網關灰度路由的策略

配置全鏈路灰度匹配規則

版本匹配灰度規則

增長版本匹配的灰度規則,Group爲discovery-guide-group,Data Id爲discovery-guide-group(全局發佈,二者都是組名),規則內容以下,實現a服務1.0版本只能訪問b服務1.0版本,a服務1.1版本只能訪問b服務1.1版本:

<?xml version="1.0" encoding="UTF-8"?>
<rule>
    <discovery>
        <version>
            <service consumer-service-name="discovery-guide-service-a" provider-service-name="discovery-guide-service-b" consumer-version-value="1.0" provider-version-value="1.0"/>
            <service consumer-service-name="discovery-guide-service-a" provider-service-name="discovery-guide-service-b" consumer-version-value="1.1" provider-version-value="1.1"/>
        </version>
    </discovery>
</rule>

Alt text

區域匹配灰度規則

增長區域匹配的灰度規則,Group爲discovery-guide-group,Data Id爲discovery-guide-group(全局發佈,二者都是組名),規則內容以下,實現dev區域的a服務只能訪問dev區域的b服務,qa區域的a服務只能訪問qa區域的b服務:

<?xml version="1.0" encoding="UTF-8"?>
<rule>
    <discovery>
        <region>
            <service consumer-service-name="discovery-guide-service-a" provider-service-name="discovery-guide-service-b" consumer-region-value="dev" provider-region-value="dev"/>
            <service consumer-service-name="discovery-guide-service-a" provider-service-name="discovery-guide-service-b" consumer-region-value="qa" provider-region-value="qa"/>
        </region>
    </discovery>
</rule>

Alt text

配置全鏈路灰度權重規則

全局版本權重灰度規則

增長全局版本權重的灰度規則,Group爲discovery-guide-group,Data Id爲discovery-guide-group(全局發佈,二者都是組名),規則內容以下,實現版本爲1.0的服務提供90%的流量,版本爲1.1的服務提供10%的流量:

<?xml version="1.0" encoding="UTF-8"?>
<rule>
    <discovery>
        <weight>
            <version provider-weight-value="1.0=90;1.1=10"/>
        </weight>
    </discovery>
</rule>

Alt text

局部版本權重灰度規則

增長局部版本權重的灰度規則,Group爲discovery-guide-group,Data Id爲discovery-guide-group(全局發佈,二者都是組名),規則內容以下,實現a服務1.0版本提供90%的流量,1.1版本提供10%的流量;b服務1.0版本提供20%的流量,1.1版本提供80%的流量:

<?xml version="1.0" encoding="UTF-8"?>
<rule>
    <discovery>
        <weight>
            <service provider-service-name="discovery-guide-service-a" provider-weight-value="1.0=90;1.1=10" type="version"/>
            <service provider-service-name="discovery-guide-service-b" provider-weight-value="1.0=20;1.1=80" type="version"/>
        </weight>
    </discovery>
</rule>

Alt text

全局區域權重灰度規則

增長全局區域權重的灰度規則,Group爲discovery-guide-group,Data Id爲discovery-guide-group(全局發佈,二者都是組名),規則內容以下,實現區域爲dev的服務提供90%的流量,區域爲qa的服務提供10%的流量:

<?xml version="1.0" encoding="UTF-8"?>
<rule>
    <discovery>
        <weight>
            <region provider-weight-value="dev=90;qa=10"/>
        </weight>
    </discovery>
</rule>

Alt text

局部區域權重灰度規則

增長局部區域權重的灰度規則,Group爲discovery-guide-group,Data Id爲discovery-guide-group(全局發佈,二者都是組名),規則內容以下,實現a服務dev區域提供90%的流量,qa區域提供10%的流量;b服務dev區域提供20%的流量,qa區域提供80%的流量:

<?xml version="1.0" encoding="UTF-8"?>
<rule>
    <discovery>
        <weight>
            <service provider-service-name="discovery-guide-service-a" provider-weight-value="dev=90;qa=10" type="region"/>
            <service provider-service-name="discovery-guide-service-b" provider-weight-value="dev=20;qa=80" type="region"/>
        </weight>
    </discovery>
</rule>

Alt text

注意:局部權重優先級高於全局權重,版本權重優先級高於區域權重

請執行Postman操做,請仔細觀察服務被隨機權重調用到的機率

配置全鏈路灰度權重&灰度版本組合式規則

增長組合式的灰度規則,Group爲discovery-guide-group,Data Id爲discovery-guide-group(全局發佈,二者都是組名),規則內容以下,實現功能:

  • a服務1.0版本向網關提供90%的流量,1.1版本向網關提供10%的流量
  • a服務1.0版本只能訪問b服務1.0版本,1.1版本只能訪問b服務1.1版本

該功能的意義是,網關隨機權重調用服務,而服務鏈路按照版本匹配方式調用

<?xml version="1.0" encoding="UTF-8"?>
<rule>
    <discovery>
        <version>
            <service consumer-service-name="discovery-guide-service-a" provider-service-name="discovery-guide-service-b" consumer-version-value="1.0" provider-version-value="1.0"/>
            <service consumer-service-name="discovery-guide-service-a" provider-service-name="discovery-guide-service-b" consumer-version-value="1.1" provider-version-value="1.1"/>
        </version>

        <weight>
            <service consumer-service-name="discovery-guide-gateway" provider-service-name="discovery-guide-service-a" provider-weight-value="1.0=90;1.1=10" type="version"/>
            <service consumer-service-name="discovery-guide-zuul" provider-service-name="discovery-guide-service-a" provider-weight-value="1.0=90;1.1=10" type="version"/>
        </weight>
    </discovery>
</rule>

Alt text

圖形化界面驗證

  • 下載源碼主頁的工程,並導入IDE
  • 啓動源碼工程下的discovery-springcloud-example-console/ConsoleApplication
  • 啓動源碼工程下的discovery-console-desktop/ConsoleLauncher
  • 經過admin/admin登陸,點擊「顯示服務拓撲」按鈕,將呈現以下界面
    Alt text
  • 在加入上述規則前,選中網關節點,右鍵點擊「執行灰度路由」,在彈出路由界面中,依次加入「discovery-guide-service-a」和「discovery-guide-service-b」,點擊「執行路由」按鈕,將呈現以下界面
    Alt text
  • 在加入上述規則後,在路由界面中,再次點擊「執行路由」按鈕,將呈現以下界面
    Alt text

全鏈路服務隔離

元數據中的Group在必定意義上表明着系統ID或者系統邏輯分組,基於Group策略意味着只有同一個系統中的服務才能調用

註冊服務隔離

基於Group黑/白名單的策略,即當前的服務所在的Group,不在Group的黑名單或者在白名單裏,才容許被註冊。只須要在網關或者服務端,開啓以下配置便可:

# 啓動和關閉註冊的服務隔離(基於Group黑/白名單的策略)。缺失則默認爲false
spring.application.strategy.register.isolation.enabled=true

黑/白名單經過以下方式配置

spring.application.strategy.register.isolation.group.blacklist=
spring.application.strategy.register.isolation.group.whitelist=

消費端服務隔離

基於Group是否相同的策略,即消費端拿到的提供端列表,二者的Group必須相同。只須要在網關或者服務端,開啓以下配置便可:

# 啓動和關閉消費端的服務隔離(基於Group是否相同的策略)。缺失則默認爲false
spring.application.strategy.consumer.isolation.enabled=true

經過修改discovery-guide-service-b的Group名爲其它名稱,執行Postman調用,將發現從discovery-guide-service-a沒法拿到discovery-guide-service-b的任何實例。意味着在discovery-guide-service-a消費端進行了隔離

提供端服務隔離

基於Group是否相同的策略,即服務端被消費端調用,二者的Group必須相同,不然拒絕調用,異構系統能夠經過Header方式傳遞n-d-service-group值進行匹配。只須要在服務端(不適用網關),開啓以下配置便可:

# 啓動和關閉提供端的服務隔離(基於Group是否相同的策略)。缺失則默認爲false
spring.application.strategy.provider.isolation.enabled=true

# 灰度路由策略的時候,須要指定對業務RestController類的掃描路徑。此項配置做用於RPC方式的調用攔截和消費端的服務隔離兩項工做
spring.application.strategy.scan.packages=com.nepxion.discovery.guide.service.feign

在Postman調用,執行http://localhost:4001/invoke/abc,去調用discovery-guide-service-b服務,將出現以下異常。意味着在discovery-guide-service-b提供端進行了隔離

Reject to invoke because of isolation with different service group

Alt text
若是加上n-d-service-group=discovery-guide-group的Header,那麼二者保持Group相同,則調用經過。這是解決異構系統調用微服務被隔離的一種手段
Alt text

基於Sentinel的全鏈路服務限流熔斷降級權限和灰度融合

經過集成Sentinel,在服務端實現該功能

封裝NacosDataSource和ApolloDataSource,支持Nacos和Apollo兩個遠程配置中心,零代碼實現Sentinel功能。更多的遠程配置中心,請參照Sentinel官方的DataSource並自行集成

1. Nacos的Key格式:Group爲元數據中配置的[組名],Data Id爲[服務名]-[規則類型]
2. Apollo的Key格式:[組名]-[服務名]-[規則類型]

支持遠程配置中心和本地規則文件的讀取邏輯,即優先讀取遠程配置,若是不存在或者規則錯誤,則讀取本地規則文件。動態實現遠程配置中心對於規則的熱刷新

支持以下開關開啓該動能,默認是關閉的

# 啓動和關閉Sentinel限流降級熔斷權限等功能。缺失則默認爲false
spring.application.strategy.sentinel.enabled=true

原生Sentinel註解

參照下面代碼,爲接口方法增長@SentinelResource註解,value爲sentinel-resource,blockHandler和fallback是防禦其做用後須要執行的方法

@RestController
@ConditionalOnProperty(name = DiscoveryConstant.SPRING_APPLICATION_NAME, havingValue = "discovery-guide-service-b")
public class BFeignImpl extends AbstractFeignImpl implements BFeign {
    private static final Logger LOG = LoggerFactory.getLogger(BFeignImpl.class);

    @Override
    @SentinelResource(value = "sentinel-resource", blockHandler = "handleBlock", fallback = "handleFallback")
    public String invoke(@PathVariable(value = "value") String value) {
        value = doInvoke(value);

        LOG.info("調用路徑:{}", value);

        return value;
    }

    public String handleBlock(String value, BlockException e) {
        return value + "-> B server sentinel block, cause=" + e.getClass().getName() + ", rule=" + e.getRule() + ", limitApp=" + e.getRuleLimitApp();
    }

    public String handleFallback(String value) {
        return value + "-> B server sentinel fallback";
    }
}

原生Sentinel規則

原生Sentinel規則的用法,請參照Sentinel官方文檔

流控規則

增長服務discovery-guide-service-b的規則,Group爲discovery-guide-group,Data Id爲discovery-guide-service-b-sentinel-flow,規則內容以下:

[
    {
        "resource": "sentinel-resource",
        "limitApp": "default",
        "grade": 1,
        "count": 1,
        "strategy": 0,
        "refResource": null,
        "controlBehavior": 0,
        "warmUpPeriodSec": 10,
        "maxQueueingTimeMs": 500,
        "clusterMode": false,
        "clusterConfig": null
    }
]

Alt text

降級規則

增長服務discovery-guide-service-b的規則,Group爲discovery-guide-group,Data Id爲discovery-guide-service-b-sentinel-degrade,規則內容以下:

[
    {
        "resource": "sentinel-resource",
        "limitApp": "default",
        "count": 2,
        "timeWindow": 10,
        "grade": 0,
        "passCount": 0
    }
]

Alt text

受權規則

增長服務discovery-guide-service-b的規則,Group爲discovery-guide-group,Data Id爲discovery-guide-service-b-sentinel-authority,規則內容以下:

[
    {
        "resource": "sentinel-resource",
        "limitApp": "discovery-guide-service-a",
        "strategy": 0
    }
]

Alt text

系統規則

增長服務discovery-guide-service-b的規則,Group爲discovery-guide-group,Data Id爲discovery-guide-service-b-sentinel-system,規則內容以下:

[
    {
        "resource": null,
        "limitApp": null,
        "highestSystemLoad": -1.0,
        "highestCpuUsage": -1.0,
        "qps": 200.0,
        "avgRt": -1,
        "maxThread": -1
    }
]

Alt text

熱點參數流控規則

增長服務discovery-guide-service-b的規則,Group爲discovery-guide-group,Data Id爲discovery-guide-service-b-sentinel-param-flow,規則內容以下:

[
    {
        "resource": "sentinel-resource",
        "limitApp": "default",
        "grade": 1,
        "paramIdx": 0,
        "count": 1,
        "controlBehavior": 0,
        "maxQueueingTimeMs": 0,
        "burstCount": 0,
        "durationInSec": 1,
        "paramFlowItemList": [],
        "clusterMode": false
    }
]

Alt text

基於灰度路由和Sentinel-LimitApp擴展的防禦機制

該方式對於上面5種規則都有效,這裏以受權規則展開闡述

受權規則中,limitApp,若是有多個,能夠經過「,」分隔。"strategy": 0 表示白名單,"strategy": 1 表示黑名單

基於服務名的防禦機制

修改配置項Sentinel Request Origin Key爲服務名的Header名稱,修改受權規則中limitApp爲對應的服務名,可實現基於服務名的防禦機制

配置項,該配置項默認爲n-d-service-id,能夠不配置

spring.application.strategy.service.sentinel.request.origin.key=n-d-service-id

增長服務discovery-guide-service-b的規則,Group爲discovery-guide-group,Data Id爲discovery-guide-service-b-sentinel-authority,規則內容以下,表示全部discovery-guide-service-a服務容許訪問discovery-guide-service-b服務

[
    {
        "resource": "sentinel-resource",
        "limitApp": "discovery-guide-service-a",
        "strategy": 0
    }
]

基於灰度組的防禦機制

修改配置項Sentinel Request Origin Key爲灰度組的Header名稱,修改受權規則中limitApp爲對應的組名,可實現基於組名的防禦機制

配置項

spring.application.strategy.service.sentinel.request.origin.key=n-d-service-group

增長服務discovery-guide-service-b的規則,Group爲discovery-guide-group,Data Id爲discovery-guide-service-b-sentinel-authority,規則內容以下,表示隸屬my-group組的全部服務都容許訪問服務discovery-guide-service-b

[
    {
        "resource": "sentinel-resource",
        "limitApp": "my-group",
        "strategy": 0
    }
]

基於灰度版本的防禦機制

修改配置項Sentinel Request Origin Key爲灰度版本的Header名稱,修改受權規則中limitApp爲對應的版本,可實現基於版本的防禦機制

配置項

spring.application.strategy.service.sentinel.request.origin.key=n-d-service-version

增長服務discovery-guide-service-b的規則,Group爲discovery-guide-group,Data Id爲discovery-guide-service-b-sentinel-authority,規則內容以下,表示版本爲1.0的全部服務都容許訪問服務discovery-guide-service-b

[
    {
        "resource": "sentinel-resource",
        "limitApp": "1.0",
        "strategy": 0
    }
]

基於灰度區域的防禦機制

修改配置項Sentinel Request Origin Key爲灰度區域的Header名稱,修改受權規則中limitApp爲對應的區域,可實現基於區域的防禦機制

配置項

spring.application.strategy.service.sentinel.request.origin.key=n-d-service-region

增長服務discovery-guide-service-b的規則,Group爲discovery-guide-group,Data Id爲discovery-guide-service-b-sentinel-authority,規則內容以下,表示區域爲dev的全部服務都容許訪問服務discovery-guide-service-b

[
    {
        "resource": "sentinel-resource",
        "limitApp": "dev",
        "strategy": 0
    }
]

基於機器地址和端口的防禦機制

修改配置項Sentinel Request Origin Key爲灰度區域的Header名稱,修改受權規則中limitApp爲對應的區域值,可實現基於機器地址和端口的防禦機制

配置項

spring.application.strategy.service.sentinel.request.origin.key=n-d-service-address

增長服務discovery-guide-service-b的規則,Group爲discovery-guide-group,Data Id爲discovery-guide-service-b-sentinel-authority,規則內容以下,表示地址和端口爲192.168.0.88:8081和192.168.0.88:8082的服務都容許訪問服務discovery-guide-service-b

[
    {
        "resource": "sentinel-resource",
        "limitApp": "192.168.0.88:8081,192.168.0.88:8082",
        "strategy": 0
    }
]

自定義業務參數的組合式防禦機制

經過適配類實現自定義業務參數的組合式防禦機制

// 自定義版本號+用戶名,實現組合式熔斷
public class MyServiceSentinelRequestOriginAdapter extends DefaultServiceSentinelRequestOriginAdapter {
    @Override
    public String parseOrigin(HttpServletRequest request) {
        String version = request.getHeader(DiscoveryConstant.N_D_SERVICE_VERSION);
        String user = request.getHeader("user");

        return version + "&" + user;
    }
}

在配置類裏@Bean方式進行適配類建立

@Bean
public ServiceSentinelRequestOriginAdapter ServiceSentinelRequestOriginAdapter() {
    return new MyServiceSentinelRequestOriginAdapter();
}

增長服務discovery-guide-service-b的規則,Group爲discovery-guide-group,Data Id爲discovery-guide-service-b-sentinel-authority,規則內容以下,表示版本爲1.0且傳入的Http Header的user=zhangsan,同時知足這兩個條件下的全部服務都容許訪問服務discovery-guide-service-b

[
    {
        "resource": "sentinel-resource",
        "limitApp": "1.0&zhangsan",
        "strategy": 0
    }
]

運行效果

  • 當傳遞的Http Header中user=zhangsan,當全鏈路調用中,API網關負載均衡discovery-guide-service-a服務到1.0版本後再去調用discovery-guide-service-b服務,最終調用成功

Alt text

  • 當傳遞的Http Header中user=lisi,不知足條件,最終調用在discovery-guide-service-b服務端被拒絕掉

Alt text

  • 當傳遞的Http Header中user=zhangsan,知足條件之一,當全鏈路調用中,API網關負載均衡discovery-guide-service-a服務到1.1版本後再去調用discovery-guide-service-b服務,不知足version=1.0的條件,最終調用在discovery-guide-service-b服務端被拒絕掉

Alt text

基於Hystrix的全鏈路服務限流熔斷和灰度融合

經過引入Hystrix組件實現服務限流熔斷的功能,在執行灰度發佈和路由時候,線程池隔離模式下進行調用會丟失上下文,那麼須要下述步驟避免該狀況。下面步驟同時適用於網關端和服務端

  • Pom引入
<!-- 當服務用Hystrix作線程隔離的時候,才須要導入下面的包 -->
<dependency>
    <groupId>com.nepxion</groupId>
    <artifactId>discovery-plugin-strategy-starter-hystrix</artifactId>
    <version>${discovery.version}</version>
</dependency>
  • 配置開啓
# 開啓服務端實現Hystrix線程隔離模式作服務隔離時,必須把spring.application.strategy.hystrix.threadlocal.supported設置爲true,同時要引入discovery-plugin-strategy-starter-hystrix包,不然線程切換時會發生ThreadLocal上下文對象丟失。缺失則默認爲false
spring.application.strategy.hystrix.threadlocal.supported=true

全鏈路灰度調用鏈

灰度調用鏈主要包括以下6個參數。使用者能夠自行定義要傳遞的調用鏈參數,例如:traceId, spanId等;也能夠自行定義要傳遞的業務調用鏈參數,例如:mobile, user等

1. n-d-service-group - 服務所屬組或者應用
2. n-d-service-type - 服務類型,分爲「網關」和「服務」
3. n-d-service-id - 服務ID
4. n-d-service-address - 服務地址,包括Host和Port
5. n-d-service-version - 服務版本
6. n-d-service-region - 服務所屬區域

灰度調用鏈輸出分爲Header方式和日誌方式

Header輸出方式

Header方式框架內部集成

  • Spring Cloud Gateway網關端自行會傳輸Header值(參考Discovery源碼中的AbstractGatewayStrategyRouteFilter.java)
  • Zuul網關端自行會傳輸Header值(參考Discovery源碼中的AbstractZuulStrategyRouteFilter.java)
  • 服務端經過Feign和RestTemplate攔截器傳輸Header值(參考Discovery源碼中的FeignStrategyInterceptor.java和RestTemplateStrategyInterceptor.java)

日誌輸出方式

  • Spring Cloud Gateway網關

繼承GatewayStrategyTracer.java,trace方法裏把6個參數(參考父類裏debugTraceHeader方法)或者更多經過MDC方式輸出到日誌

// 自定義調用鏈和灰度調用鏈經過MDC輸出到日誌。使用者集成時候,關注trace方法中的MDC.put和release方法中MDC.clear代碼部分便可
public class MyGatewayStrategyTracer extends DefaultGatewayStrategyTracer {
    @Override
    public void trace(ServerWebExchange exchange) {
        super.trace(exchange);
        
        // 輸出到日誌
        MDC.put("traceid", "traceid=" + strategyContextHolder.getHeader("traceid"));
        ...

        MDC.put(DiscoveryConstant.N_D_SERVICE_GROUP, "服務組名=" + strategyContextHolder.getHeader(DiscoveryConstant.N_D_SERVICE_GROUP));
        ...
    }

    @Override
    public void release(ServerWebExchange exchange) {
        MDC.clear();
    }
}

在配置類裏@Bean方式進行調用鏈類建立,覆蓋框架內置的調用鏈類

@Bean
@ConditionalOnProperty(value = StrategyConstant.SPRING_APPLICATION_STRATEGY_TRACE_ENABLED, matchIfMissing = false)
public GatewayStrategyTracer gatewayStrategyTracer() {
    return new MyGatewayStrategyTracer();
}
  • Zuul網關

繼承ZuulStrategyTracer.java,trace方法裏把6個參數(參考父類裏debugTraceHeader方法)或者更多經過MDC方式輸出到日誌

// 自定義調用鏈和灰度調用鏈經過MDC輸出到日誌。使用者集成時候,關注trace方法中的MDC.put和release方法中MDC.clear代碼部分便可
public class MyZuulStrategyTracer extends DefaultZuulStrategyTracer {
    @Override
    public void trace(RequestContext context) {
        super.trace(context);
        
        // 輸出到日誌
        MDC.put("traceid", "traceid=" + strategyContextHolder.getHeader("traceid"));
        ...

        MDC.put(DiscoveryConstant.N_D_SERVICE_GROUP, "服務組名=" + strategyContextHolder.getHeader(DiscoveryConstant.N_D_SERVICE_GROUP));
        ...
    }

    @Override
    public void release(RequestContext context) {
        MDC.clear();
    }
}

在配置類裏@Bean方式進行調用鏈類建立,覆蓋框架內置的調用鏈類

@Bean
@ConditionalOnProperty(value = StrategyConstant.SPRING_APPLICATION_STRATEGY_TRACE_ENABLED, matchIfMissing = false)
public ZuulStrategyTracer zuulStrategyTracer() {
    return new MyZuulStrategyTracer();
}
  • Service服務

繼承ServiceStrategyTracer.java,trace方法裏把6個參數(參考父類裏debugTraceLocal方法)或者更多經過MDC方式輸出到日誌

// 自定義調用鏈和灰度調用鏈經過MDC輸出到日誌。使用者集成時候,關注trace方法中的MDC.put和release方法中MDC.clear代碼部分便可
public class MyServiceStrategyTracer extends DefaultServiceStrategyTracer {
    @Override
    public void trace(ServiceStrategyTracerInterceptor interceptor, MethodInvocation invocation) {
        super.trace(interceptor, invocation);
        
        // 輸出到日誌
        MDC.put("traceid", "traceid=" + strategyContextHolder.getHeader("traceid"));
        ...

        MDC.put(DiscoveryConstant.N_D_SERVICE_GROUP, "服務組名=" + pluginAdapter.getGroup());
        ...
    }

    @Override
    public void release(ServiceStrategyTracerInterceptor interceptor, MethodInvocation invocation) {
        MDC.clear();
    }
}

在配置類裏@Bean方式進行調用鏈類建立,覆蓋框架內置的調用鏈類

@Bean
@ConditionalOnProperty(value = StrategyConstant.SPRING_APPLICATION_STRATEGY_TRACE_ENABLED, matchIfMissing = false)
public ServiceStrategyTracer serviceStrategyTracer() {
    return new MyServiceStrategyTracer();
}

請參考在IDE控制檯打印的結果
Alt text

對於調用鏈功能的開啓和關閉,須要經過以下開關作控制:

# 啓動和關閉調用鏈。缺失則默認爲false
spring.application.strategy.trace.enabled=true
# 啓動和關閉調用鏈的Debug日誌打印,注意每調用一次都會打印一次,會對性能有所影響,建議壓測環境和生產環境關閉。缺失則默認爲false
spring.application.strategy.trace.debug.enabled=true

全鏈路Header傳遞

框架會默認把相關的Header,進行全鏈路傳遞,能夠經過以下配置進行。除此以外,凡是以「n-d-」開頭的任何Header,框架都會默認全鏈路傳遞

# 啓動和關閉路由策略的時候,對REST方式的調用攔截。缺失則默認爲true
spring.application.strategy.rest.intercept.enabled=true
# 啓動和關閉Header傳遞的Debug日誌打印,注意每調用一次都會打印一次,會對性能有所影響,建議壓測環境和生產環境關閉。缺失則默認爲false
spring.application.strategy.rest.intercept.debug.enabled=true
# 灰度路由策略的時候,對REST方式調用攔截的時候(支持Feign或者RestTemplate調用),但願把來自外部自定義的Header參數(用於框架內置上下文Header,例如:traceid, spanid等)傳遞到服務裏,那麼配置以下值。若是多個用「;」分隔,不容許出現空格
spring.application.strategy.context.request.headers=traceid;spanid
# 灰度路由策略的時候,對REST方式調用攔截的時候(支持Feign或者RestTemplate調用),但願把來自外部自定義的Header參數(用於業務系統子定義Header,例如:mobile)傳遞到服務裏,那麼配置以下值。若是多個用「;」分隔,不容許出現空格
spring.application.strategy.business.request.headers=user;mobile

原生的Feign Header傳遞可使用RequestInterceptor攔截器實現,原生的RestTemplate Header傳遞可使用ClientHttpRequestInterceptor攔截器實現

本框架也使用這些原生的攔截器用做Header在灰度功能上的傳遞,爲了不使用者再去多建立一層攔截器,框架抽象出兩個攔截適配器,用法和原生的兩個攔截器一致,能夠幫助使用者實現自定義Header的傳遞

自定義Feign-Header傳遞

實現FeignStrategyInterceptorAdapter.java,在apply方法里加入自定義的Header傳遞

// 自定義Feign攔截器中的Header傳遞
public class MyFeignStrategyInterceptorAdapter extends DefaultFeignStrategyInterceptorAdapter {
    @Override
    public void apply(RequestTemplate requestTemplate) {
        requestTemplate.header("n-d-my-id", "123");
    }
}

在配置類裏@Bean方式進行攔截適配器建立

@Bean
public FeignStrategyInterceptorAdapter feignStrategyInterceptorAdapter() {
    return new MyFeignStrategyInterceptorAdapter();
}

自定義RestTemplate-Header傳遞

實現RestTemplateStrategyInterceptorAdapter.java,在intercept方法里加入自定義的Header傳遞

// 自定義RestTemplate攔截器中的Header傳遞
public class MyRestTemplateStrategyInterceptorAdapter extends DefaultRestTemplateStrategyInterceptorAdapter {
    @Override
    public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException {
        HttpHeaders headers = request.getHeaders();
        headers.add("n-d-my-id", "456");

        return execution.execute(request, body);
    }
}

在配置類裏@Bean方式進行攔截適配器建立

@Bean
public RestTemplateStrategyInterceptorAdapter restTemplateStrategyInterceptorAdapter() {
    return new MyRestTemplateStrategyInterceptorAdapter();
}

Docker容器化和Kubernetes平臺

Docker容器化

  • 搭建Windows10操做系統或者Linux操做系統下的Docker環境
  • 全自動部署和運行Docker化的服務。在根目錄下
    • 一鍵運行install-docker-gateway.bat或者.sh,把Spring Cloud Gateway網關全自動部署且運行起來
    • 一鍵運行install-docker-zuul.bat或者.sh,把Zuul網關全自動部署且運行起來
    • 一鍵運行install-docker-service-xx.bat或者.sh,把微服務全自動部署且運行起來。注意,必須依次運行,即等上一個部署完畢後才能執行下一個

Alt text

Kubernetes平臺

請自行研究

本文由博文多發平臺 OpenWrite 發佈!
本文由博客一文多發平臺 OpenWrite 發佈!

相關文章
相關標籤/搜索