springcloud + nacos實現共用基礎服務(灰度版本)

来源:https://www.cnblogs.com/long88-club/archive/2022/05/10/16252113.html
-Advertisement-
Play Games

背景: 當我們使用微服務時,若想在本地聯調就需要啟動多個服務,為了避免本地啟動過多服務,現將註冊中心等基礎服務共用。當我們在服務A開發時,都是註冊到同一個nacos,這樣本地和開發環境的服務A就會同時存在,當調用服務時就會使用負載均衡選擇服務,導致我們無法正常調試介面。這時我們可以選擇使用灰度版本來 ...


背景:

當我們使用微服務時,若想在本地聯調就需要啟動多個服務,為了避免本地啟動過多服務,現將註冊中心等基礎服務共用。當我們在服務A開發時,都是註冊到同一個nacos,這樣本地和開發環境的服務A就會同時存在,當調用服務時就會使用負載均衡選擇服務,導致我們無法正常調試介面。這時我們可以選擇使用灰度版本來進行服務的選擇。

具體實現步驟如下:

1、我們在本地配置文件中添加版本頭

這樣我們服務註冊到nacos中點擊服務列表會發現服務中都會帶VERSION

spring:
  cloud:
    nacos:
      discovery:
        metadata:
          VERSION: zhangsan

2、添加灰度服務介面

public interface GrayLoadBalancer {

	/**
	 * 根據serviceId 篩選可用服務
	 * @param serviceId 服務ID
	 * @param request 當前請求
	 * @return ServiceInstance
	 */
	ServiceInstance choose(String serviceId, ServerHttpRequest request);

}

3、灰度過濾器

import lombok.extern.slf4j.Slf4j;
import org.apache.http.util.Asserts;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.loadbalancer.DefaultResponse;
import org.springframework.cloud.client.loadbalancer.LoadBalancerUriTools;
import org.springframework.cloud.client.loadbalancer.Response;
import org.springframework.cloud.gateway.config.LoadBalancerProperties;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.ReactiveLoadBalancerClientFilter;
import org.springframework.cloud.gateway.support.DelegatingServiceInstance;
import org.springframework.cloud.gateway.support.NotFoundException;
import org.springframework.cloud.gateway.support.ServerWebExchangeUtils;
import org.springframework.cloud.loadbalancer.support.LoadBalancerClientFactory;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.net.URI;

@Slf4j
@Component
public class GrayReactiveLoadBalancerClientFilter extends ReactiveLoadBalancerClientFilter {

    private final static String SCHEME = "lb";

    private static final int LOAD_BALANCER_CLIENT_FILTER_ORDER = 10150;
    private final GrayLoadBalancer grayLoadBalancer;
    private final LoadBalancerProperties loadBalancerProperties;

    public GrayReactiveLoadBalancerClientFilter(LoadBalancerClientFactory clientFactory, LoadBalancerProperties loadBalancerProperties, GrayLoadBalancer grayLoadBalancer) {
        super(clientFactory, loadBalancerProperties);
        this.loadBalancerProperties = loadBalancerProperties;
        this.grayLoadBalancer = grayLoadBalancer;
    }

    @Override
    public int getOrder() {
        return LOAD_BALANCER_CLIENT_FILTER_ORDER;
    }

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        URI url = exchange.getAttribute(ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR);
        String schemePrefix = exchange.getAttribute(ServerWebExchangeUtils.GATEWAY_SCHEME_PREFIX_ATTR);

        // 直接放行
        if (url == null || (!SCHEME.equals(url.getScheme()) && !SCHEME.equals(schemePrefix))) {
            return chain.filter(exchange);
        }
        // 保留原始url
        ServerWebExchangeUtils.addOriginalRequestUrl(exchange, url);

        if (log.isTraceEnabled()) {
            log.trace(ReactiveLoadBalancerClientFilter.class.getSimpleName() + " url before: " + url);
        }

        return choose(exchange).doOnNext(response -> {

            if (!response.hasServer()) {
                throw NotFoundException.create(loadBalancerProperties.isUse404(),
                        "Unable to find instance for " + url.getHost());
            }

            URI uri = exchange.getRequest().getURI();

            // if the `lb:<scheme>` mechanism was used, use `<scheme>` as the default,
            // if the loadbalancer doesn't provide one.
            String overrideScheme = null;
            if (schemePrefix != null) {
                overrideScheme = url.getScheme();
            }

            DelegatingServiceInstance serviceInstance = new DelegatingServiceInstance(response.getServer(),
                    overrideScheme);

            URI requestUrl = LoadBalancerUriTools.reconstructURI(serviceInstance, uri);

            if (log.isTraceEnabled()) {
                log.trace("LoadBalancerClientFilter url chosen: " + requestUrl);
            }
            exchange.getAttributes().put(ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR, requestUrl);
        }).then(chain.filter(exchange));
    }

    /**
     * 獲取實例
     * @param exchange ServerWebExchange
     * @return ServiceInstance
     */
    private Mono<Response<ServiceInstance>> choose(ServerWebExchange exchange) {
        URI uri = exchange.getAttribute(ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR);
        Asserts.notNull(uri, "uri");
        ServiceInstance serviceInstance = grayLoadBalancer.choose(uri.getHost(), exchange.getRequest());
        return Mono.just(new DefaultResponse(serviceInstance));
    }

}

4、基於客戶端版本號灰度路由

當我們調用服務帶版本號時會優先匹配帶版本號的服務,若找不到則會隨機選擇一個服務

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.cloud.gateway.support.NotFoundException;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.stereotype.Component;

import java.util.List;
import java.util.stream.Collectors;

@Slf4j
@RequiredArgsConstructor
@Component
public class VersionGrayLoadBalancer implements GrayLoadBalancer {

	private final DiscoveryClient discoveryClient;

	/**
	 * 根據serviceId 篩選可用服務
	 * @param serviceId 服務ID
	 * @param request 當前請求
	 * @return ServiceInstance
	 */
	@Override
	public ServiceInstance choose(String serviceId, ServerHttpRequest request) {
		List<ServiceInstance> instances = discoveryClient.getInstances(serviceId);

		// 註冊中心無實例 拋出異常
		if (CollUtil.isEmpty(instances)) {
			log.warn("No instance available for {}", serviceId);
			throw new NotFoundException("No instance available for " + serviceId);
		}

		// 獲取請求version,無則隨機返回可用實例
		String reqVersion = request.getHeaders().getFirst(CommonConstant.VERSION);
		if (StrUtil.isBlank(reqVersion)) {
			return instances.get(RandomUtil.randomInt(instances.size()));
		}

		// 遍歷可以實例元數據,若匹配則返回此實例
		List<ServiceInstance> availableList = instances.stream()
				.filter(instance -> reqVersion
						.equalsIgnoreCase(MapUtil.getStr(instance.getMetadata(), CommonConstant.VERSION)))
				.collect(Collectors.toList());

		if (CollUtil.isEmpty(availableList)) {
			return instances.get(RandomUtil.randomInt(instances.size()));
		}
		return availableList.get(RandomUtil.randomInt(availableList.size()));

	}
}


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 今天內容挺多,因為想的是必須在一天內把這個vuex完成,說實話這裡面要記得東西還是蠻多的,主要是分為原生的和簡便方法兩種都是vue官方定義的,只不過看你要用哪種,vuex感覺要是用熟練了不得了,直接可以把vue起飛了,數據到處用,那種起飛的感覺,曾經體驗過,所以這個應該還是可以多練練的。 明天進入v ...
  • 8 個你應該立即停止使用的無效 HTML 元素 HTML 規範的開發是一個漸進的過程,有時會出現問題。隨著時間的推移,許多元素和屬性被添加到 HTML 中,直到後來 Web 社區集體意識到有更好的方法時才被刪除。由於已棄用和過時的元素和屬性已經存在於網路上,因此許多現代瀏覽器繼續支持它們的使用。儘管 ...
  • 前言 在我們日常代碼開發過程中,組件的使用是必不可少的,我們也會去封裝組件。但是大家寫組件的風格各式各樣,沒有一個統一的準則。而且也沒有遵循軟體開發的原則:高內聚、低耦合;因為我是給行業提供代碼的,行業給交付提供代碼。我們要儘量去減少大家的接入成本,降低接入成本的最好方案就是我們在設計組件的時候編寫 ...
  • HBuilderx快速新建VUE項目 一、安裝HBuilderx開發工具 官網:HBuilderX HBuilderXH是HTML的第一個字母,Builder是builder,X是HBuilder的下一個版本。我們也被稱為HX。 HBuilderX是輕量級但功能強大的 IDE。 它的官網上介紹到HB ...
  • 這裡給大家分享我在網上總結出來的一些知識,希望對大家有所幫助 一、面向對象 一般使用字面量的形式直接創建對象,但是這種創建方式對於創建大量相似對象的時候,會產生大量的重覆代碼。但 js和一般的面向對象的語言不同,在 ES6 之前它沒有類的概念。但是可以使用函數來進行模擬,從而產生出可復用的對象創建方 ...
  • DNS 解析:將功能變數名稱解析成 IP 地址 TCP 連接:TCP 三次握手 發送 HTTP 請求 伺服器處理請求並返回 HTTP 報文 瀏覽器解析渲染頁面 斷開連接:TCP 四次揮手 一、什麼是URL? URL(Uniform Resource Locator),統一資源定位符,用於定位互聯網上資源,俗 ...
  • 一、什麼是跨域 當a.qq.com功能變數名稱下的頁⾯或腳本試圖去請求b.qq.com功能變數名稱下的資源時,就是典型的跨域行為。跨域的定義從受限範圍可以分為兩種,⼴義跨域和狹義跨域。 (一)廣義跨域 ⼴義跨域通常包含以下三種⾏為:1. 資源跳轉:a鏈接、重定向。2. 資源嵌⼊:<link>、<script>、<i ...
  • 購物車可以說是電商平臺的一個標配了,起初是用於多種商品的結算,現在很多用戶也把購物車當作臨時收藏來使用,這裡嘗試做一個基本的購物車架構設計。 用例分析 加入購物車、查看購物車、修改數量或者規格、移除商品、清空購物車,是一個購物車最基本的功能。 關鍵流程 1.查看購物車 關鍵點: 1)商品狀態判斷:上 ...
一周排行
    -Advertisement-
    Play Games
  • 分組和樹形結構是不一樣的。 樹形結構是以遞歸形式存在。分組是以鍵值對存在的形式,類似於GroupBy這樣的形式。 舉個例子 ID NAME SEX Class 1 張三 男 1 2 李四 女 2 3 王二 男 1 當以Sex為分組依據時則是 Key Value 男 1 張三 男 1 3 王二 男 1 ...
  • NetCore中將SQLServer資料庫備份為Sql腳本 描述: 最近寫項目收到了一個需求, 就是將SQL Server資料庫備份為Sql腳本, 如果是My Sql之類的還好說, 但是在網上搜了一大堆, 全是教你怎麼操作SSMS的, 就很d疼! 解決方案: 通過各種查找資料, 還有一些老哥的幫助, ...
  • 我的Notion Clowd.Squirrel Squirrel.Windows 是一組工具和適用於.Net的庫,用於管理 Desktop Windows 應用程式的安裝和更新。 Squirrel.Windows 對 Windows 應用程式的實現語言沒有任何要求,甚至無需服務端即可完成增量更新。 ...
  • 轉載請註明來源 https://www.cnblogs.com/brucejiao/p/16188865.html 謝謝! 轉載請註明來源 https://www.cnblogs.com/brucejiao/p/16188865.html 謝謝! 轉載請註明來源 https://www.cnblog ...
  • 1. Netty源碼研究筆記(3)——Channel系列 依舊是通過先縱向再橫向的研究方法,在開篇中,我們發現不管是Sever還是Client,最終的啟動是通過調用channel的對應方法來完成的,而這個動作實際在channel綁定的eventLoop中執行。 接下來,我們繼續EchoSever、E ...
  • 大家好,今天給大家介紹一款輕量、快速、穩定可編排的組件式規則引擎框架LiteFlow。 一、LiteFlow的介紹 LiteFlow官方網站和代碼倉庫地址 官方網站:https://yomahub.com/liteflow Gitee托管倉庫:https://gitee.com/dromara/li ...
  • 我使用Spring AOP實現了用戶操作日誌功能 今天答辯完了,復盤了一下系統,發現還是有一些東西值得拿出來和大家分享一下。 需求分析 系統需要對用戶的操作進行記錄,方便未來溯源 首先想到的就是在每個方法中,去實現記錄的邏輯,但是這樣做肯定是不現實的,首先工作量大,其次違背了軟體工程設計原則(開閉原 ...
  • 《零基礎學Java》 繪製幾何圖形 Java可以分別使用 Graphics 和 Graphics2D 繪製圖形,Graphics類 使用不同的方法繪製不同的圖形(drawLine()方法可f以繪製線、drawRect()方法用於繪製矩形、drawOval()方法用於繪製橢圓形)。 Graphics類 ...
  • 本期教程人臉識別第三方平臺為虹軟科技,本文章講解的是人臉識別RGB活體追蹤技術,免費的功能很多可以自行搭配,希望在你看完本章課程有所收穫。 ...
  • 很多人都喜歡使用黑色的主題樣式,包括我自己,使用了差不多三年的黑色主題,但是個人覺得在進行視窗轉換的時候很廢眼睛。 比如IDEA是全黑的,然後需要看PDF或者WORD又變成白色的了,這樣來回切換導致眼睛很累,畢竟現在網頁以及大部分軟體的界面都是白色的。那麼還是老老實實的使用原來比較順眼的模式吧。 1 ...