最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Spring?Cloud灰度部署實(shí)現(xiàn)過(guò)程詳解

 更新時(shí)間:2023年06月21日 09:17:32   作者:huan1993  
這篇文章主要為大家介紹了Spring?Cloud灰度部署實(shí)現(xiàn)過(guò)程詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪

1、背景(灰度部署)

在我們系統(tǒng)發(fā)布生產(chǎn)環(huán)境時(shí),有時(shí)為了確保新的服務(wù)邏輯沒(méi)有問(wèn)題,會(huì)讓一小部分特定的用戶(hù)來(lái)使用新的版本(比如客戶(hù)端的內(nèi)測(cè)版本),而其余的用戶(hù)使用舊的版本,那么這個(gè)在Spring Cloud中該如何來(lái)實(shí)現(xiàn)呢?

負(fù)載均衡組件使用:Spring Cloud LoadBalancer

2、需求

3、實(shí)現(xiàn)思路

通過(guò)翻閱Spring Cloud的官方文檔,我們知道,大概可以通過(guò)2種方式來(lái)達(dá)到我們的目的。

  • 實(shí)現(xiàn) ReactiveLoadBalancer接口,重寫(xiě)負(fù)載均衡算法。
  • 實(shí)現(xiàn)ServiceInstanceListSupplier接口,重寫(xiě)get方法,返回自定義的服務(wù)列表。

ServiceInstanceListSupplier: 可以實(shí)現(xiàn)如下功能,比如我們的 user-service在注冊(cè)中心上存在5個(gè),此處我可以只返回3個(gè)。

4、Spring Cloud中是否有我上方類(lèi)似需求的例子

查閱Spring Cloud官方文檔,發(fā)現(xiàn)org.springframework.cloud.loadbalancer.core.HintBasedServiceInstanceListSupplier 類(lèi)可以實(shí)現(xiàn)類(lèi)似的功能。

那可能有人會(huì)說(shuō),既然Spring Cloud已經(jīng)提供了這個(gè)功能,為什么你還要重寫(xiě)一個(gè)? 此處只是為了一個(gè)記錄,因?yàn)楣ぷ髦械男枨罂赡芨鞣N各樣,萬(wàn)一后期有類(lèi)似的需求,此處記錄了,后期知道怎么實(shí)現(xiàn)。

5、核心代碼實(shí)現(xiàn)

5.1 灰度核心代碼

5.1.1 灰度服務(wù)實(shí)例選擇器實(shí)現(xiàn)

package com.huan.loadbalancer;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.loadbalancer.Request;
import org.springframework.cloud.client.loadbalancer.RequestDataContext;
import org.springframework.cloud.loadbalancer.core.DelegatingServiceInstanceListSupplier;
import org.springframework.cloud.loadbalancer.core.ServiceInstanceListSupplier;
import org.springframework.http.HttpHeaders;
import reactor.core.publisher.Flux;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
/**
 * 自定義 根據(jù)服務(wù)名 獲取服務(wù)實(shí)例 列表
 * <p>
 * 需求: 用戶(hù)通過(guò)請(qǐng)求訪問(wèn) 網(wǎng)關(guān)<br />
 * 1、如果請(qǐng)求頭中的 version 值和 下游服務(wù)元數(shù)據(jù)的 version 值一致,則選擇該 服務(wù)。<br />
 * 2、如果請(qǐng)求頭中的 version 值和 下游服務(wù)元數(shù)據(jù)的 version 值不一致,且 不存在 version 的值 為 default 則直接報(bào)錯(cuò)。<br />
 * 3、如果請(qǐng)求頭中的 version 值和 下游服務(wù)元數(shù)據(jù)的 version 值不一致,且 存在 version 的值 為 default,則選擇該服務(wù)。<br />
 * <p>
 * 參考: {@link org.springframework.cloud.loadbalancer.core.HintBasedServiceInstanceListSupplier} 實(shí)現(xiàn)
 *
 * @author huan.fu
 * @date 2023/6/19 - 21:14
 */
@Slf4j
public class VersionServiceInstanceListSupplier extends DelegatingServiceInstanceListSupplier {
    /**
     * 請(qǐng)求頭的名字, 通過(guò)這個(gè) version 字段和 服務(wù)中的元數(shù)據(jù)來(lái)version字段進(jìn)行比較,
     * 得到最終的實(shí)例數(shù)據(jù)
     */
    private static final String VERSION_HEADER_NAME = "version";
    public VersionServiceInstanceListSupplier(ServiceInstanceListSupplier delegate) {
        super(delegate);
    }
    @Override
    public Flux<List<ServiceInstance>> get() {
        return delegate.get();
    }
    @Override
    public Flux<List<ServiceInstance>> get(Request request) {
        return delegate.get(request).map(instances -> filteredByVersion(instances, getVersion(request.getContext())));
    }
    private String getVersion(Object requestContext) {
        if (requestContext == null) {
            return null;
        }
        String version = null;
        if (requestContext instanceof RequestDataContext) {
            version = getVersionFromHeader((RequestDataContext) requestContext);
        }
        log.info("獲取到需要請(qǐng)求服務(wù)[{}]的version:[{}]", getServiceId(), version);
        return version;
    }
    /**
     * 從請(qǐng)求中獲取version
     */
    private String getVersionFromHeader(RequestDataContext context) {
        if (context.getClientRequest() != null) {
            HttpHeaders headers = context.getClientRequest().getHeaders();
            if (headers != null) {
                return headers.getFirst(VERSION_HEADER_NAME);
            }
        }
        return null;
    }
    private List<ServiceInstance> filteredByVersion(List<ServiceInstance> instances, String version) {
        // 1、獲取 請(qǐng)求頭中的 version 和 ServiceInstance 中 元數(shù)據(jù)中 version 一致的服務(wù)
        List<ServiceInstance> selectServiceInstances = instances.stream()
                .filter(instance -> instance.getMetadata().get(VERSION_HEADER_NAME) != null
                        && Objects.equals(version, instance.getMetadata().get(VERSION_HEADER_NAME)))
                .collect(Collectors.toList());
        if (!selectServiceInstances.isEmpty()) {
            log.info("返回請(qǐng)求服務(wù):[{}]為version:[{}]的有:[{}]個(gè)", getServiceId(), version, selectServiceInstances.size());
            return selectServiceInstances;
        }
        // 2、返回 version=default 的實(shí)例
        selectServiceInstances = instances.stream()
                .filter(instance -> Objects.equals(instance.getMetadata().get(VERSION_HEADER_NAME), "default"))
                .collect(Collectors.toList());
        log.info("返回請(qǐng)求服務(wù):[{}]為version:[{}]的有:[{}]個(gè)", getServiceId(), "default", selectServiceInstances.size());
        return selectServiceInstances;
    }
}

5.1.2 灰度f(wàn)eign請(qǐng)求頭傳遞攔截器

package com.huan.loadbalancer;
import feign.RequestInterceptor;
import feign.RequestTemplate;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
/**
 * 將version請(qǐng)求頭通過(guò)feign傳遞到下游
 *
 * @author huan.fu
 * @date 2023/6/20 - 08:27
 */
@Component
@Slf4j
public class VersionRequestInterceptor implements RequestInterceptor {
    @Override
    public void apply(RequestTemplate requestTemplate) {
        String version = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest()
                .getHeader("version");
        log.info("feign 中傳遞的 version 請(qǐng)求頭的值為:[{}]", version);
        requestTemplate
                .header("version", version);
    }
}

注意: 此處全局配置了,配置了一個(gè)feign的全局?jǐn)r截器,進(jìn)行請(qǐng)求頭version的傳遞。

5.1.3 灰度服務(wù)實(shí)例選擇器配置

package com.huan.loadbalancer;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.cloud.client.discovery.ReactiveDiscoveryClient;
import org.springframework.cloud.loadbalancer.annotation.LoadBalancerClients;
import org.springframework.cloud.loadbalancer.core.ServiceInstanceListSupplier;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
 * 此處選擇全局配置
 *
 * @author huan.fu
 * @date 2023/6/19 - 22:16
 */
@Configuration
@Slf4j
@LoadBalancerClients(defaultConfiguration = VersionServiceInstanceListSupplierConfiguration.class)
public class VersionServiceInstanceListSupplierConfiguration {
    @Bean
    @ConditionalOnClass(name = "org.springframework.web.servlet.DispatcherServlet")
    public VersionServiceInstanceListSupplier versionServiceInstanceListSupplierV1(
            ConfigurableApplicationContext context) {
        log.error("===========> versionServiceInstanceListSupplierV1");
        ServiceInstanceListSupplier delegate = ServiceInstanceListSupplier.builder()
                .withBlockingDiscoveryClient()
                .withCaching()
                .build(context);
        return new VersionServiceInstanceListSupplier(delegate);
    }
    @Bean
    @ConditionalOnClass(name = "org.springframework.web.reactive.DispatcherHandler")
    public VersionServiceInstanceListSupplier versionServiceInstanceListSupplierV2(
            ConfigurableApplicationContext context) {
        log.error("===========> versionServiceInstanceListSupplierV2");
        ServiceInstanceListSupplier delegate = ServiceInstanceListSupplier.builder()
                .withDiscoveryClient()
                .withCaching()
                .build(context);
        return new VersionServiceInstanceListSupplier(delegate);
    }
}

此處偷懶全局配置了

`@Configuration
@Slf4j
@LoadBalancerClients(defaultConfiguration = VersionServiceInstanceListSupplierConfiguration.class)
`

5.2 網(wǎng)關(guān)核心代碼

5.2.1 網(wǎng)關(guān)配置文件

spring:
  application:
    name: lobalancer-gateway-8001
  cloud:
    nacos:
      discovery:
        # 配置 nacos 的服務(wù)地址
        server-addr: localhost:8848
        group: DEFAULT_GROUP
      config:
        server-addr: localhost:8848
    gateway:
      discovery:
        locator:
          enabled: true
server:
  port: 8001
logging:
  level:
    root: info

5.3 服務(wù)提供者核心代碼

5.3.1 向外提供一個(gè)方法

package com.huan.loadbalancer.controller;
import com.alibaba.cloud.nacos.NacosDiscoveryProperties;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
/**
 * 提供者控制器
 *
 * @author huan.fu
 * @date 2023/3/6 - 21:58
 */
@RestController
public class ProviderController {
    @Resource
    private NacosDiscoveryProperties nacosDiscoveryProperties;
    /**
     * 獲取服務(wù)信息
     *
     * @return ip:port
     */
    @GetMapping("serverInfo")
    public String serverInfo() {
        return nacosDiscoveryProperties.getIp() + ":" + nacosDiscoveryProperties.getPort();
    }
}

5.3.2 提供者端口8005配置信息

spring:
  application:
    name: provider
  cloud:
    nacos:
      discovery:
        # 配置 nacos 的服務(wù)地址
        server-addr: localhost:8848
        # 配置元數(shù)據(jù)
        metadata:
          version: v1
      config:
        server-addr: localhost:8848
server:
  port: 8005

注意 metadata中version的值

5.3.2 提供者端口8006配置信息

spring:
  application:
    name: provider
  cloud:
    nacos:
      discovery:
        # 配置 nacos 的服務(wù)地址
        server-addr: localhost:8848
        # 配置元數(shù)據(jù)
        metadata:
          version: v1
      config:
        server-addr: localhost:8848
server:
  port: 8006

注意 metadata中version的值

5.3.3 提供者端口8007配置信息

spring:
  application:
    name: provider
  cloud:
    nacos:
      discovery:
        # 配置 nacos 的服務(wù)地址
        server-addr: localhost:8848
        # 配置元數(shù)據(jù)
        metadata:
          version: default
      config:
        server-addr: localhost:8848
server:
  port: 8007

注意 metadata中version的值

5.4 服務(wù)消費(fèi)者代碼

5.4.1 通過(guò) feign 調(diào)用提供者方法

/**
 * @author huan.fu
 * @date 2023/6/19 - 22:21
 */
@FeignClient(value = "provider")
public interface FeignProvider {

    /**
     * 獲取服務(wù)信息
     *
     * @return ip:port
     */
    @GetMapping("serverInfo")
    String fetchServerInfo();

}

5.4.2 向外提供一個(gè)方法

package com.huan.loadbalancer.controller;
import com.alibaba.cloud.nacos.NacosDiscoveryProperties;
import com.huan.loadbalancer.feign.FeignProvider;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
import java.util.HashMap;
import java.util.Map;
/**
 * 消費(fèi)者控制器
 *
 * @author huan.fu
 * @date 2023/6/19 - 22:21
 */
@RestController
public class ConsumerController {
    @Resource
    private FeignProvider feignProvider;
    @Resource
    private NacosDiscoveryProperties nacosDiscoveryProperties;
    @GetMapping("fetchProviderServerInfo")
    public Map<String, String> fetchProviderServerInfo() {
        Map<String, String> ret = new HashMap<>(4);
        ret.put("consumer信息", nacosDiscoveryProperties.getIp() + ":" + nacosDiscoveryProperties.getPort());
        ret.put("provider信息", feignProvider.fetchServerInfo());
        return ret;
    }
}

消費(fèi)者端口 8002 配置信息

spring:
  application:
    name: consumer
  cloud:
    nacos:
      discovery:
        # 配置 nacos 的服務(wù)地址
        server-addr: localhost:8848
        register-enabled: true
        service: nacos-feign-consumer
        group: DEFAULT_GROUP
        metadata:
          version: v1
      config:
        server-addr: localhost:8848
server:
  port: 8002

注意 metadata中version的值

消費(fèi)者端口 8003 配置信息

spring:
  application:
    name: consumer
  cloud:
    nacos:
      discovery:
        # 配置 nacos 的服務(wù)地址
        server-addr: localhost:8848
        register-enabled: true
        service: nacos-feign-consumer
        group: DEFAULT_GROUP
        metadata:
          version: v2
      config:
        server-addr: localhost:8848
server:
  port: 8003

注意 metadata中version的值

消費(fèi)者端口 8004 配置信息

spring:
  application:
    name: consumer
  cloud:
    nacos:
      discovery:
        # 配置 nacos 的服務(wù)地址
        server-addr: localhost:8848
        register-enabled: true
        service: nacos-feign-consumer
        group: DEFAULT_GROUP
        metadata:
          version: default
      config:
        server-addr: localhost:8848
server:
  port: 8003

注意 metadata中version的值

6、測(cè)試

6.1 請(qǐng)求頭中攜帶 version=v1

從上圖中可以看到,當(dāng)version=v1時(shí),服務(wù)消費(fèi)者為consumer-8002, 提供者為provider-8005和provider-8006

?  ~ curl --location --request GET 'http://localhost:8001/nacos-feign-consumer/fetchProviderServerInfo' \
--header 'version: v1'
{"consumer信息":"192.168.8.168:8002","provider信息":"192.168.8.168:8005"}%
?  ~ curl --location --request GET 'http://localhost:8001/nacos-feign-consumer/fetchProviderServerInfo' \
--header 'version: v1'
{"consumer信息":"192.168.8.168:8002","provider信息":"192.168.8.168:8006"}%
?  ~ curl --location --request GET 'http://localhost:8001/nacos-feign-consumer/fetchProviderServerInfo' \
--header 'version: v1'
{"consumer信息":"192.168.8.168:8002","provider信息":"192.168.8.168:8005"}%
?  ~ curl --location --request GET 'http://localhost:8001/nacos-feign-consumer/fetchProviderServerInfo' \
--header 'version: v1'
{"consumer信息":"192.168.8.168:8002","provider信息":"192.168.8.168:8006"}%
?  ~

可以看到,消費(fèi)者返回的端口是8002,提供者返回的端口是8005|8006是符合預(yù)期的。

6.2 不傳遞version

從上圖中可以看到,當(dāng)不攜帶時(shí),服務(wù)消費(fèi)者為consumer-8004, 提供者為provider-8007和

?  ~ curl --location --request GET 'http://localhost:8001/nacos-feign-consumer/fetchProviderServerInfo'
{"consumer信息":"192.168.8.168:8004","provider信息":"192.168.8.168:8007"}%
?  ~ curl --location --request GET 'http://localhost:8001/nacos-feign-consumer/fetchProviderServerInfo'
{"consumer信息":"192.168.8.168:8004","provider信息":"192.168.8.168:8007"}%
?  ~ curl --location --request GET 'http://localhost:8001/nacos-feign-consumer/fetchProviderServerInfo'
{"consumer信息":"192.168.8.168:8004","provider信息":"192.168.8.168:8007"}%
?  ~

可以看到,消費(fèi)者返回的端口是8004,提供者返回的端口是8007是符合預(yù)期的。

完整代碼

https://gitee.com/huan1993/spring-cloud-alibaba-parent/tree/master/loadbalancer-supply-service-instance

參考文檔

1、https://docs.spring.io/spring-cloud-commons/docs/current/reference/html/#spring-cloud-loadbalancer

以上就是Spring Cloud灰度部署實(shí)現(xiàn)過(guò)程詳解的詳細(xì)內(nèi)容,更多關(guān)于Spring Cloud灰度部署的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • Java并發(fā)之Phaser的全面解析詳解

    Java并發(fā)之Phaser的全面解析詳解

    Phaser是Java中一個(gè)靈活的同步工具,其優(yōu)點(diǎn)在于支持多階段的任務(wù)拆分與同步,并且能夠動(dòng)態(tài)地注冊(cè)與注銷(xiāo)參與者,下面我們就來(lái)深入了解一下Phaser的應(yīng)用吧
    2024-02-02
  • 帶你快速搞定java IO

    帶你快速搞定java IO

    這篇文章主要介紹了Java IO流 文件傳輸基礎(chǔ)的相關(guān)資料,非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友可以參考下,希望能給你帶來(lái)幫助
    2021-07-07
  • 劍指Offer之Java算法習(xí)題精講鏈表與二叉樹(shù)專(zhuān)項(xiàng)訓(xùn)練

    劍指Offer之Java算法習(xí)題精講鏈表與二叉樹(shù)專(zhuān)項(xiàng)訓(xùn)練

    跟著思路走,之后從簡(jiǎn)單題入手,反復(fù)去看,做過(guò)之后可能會(huì)忘記,之后再做一次,記不住就反復(fù)做,反復(fù)尋求思路和規(guī)律,慢慢積累就會(huì)發(fā)現(xiàn)質(zhì)的變化
    2022-03-03
  • 淺析SpringBoot統(tǒng)一返回結(jié)果的實(shí)現(xiàn)

    淺析SpringBoot統(tǒng)一返回結(jié)果的實(shí)現(xiàn)

    前后端開(kāi)發(fā)過(guò)程中數(shù)據(jù)交互規(guī)范化是一件非常重要的事情,不僅可以減少前后端交互過(guò)程中出現(xiàn)的問(wèn)題,也讓代碼邏輯更加具有條理,下面小編就和大家講講SpringBoot如何統(tǒng)一返回結(jié)果的吧
    2023-07-07
  • Java?超基礎(chǔ)講解String的使用

    Java?超基礎(chǔ)講解String的使用

    字符串廣泛應(yīng)用?在?Java?編程中,在?Java?中字符串屬于對(duì)象,Java?提供了?String?類(lèi)來(lái)創(chuàng)建和操作字符串,讓我們一起來(lái)了解它
    2022-04-04
  • Java?empty、null、blank的區(qū)別小結(jié)

    Java?empty、null、blank的區(qū)別小結(jié)

    本文主要介紹了Java?empty、null、blank的區(qū)別小結(jié),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2023-06-06
  • Springboot項(xiàng)目中定時(shí)任務(wù)的四種實(shí)現(xiàn)方式詳解

    Springboot項(xiàng)目中定時(shí)任務(wù)的四種實(shí)現(xiàn)方式詳解

    Spring的@Scheduled注解是一種非常簡(jiǎn)單和便捷的實(shí)現(xiàn)定時(shí)任務(wù)的方式,通過(guò)在方法上添加@Scheduled注解,我們可以指定方法在特定的時(shí)間間隔或固定的時(shí)間點(diǎn)執(zhí)行,本文給大家介紹Springboot項(xiàng)目中定時(shí)任務(wù)的四種實(shí)現(xiàn)方式,感興趣的的朋友一起看看b
    2024-02-02
  • spring聲明式事務(wù)管理解析

    spring聲明式事務(wù)管理解析

    這篇文章主要為大家詳細(xì)介紹了spring聲明式事務(wù)管理,對(duì)spring事務(wù)管理進(jìn)行深入了解,感興趣的小伙伴們可以參考一下
    2016-10-10
  • 使用SpringBoot注解方式處理事務(wù)回滾實(shí)現(xiàn)

    使用SpringBoot注解方式處理事務(wù)回滾實(shí)現(xiàn)

    這篇文章主要介紹了使用SpringBoot注解方式處理事務(wù)回滾實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2020-08-08
  • 詳解Spring Boot Security工作流程

    詳解Spring Boot Security工作流程

    Spring Security,這是一種基于 Spring AOP 和 Servlet 。這篇文章主要介紹了Spring Boot Security的相關(guān)知識(shí),需要的朋友可以參考下
    2019-04-04

最新評(píng)論

金塔县| 金华市| 娄底市| 太谷县| 准格尔旗| 宜昌市| 上思县| 吴旗县| 吉木萨尔县| 黄梅县| 南康市| 桂东县| 兴国县| 九台市| 和平区| 顺平县| 富源县| 连江县| 新兴县| 汝阳县| 仙桃市| 溧阳市| 合阳县| 大名县| 五指山市| 葫芦岛市| 呼伦贝尔市| 湛江市| 延寿县| 山阳县| 咸宁市| 双流县| 都匀市| 白银市| 石门县| 宜春市| 高州市| 从化市| 潢川县| 保定市| 沂水县|