自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

微服務(wù)如何灰度發(fā)布?你會(huì)嗎?

開發(fā) 架構(gòu)
通過 Spring Cloud 的擴(kuò)展組件和自定義路由策略,開發(fā)人員可以輕松實(shí)現(xiàn)灰度發(fā)布功能,確保在微服務(wù)架構(gòu)中安全、平滑地進(jìn)行版本迭代升級(jí)。

微服務(wù)中的灰度發(fā)布(又稱為金絲雀發(fā)布)是一種持續(xù)部署策略,它允許在正式環(huán)境的小部分用戶群體上先部署新版本的應(yīng)用程序或服務(wù),而不是一次性對(duì)所有用戶同時(shí)發(fā)布全新的版本。

這種方式有助于在生產(chǎn)環(huán)境中逐步驗(yàn)證新版本的穩(wěn)定性和兼容性,同時(shí)最小化潛在風(fēng)險(xiǎn),不影響大部分用戶的正常使用。

1.灰度發(fā)布關(guān)鍵步驟

在 Spring Cloud 微服務(wù)架構(gòu)中,實(shí)現(xiàn)灰度發(fā)布通常涉及到以下幾個(gè)方面:

  1. 流量分割

根據(jù)一定的策略(如用戶 ID、請(qǐng)求頭信息、IP 地址等)將流入的請(qǐng)求分配給不同版本的服務(wù)實(shí)例。

使用 Spring Cloud Gateway、Zuul 等 API 網(wǎng)關(guān)組件實(shí)現(xiàn)路由規(guī)則,將部分請(qǐng)求定向至新版本的服務(wù)節(jié)點(diǎn)。

  1. 版本標(biāo)識(shí)

新版本服務(wù)啟動(dòng)時(shí)會(huì)注冊(cè)帶有特定版本標(biāo)簽的服務(wù)實(shí)例到服務(wù)注冊(cè)中心(如 Eureka 或 Nacos)。

請(qǐng)求在路由時(shí)可以根據(jù)版本標(biāo)簽選擇相應(yīng)版本的服務(wù)實(shí)例。

  1. 監(jiān)控與評(píng)估

在灰度發(fā)布的階段,運(yùn)維團(tuán)隊(duì)會(huì)對(duì)新版本服務(wù)的性能、穩(wěn)定性以及用戶體驗(yàn)等方面進(jìn)行實(shí)時(shí)監(jiān)控和評(píng)估。

如果新版本表現(xiàn)良好,則可以逐漸擴(kuò)大灰度范圍直至全面替換舊版本。

  1. 故障恢復(fù)與回滾:若新版本出現(xiàn)問題,可通過快速撤銷灰度發(fā)布策略,使所有流量恢復(fù)到舊版本服務(wù),實(shí)現(xiàn)快速回滾,確保服務(wù)整體可用性。

通過 Spring Cloud 的擴(kuò)展組件和自定義路由策略,開發(fā)人員可以輕松實(shí)現(xiàn)灰度發(fā)布功能,確保在微服務(wù)架構(gòu)中安全、平滑地進(jìn)行版本迭代升級(jí)。

2.實(shí)現(xiàn)思路

灰色發(fā)布的常見實(shí)現(xiàn)思路有以下幾種:

  • 根據(jù)用戶劃分:根據(jù)用戶標(biāo)識(shí)或用戶組進(jìn)行劃分,在整個(gè)用戶群體中只選擇一小部分用戶獲得新功能。
  • 根據(jù)地域劃分:在不同地區(qū)或不同節(jié)點(diǎn)上進(jìn)行劃分,在其中的一小部分地區(qū)或節(jié)點(diǎn)進(jìn)行新功能的發(fā)布。
  • 根據(jù)流量劃分:根據(jù)流量的百分比或請(qǐng)求次數(shù)進(jìn)行劃分,只將一部分請(qǐng)求流量引導(dǎo)到新功能上。

而在生產(chǎn)環(huán)境中,比較常用的是根據(jù)用戶標(biāo)識(shí)來實(shí)現(xiàn)灰色發(fā)布,也就是說先讓一小部分用戶體驗(yàn)新功能,以發(fā)現(xiàn)新服務(wù)中可能存在的某種缺陷或不足。

3.底層實(shí)現(xiàn)

Spring Cloud 全鏈路灰色發(fā)布的關(guān)鍵實(shí)現(xiàn)思路如下圖所示:

圖片圖片

灰度發(fā)布的具體實(shí)現(xiàn)步驟如下:

  1. 前端程序在灰度測(cè)試的用戶 Header 頭中打上標(biāo)簽,例如在 Header 中添加“gray-tag: true”,其表示要進(jìn)行灰常測(cè)試(訪問灰度服務(wù)),而其他則為訪問正式服務(wù)。
  2. 在負(fù)載均衡器 Spring Cloud LoadBalancer 中,拿到 Header 中的“gray-tag”進(jìn)行判斷,如果此標(biāo)簽不為空,并等于“true”的話,表示要訪問灰度發(fā)布的服務(wù),否則只訪問正式的服務(wù)。
  3. 在網(wǎng)關(guān) Spring Cloud Gateway 中,將 Header 標(biāo)簽“gray-tag: true”繼續(xù)往下一個(gè)調(diào)用服務(wù)中傳遞。
  4. 在后續(xù)的調(diào)用服務(wù)中,需要實(shí)現(xiàn)以下兩個(gè)關(guān)鍵功能:

在負(fù)載均衡器 Spring Cloud LoadBalancer 中,判斷灰度發(fā)布標(biāo)簽,將請(qǐng)求分發(fā)到對(duì)應(yīng)服務(wù)。

將灰度發(fā)布標(biāo)簽(如果存在),繼續(xù)傳遞給下一個(gè)調(diào)用的服務(wù)。

經(jīng)過第四步的反復(fù)傳遞之后,整個(gè) Spring Cloud 全鏈路的灰度發(fā)布就完成了。

4.具體實(shí)現(xiàn)

4.1 版本標(biāo)識(shí)

在灰度發(fā)布的執(zhí)行流程中,有一個(gè)核心的問題,如果在 Spring Cloud LoadBalancer 進(jìn)行服務(wù)調(diào)用時(shí),區(qū)分正式服務(wù)和灰度服務(wù)呢?

這個(gè)問題的解決方案是:在灰度服務(wù)既注冊(cè)中心的 MetaData(元數(shù)據(jù))中標(biāo)識(shí)自己為灰度服務(wù)即可,而元數(shù)據(jù)中沒有標(biāo)識(shí)(灰度服務(wù))的則為正式服務(wù),以 Nacos 為例,它的設(shè)置如下:

spring:
  application:
    name: gray-user-service
  cloud:
    nacos:
      discovery:
        username: nacos
        password: nacos
        server-addr: localhost:8848
        namespace: public
        register-enabled: true 
        metadata: { "gray-tag":"true" } # 標(biāo)識(shí)自己為灰度服務(wù)

4.2 負(fù)載均衡調(diào)用灰度服務(wù)

Spring Cloud LoadBalancer 判斷并調(diào)用灰度服務(wù)的關(guān)鍵實(shí)現(xiàn)代碼如下:

private Response<ServiceInstance> getInstanceResponse(List<ServiceInstance> instances,
                                                          Request request) {
        // 實(shí)例為空
        if (instances.isEmpty()) {
            if (log.isWarnEnabled()) {
                log.warn("No servers available for service: " + this.serviceId);
            }
            return new EmptyResponse();
        } else { // 服務(wù)不為空
            RequestDataContext dataContext = (RequestDataContext) request.getContext();
            HttpHeaders headers = dataContext.getClientRequest().getHeaders();
            // 判斷是否為灰度發(fā)布(請(qǐng)求)
            if (headers.get(GlobalVariables.GRAY_KEY) != null &&
                    headers.get(GlobalVariables.GRAY_KEY).get(0).equals("true")) {
                // 灰度發(fā)布請(qǐng)求,得到新服務(wù)實(shí)例列表
                List<ServiceInstance> findInstances = instances.stream().
                        filter(s -> s.getMetadata().get(GlobalVariables.GRAY_KEY) != null &&
                                s.getMetadata().get(GlobalVariables.GRAY_KEY).equals("true"))
                        .toList();
                if (findInstances.size() > 0) { // 存在灰度發(fā)布節(jié)點(diǎn)
                    instances = findInstances;
                }
            } else { // 查詢非灰度發(fā)布節(jié)點(diǎn)
                // 灰度發(fā)布測(cè)試請(qǐng)求,得到新服務(wù)實(shí)例列表
                instances = instances.stream().
                        filter(s -> s.getMetadata().get(GlobalVariables.GRAY_KEY) == null ||
                                !s.getMetadata().get(GlobalVariables.GRAY_KEY).equals("true"))
                        .toList();
            }
            // 隨機(jī)正數(shù)值 ++i( & 去負(fù)數(shù))
            int pos = this.position.incrementAndGet() & Integer.MAX_VALUE;
            // ++i 數(shù)值 % 實(shí)例數(shù) 取模 -> 輪詢算法
            int index = pos % instances.size();
            // 得到服務(wù)實(shí)例方法
            ServiceInstance instance = (ServiceInstance) instances.get(index);
            return new DefaultResponse(instance);
        }
    }

以上代碼為自定義負(fù)載均衡器,并使用了輪詢算法。如果 Header 中有灰度標(biāo)簽,則只查詢灰度服務(wù)的節(jié)點(diǎn)實(shí)例,否則則查詢出所有的正式節(jié)點(diǎn)實(shí)例(以供服務(wù)調(diào)用或服務(wù)轉(zhuǎn)發(fā))。

4.3 網(wǎng)關(guān)傳遞灰度標(biāo)識(shí)

要在網(wǎng)關(guān) Spring Cloud Gateway 中傳遞灰度標(biāo)識(shí),只需要在 Gateway 的全局自定義過濾器中設(shè)置 Response 的 Header 即可,具體實(shí)現(xiàn)代碼如下:

package com.example.gateway.config;

import com.loadbalancer.canary.common.GlobalVariables;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

@Component
public class LoadBalancerFilter implements GlobalFilter {
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        // 得到 request、response 對(duì)象
        ServerHttpRequest request = exchange.getRequest();
        ServerHttpResponse response = exchange.getResponse();
        if (request.getQueryParams().getFirst(GlobalVariables.GRAY_KEY) != null) {
            // 設(shè)置金絲雀標(biāo)識(shí)
            response.getHeaders().set(GlobalVariables.GRAY_KEY,
                    "true");
        }
        // 此步驟正常,執(zhí)行下一步
        return chain.filter(exchange);
    }
}

4.4 微服務(wù)中傳遞灰度標(biāo)簽

HTTP 調(diào)用工具 Openfeign,我們需要在微服務(wù)間繼續(xù)傳遞灰度標(biāo)簽,它的實(shí)現(xiàn)代碼如下:

import feign.RequestInterceptor;
import feign.RequestTemplate;
import jakarta.servlet.http.HttpServletRequest;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import java.util.Enumeration;
import java.util.LinkedHashMap;
import java.util.Map;

@Component
public class FeignRequestInterceptor implements RequestInterceptor {
    @Override
    public void apply(RequestTemplate template) {
        // 從 RequestContextHolder 中獲取 HttpServletRequest
        ServletRequestAttributes attributes = (ServletRequestAttributes)
                RequestContextHolder.getRequestAttributes();
        // 獲取 RequestContextHolder 中的信息
        Map<String, String> headers = getHeaders(attributes.getRequest());
        // 放入 openfeign 的 RequestTemplate 中
        for (Map.Entry<String, String> entry : headers.entrySet()) {
            template.header(entry.getKey(), entry.getValue());
        }
    }

    /**
     * 獲取原請(qǐng)求頭
     */
    private Map<String, String> getHeaders(HttpServletRequest request) {
        Map<String, String> map = new LinkedHashMap<>();
        Enumeration<String> enumeration = request.getHeaderNames();
        if (enumeration != null) {
            while (enumeration.hasMoreElements()) {
                String key = enumeration.nextElement();
                String value = request.getHeader(key);
                map.put(key, value);
            }
        }
        return map;
    }
}


責(zé)任編輯:武曉燕 來源: Java中文社群
相關(guān)推薦

2022-12-05 09:08:12

微服務(wù)灰度發(fā)布

2023-11-21 09:35:49

全量部署微服務(wù)

2025-04-03 08:00:00

灰度發(fā)布Java開發(fā)

2024-03-06 15:38:06

Spring微服務(wù)架構(gòu)擴(kuò)展組件

2021-06-09 09:42:50

SpringCloud微服務(wù)灰度發(fā)布

2021-10-18 08:52:42

技術(shù)

2024-06-04 07:58:31

架構(gòu)本質(zhì)微服務(wù)

2018-10-28 18:09:22

微服務(wù)Microservic架構(gòu)

2024-03-29 12:50:00

項(xiàng)目分層模型

2025-02-27 00:00:55

Dubbo服務(wù)不兼容

2024-06-07 09:13:23

2021-08-19 15:36:09

數(shù)據(jù)備份存儲(chǔ)備份策略

2020-02-08 16:46:29

微服務(wù)架構(gòu)復(fù)雜

2017-09-14 14:50:10

2020-01-09 15:30:32

微服務(wù)架構(gòu)互聯(lián)網(wǎng)

2022-03-29 08:30:15

微服務(wù)架構(gòu)單體架構(gòu)

2016-09-26 14:45:46

微服務(wù)

2021-12-29 08:30:48

微服務(wù)架構(gòu)開發(fā)

2024-11-06 16:27:12

2021-04-14 06:53:52

C# 修飾符 Public
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)