springcloud按版本发布微服务达到不停机更新的效果

本文基于以下环境完成

  • spring-boot 2.3.2.RELEASE
  • spring-cloud Hoxton.SR9
  • spring-cloud-alibaba 2.2.6.RELEASE
  • spring-cloud-starter-gateway 2.2.6.RELEASE
  • spring-cloud-starter-loadbalancer 2.2.6.RELEASE
  • nacos 2.0.3

一、思路

实现思路:
前端项目在请求后端接口时,携带一个版本号version,网关gateway在接收到这个version后,根据version的值去选择对应的微服务实例,服务之间的调用openfeign也通过版本号去选择对应的服务实例

举例:
每次更新项目时版本号递增,比如目前在使用的后端项目版本为1.0.0,那么前端携带的版本也是1.0.0,
当我们要更新项目时,后端新的版本为2.0.0 前端项目的版本也为2.0.0,如果用户没有刷新页面,那么还是携带旧的版本号1.0.0, 那么请求都会转发到后端1.0.0, 如果用户刷新页面,那么就会携带新版本号2.0.0,此时请求被转发后端2.0.0版本

实现方式:
排除自带的ribbon依赖

<exclusions><exclusion><groupId>org.springframework.cloud</groupId></exclusion>
</exclusions>

引入spring-cloud-loadbalancer依赖

<dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-loadbalancer</artifactId><version>2.2.6.RELEASE</version>
</dependency>

spring-cloud-loadbalancer文档地址:点我跳转

配置微服务版本号

spring:cloud:nacos:discovery:metadata:version: 2.0.0

后续可以在nacos中修改
在这里插入图片描述

二 、重写网关的负载策略

2.1 重写轮训负载均衡

spring-cloud-loadbalancer默认的负载策略是轮训,实现类为org.springframework.cloud.loadbalancer.core.RoundRobinLoadBalancer,我们主要看里面的choose和getInstanceResponse方法,在getInstanceResponse方法中传入ServiceInstance集合,然后从这个集合中选择一个合适的instance实例,那么我们只要在重写这个方法在里面添加版本的筛选
在这里插入图片描述那么我们再看一下这个类是如何实例化的,主要是org.springframework.cloud.loadbalancer.annotation.LoadBalancerClientConfiguration

在这里插入图片描述
注意@ConditionalOnMissingBean这个注解,只有当spring容器中没有ReactorLoadBalancer类型的bean时才会实例化。

我们新建一个VersionLoadBalancer类,跟RoundRobinLoadBalancer一样去实现ReactorServiceInstanceLoadBalancer接口(其实就是把他的代码copy过来,然后添加我们自定义的筛选条件)

import java.util.List;
import java.util.Random;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.cloud.loadbalancer.core.*;
import org.springframework.http.HttpHeaders;
import org.springframework.util.StringUtils;
import reactor.core.publisher.Mono;import org.springframework.beans.factory.ObjectProvider;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.loadbalancer.reactive.DefaultResponse;
import org.springframework.cloud.client.loadbalancer.reactive.EmptyResponse;
import org.springframework.cloud.client.loadbalancer.reactive.Request;
import org.springframework.cloud.client.loadbalancer.reactive.Response;/*** {@link org.springframework.cloud.loadbalancer.core.RoundRobinLoadBalancer}** @Author * @Date 2024/4/18 11:46* @Description**/
public class VersionLoadBalancer implements ReactorServiceInstanceLoadBalancer {private static final Log log = LogFactory.getLog(VersionLoadBalancer.class);private final AtomicInteger position;@Deprecatedprivate ObjectProvider<ServiceInstanceSupplier> serviceInstanceSupplier;private ObjectProvider<ServiceInstanceListSupplier> serviceInstanceListSupplierProvider;private final String serviceId;/*** @param serviceId               id of the service for which to choose an instance* @param serviceInstanceSupplier a provider of {@link ServiceInstanceSupplier} that*                                will be used to get available instances* @deprecated Use {@link #VersionLoadBalancer(ObjectProvider, String)}} instead.*/@Deprecatedpublic VersionLoadBalancer(String serviceId,ObjectProvider<ServiceInstanceSupplier> serviceInstanceSupplier) {this(serviceId, serviceInstanceSupplier, new Random().nextInt(1000));}/*** @param serviceInstanceListSupplierProvider a provider of*                                            {@link ServiceInstanceListSupplier} that will be used to get available instances* @param serviceId                           id of the service for which to choose an instance*/public VersionLoadBalancer(ObjectProvider<ServiceInstanceListSupplier> serviceInstanceListSupplierProvider,String serviceId) {this(serviceInstanceListSupplierProvider, serviceId, new Random().nextInt(1000));}/*** @param serviceInstanceListSupplierProvider a provider of*                                            {@link ServiceInstanceListSupplier} that will be used to get available instances* @param serviceId                           id of the service for which to choose an instance* @param seedPosition                        Round Robin element position marker*/public VersionLoadBalancer(ObjectProvider<ServiceInstanceListSupplier> serviceInstanceListSupplierProvider,String serviceId, int seedPosition) {this.serviceId = serviceId;this.serviceInstanceListSupplierProvider = serviceInstanceListSupplierProvider;this.position = new AtomicInteger(seedPosition);}/*** @param serviceId               id of the service for which to choose an instance* @param serviceInstanceSupplier a provider of {@link ServiceInstanceSupplier} that*                                will be used to get available instances* @param seedPosition            Round Robin element position marker* @deprecated Use {@link #VersionLoadBalancer(ObjectProvider, String, int)}}* instead.*/@Deprecatedpublic VersionLoadBalancer(String serviceId,ObjectProvider<ServiceInstanceSupplier> serviceInstanceSupplier,int seedPosition) {this.serviceId = serviceId;this.serviceInstanceSupplier = serviceInstanceSupplier;this.position = new AtomicInteger(seedPosition);}@SuppressWarnings("all")@Override// see original// https://github.com/Netflix/ocelli/blob/master/ocelli-core/// src/main/java/netflix/ocelli/loadbalancer/RoundRobinLoadBalancer.javapublic Mono<Response<ServiceInstance>> choose(Request request) {HttpHeaders headers = (HttpHeaders) request.getContext();String requestVersion = headers.getFirst("version");// TODO: move supplier to Request?// Temporary conditional logic till deprecated members are removed.if (serviceInstanceListSupplierProvider != null) {ServiceInstanceListSupplier supplier = serviceInstanceListSupplierProvider.getIfAvailable(NoopServiceInstanceListSupplier::new);return supplier.get().next().map(m -> this.getInstanceResponse(m, requestVersion));}ServiceInstanceSupplier supplier = this.serviceInstanceSupplier.getIfAvailable(NoopServiceInstanceSupplier::new);return supplier.get().collectList().map(m -> this.getInstanceResponse(m, requestVersion));}@SuppressWarnings("deprecation")private Response<ServiceInstance> getInstanceResponse(List<ServiceInstance> instances, String requestVersion) {List<ServiceInstance> serviceInstances = this.filterInstance(instances, requestVersion);if (serviceInstances.isEmpty()) {log.warn("No servers available for service: " + this.serviceId + " ,request version: " + requestVersion);return new EmptyResponse();}// TODO: enforce order?int pos = Math.abs(this.position.incrementAndGet());ServiceInstance instance = serviceInstances.get(pos % serviceInstances.size());return new DefaultResponse(instance);}/*** 获取对应的版本** @param instances* @param requestVersion* @return*/private List<ServiceInstance> filterInstance(List<ServiceInstance> instances, String requestVersion) {if (StringUtils.isEmpty(requestVersion)) {return instances;}return instances.stream().filter(f -> requestVersion.equals(f.getMetadata().get("version"))).collect(Collectors.toList());}}

然后新建一个配置类VersionLoadBalancerConfig,注意不能使用@Configuration

import com.demo.gateway.filter.VersionLoadBalancer;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.loadbalancer.core.ReactorLoadBalancer;
import org.springframework.cloud.loadbalancer.core.ServiceInstanceListSupplier;
import org.springframework.cloud.loadbalancer.support.LoadBalancerClientFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.core.env.Environment;/*** 注意: 这里不能使用@Configuration !!!* 参考 {@link org.springframework.cloud.loadbalancer.annotation.LoadBalancerClientConfiguration}** @Author * @Date 2024/4/18 11:50* @Description**/public class VersionLoadBalancerConfig {@Beanpublic ReactorLoadBalancer<ServiceInstance> reactorServiceInstanceLoadBalancer(Environment environment,LoadBalancerClientFactory loadBalancerClientFactory) {String name = environment.getProperty(LoadBalancerClientFactory.PROPERTY_NAME);return new VersionLoadBalancer(loadBalancerClientFactory.getLazyProvider(name,ServiceInstanceListSupplier.class), name);}}

官方文档示例
在这里插入图片描述
然后在项目的启动类添加@LoadBalancerClients(defaultConfiguration = {VersionLoadBalancerConfig.class})

import com.demo.gateway.config.VersionLoadBalancerConfig;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.loadbalancer.annotation.LoadBalancerClients;/*** @Author * @Date 2022/10/10 16:38* @Description**/
@EnableDiscoveryClient
@SpringBootApplication
@LoadBalancerClients(defaultConfiguration = {VersionLoadBalancerConfig.class})
public class GatewayApplication {public static void main(String[] args) {SpringApplication.run(GatewayApplication.class, args);}}

2.2 重写过滤器

此时运行项目发现碰到空指针异常,断点后发现是VersionLoadBalancer.choose方法里面request.getContext()是个null值,那么我们再看一下是在哪里调用了这个方法,主要是org.springframework.cloud.gateway.filter.ReactiveLoadBalancerClientFilter
在这里插入图片描述在这里插入图片描述那么我们需要重写这个过滤器,在choose方法里面将我们http请求的header对象传递给VersionLoadBalancer,我们再看一下ReactiveLoadBalancerClientFilter是怎么初始化的,主要是org.springframework.cloud.gateway.config.GatewayReactiveLoadBalancerClientAutoConfiguration
在这里插入图片描述
那么我们就可以新建一个VersionLoadBalancerFilter去覆盖原来的ReactiveLoadBalancerClientFilter(如果直接新增一个过滤器那么原来的过滤器也会执行,还会有过滤器的执行顺序问题)

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.loadbalancer.LoadBalancerUriTools;
import org.springframework.cloud.client.loadbalancer.reactive.DefaultRequest;
import org.springframework.cloud.client.loadbalancer.reactive.Request;
import org.springframework.cloud.client.loadbalancer.reactive.Response;
import org.springframework.cloud.gateway.config.LoadBalancerProperties;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
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.core.ReactorLoadBalancer;
import org.springframework.cloud.loadbalancer.support.LoadBalancerClientFactory;
import org.springframework.core.Ordered;
import org.springframework.http.HttpHeaders;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;import java.net.URI;import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR;/*** 参考 {@link ReactiveLoadBalancerClientFilter}* @Author SYLIANG* @Date 2024/4/19 10:48* @Description**/
public class VersionLoadBalancerFilter extends ReactiveLoadBalancerClientFilter implements GlobalFilter, Ordered {private static final Log log = LogFactory.getLog(VersionLoadBalancerFilter.class);private static final int LOAD_BALANCER_CLIENT_FILTER_ORDER = 10250;private final LoadBalancerClientFactory clientFactory;private LoadBalancerProperties properties;public VersionLoadBalancerFilter(LoadBalancerClientFactory clientFactory, LoadBalancerProperties properties) {super(clientFactory, properties);this.clientFactory = clientFactory;this.properties = properties;}@Overridepublic int getOrder() {return LOAD_BALANCER_CLIENT_FILTER_ORDER;}@Overridepublic Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {URI url = (URI)exchange.getAttribute(ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR);String schemePrefix = (String)exchange.getAttribute(ServerWebExchangeUtils.GATEWAY_SCHEME_PREFIX_ATTR);if (url != null && ("lb".equals(url.getScheme()) || "lb".equals(schemePrefix))) {ServerWebExchangeUtils.addOriginalRequestUrl(exchange, url);if (log.isTraceEnabled()) {log.trace(ReactiveLoadBalancerClientFilter.class.getSimpleName() + " url before: " + url);}return this.choose(exchange).doOnNext((response) -> {if (!response.hasServer()) {throw NotFoundException.create(this.properties.isUse404(), "Unable to find instance for " + url.getHost());} else {ServiceInstance retrievedInstance = (ServiceInstance)response.getServer();URI uri = exchange.getRequest().getURI();String overrideScheme = retrievedInstance.isSecure() ? "https" : "http";if (schemePrefix != null) {overrideScheme = url.getScheme();}DelegatingServiceInstance serviceInstance = new DelegatingServiceInstance(retrievedInstance, overrideScheme);URI requestUrl = this.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));} else {return chain.filter(exchange);}}@Overrideprotected URI reconstructURI(ServiceInstance serviceInstance, URI original) {return LoadBalancerUriTools.reconstructURI(serviceInstance, original);}@SuppressWarnings("deprecation")private Mono<Response<ServiceInstance>> choose(ServerWebExchange exchange) {URI uri = exchange.getAttribute(GATEWAY_REQUEST_URL_ATTR);ReactorLoadBalancer<ServiceInstance> loadBalancer = this.clientFactory.getInstance(uri.getHost(), VersionLoadBalancer.class);if (loadBalancer == null) {throw new NotFoundException("No loadbalancer available for " + uri.getHost());} else {return loadBalancer.choose(this.createRequest(exchange));}}@SuppressWarnings("deprecation")private Request createRequest(ServerWebExchange exchange) {HttpHeaders headers = exchange.getRequest().getHeaders();Request<HttpHeaders> request = new DefaultRequest<>(headers);return request;}
}

注意choose方法里面的ReactorLoadBalancer loadBalancer = this.clientFactory.getInstance(uri.getHost(), VersionLoadBalancer.class); 这里不能直接new,否则轮训会失效

新建配置类VersionFilterConfiguration

import com.personnel.gateway.filter.VersionLoadBalancerFilter;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.cloud.client.loadbalancer.LoadBalancerClient;
import org.springframework.cloud.client.loadbalancer.reactive.ReactiveLoadBalancer;
import org.springframework.cloud.gateway.config.GatewayLoadBalancerClientAutoConfiguration;
import org.springframework.cloud.gateway.config.LoadBalancerProperties;
import org.springframework.cloud.gateway.filter.ReactiveLoadBalancerClientFilter;
import org.springframework.cloud.loadbalancer.config.LoadBalancerAutoConfiguration;
import org.springframework.cloud.loadbalancer.support.LoadBalancerClientFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.reactive.DispatcherHandler;/*** ReactiveLoadBalancerClientFilter过滤器的Request没有请求头信息* 参考 {@link org.springframework.cloud.gateway.config.GatewayReactiveLoadBalancerClientAutoConfiguration}** @Author * @Date 2024/4/22 15:56* @Description**/
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass({LoadBalancerClient.class, ReactiveLoadBalancer.class,LoadBalancerAutoConfiguration.class, DispatcherHandler.class})
@AutoConfigureBefore(GatewayLoadBalancerClientAutoConfiguration.class)
@AutoConfigureAfter(LoadBalancerAutoConfiguration.class)
@EnableConfigurationProperties(LoadBalancerProperties.class)
public class VersionFilterConfiguration {@Beanpublic ReactiveLoadBalancerClientFilter versionLoadBalancer(LoadBalancerClientFactory clientFactory,LoadBalancerProperties properties) {return new VersionLoadBalancerFilter(clientFactory, properties);}}

三、重写openfeign的负载策略

3.1 spring-cloud-loadbalancer负载

我们同样使用spring-cloud-loadbalancer作为负载均衡器,新建自定义负载策略VersionLoadBalancer类,注意这里面的版本号version通过spring-web的ServletRequestAttributes获取

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.loadbalancer.reactive.DefaultResponse;
import org.springframework.cloud.client.loadbalancer.reactive.EmptyResponse;
import org.springframework.cloud.client.loadbalancer.reactive.Request;
import org.springframework.cloud.client.loadbalancer.reactive.Response;
import org.springframework.cloud.loadbalancer.core.*;
import org.springframework.http.HttpHeaders;
import org.springframework.util.StringUtils;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import reactor.core.publisher.Mono;import java.util.List;
import java.util.Random;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;/**** 重写负载均衡策略: 通过版本选择服务实例* {@link RoundRobinLoadBalancer}** @Author * @Date 2024/4/18 11:46* @Description**/
public class VersionLoadBalancer implements ReactorServiceInstanceLoadBalancer {private static final Log log = LogFactory.getLog(VersionLoadBalancer.class);private final AtomicInteger position;@Deprecatedprivate ObjectProvider<ServiceInstanceSupplier> serviceInstanceSupplier;private ObjectProvider<ServiceInstanceListSupplier> serviceInstanceListSupplierProvider;private final String serviceId;private final String version = "version";/*** @param serviceId               id of the service for which to choose an instance* @param serviceInstanceSupplier a provider of {@link ServiceInstanceSupplier} that*                                will be used to get available instances* @deprecated Use {@link #VersionLoadBalancer(ObjectProvider, String)}} instead.*/@Deprecatedpublic VersionLoadBalancer(String serviceId,ObjectProvider<ServiceInstanceSupplier> serviceInstanceSupplier) {this(serviceId, serviceInstanceSupplier, new Random().nextInt(1000));}/*** @param serviceInstanceListSupplierProvider a provider of*                                            {@link ServiceInstanceListSupplier} that will be used to get available instances* @param serviceId                           id of the service for which to choose an instance*/public VersionLoadBalancer(ObjectProvider<ServiceInstanceListSupplier> serviceInstanceListSupplierProvider,String serviceId) {this(serviceInstanceListSupplierProvider, serviceId, new Random().nextInt(1000));}/*** @param serviceInstanceListSupplierProvider a provider of*                                            {@link ServiceInstanceListSupplier} that will be used to get available instances* @param serviceId                           id of the service for which to choose an instance* @param seedPosition                        Round Robin element position marker*/public VersionLoadBalancer(ObjectProvider<ServiceInstanceListSupplier> serviceInstanceListSupplierProvider,String serviceId, int seedPosition) {this.serviceId = serviceId;this.serviceInstanceListSupplierProvider = serviceInstanceListSupplierProvider;this.position = new AtomicInteger(seedPosition);}/*** @param serviceId               id of the service for which to choose an instance* @param serviceInstanceSupplier a provider of {@link ServiceInstanceSupplier} that*                                will be used to get available instances* @param seedPosition            Round Robin element position marker* @deprecated Use {@link #VersionLoadBalancer(ObjectProvider, String, int)}}* instead.*/@Deprecatedpublic VersionLoadBalancer(String serviceId,ObjectProvider<ServiceInstanceSupplier> serviceInstanceSupplier,int seedPosition) {this.serviceId = serviceId;this.serviceInstanceSupplier = serviceInstanceSupplier;this.position = new AtomicInteger(seedPosition);}@SuppressWarnings("all")@Override// see original// https://github.com/Netflix/ocelli/blob/master/ocelli-core/// src/main/java/netflix/ocelli/loadbalancer/RoundRobinLoadBalancer.javapublic Mono<Response<ServiceInstance>> choose(Request request) {ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();String requestVersion = requestAttributes.getRequest().getHeader(version);// TODO: move supplier to Request?// Temporary conditional logic till deprecated members are removed.if (serviceInstanceListSupplierProvider != null) {ServiceInstanceListSupplier supplier = serviceInstanceListSupplierProvider.getIfAvailable(NoopServiceInstanceListSupplier::new);return supplier.get().next().map(m -> this.getInstanceResponse(m, requestVersion));}ServiceInstanceSupplier supplier = this.serviceInstanceSupplier.getIfAvailable(NoopServiceInstanceSupplier::new);return supplier.get().collectList().map(m -> this.getInstanceResponse(m, requestVersion));}@SuppressWarnings("deprecation")private Response<ServiceInstance> getInstanceResponse(List<ServiceInstance> instances, String requestVersion) {List<ServiceInstance> serviceInstances = this.filterInstance(instances, requestVersion);if (serviceInstances.isEmpty()) {log.warn("No servers available for service: " + this.serviceId + " ,request version: " + requestVersion);return new EmptyResponse();}// TODO: enforce order?int pos = Math.abs(this.position.incrementAndGet());ServiceInstance instance = serviceInstances.get(pos % serviceInstances.size());return new DefaultResponse(instance);}/*** 获取对应的版本** @param instances* @param requestVersion* @return*/private List<ServiceInstance> filterInstance(List<ServiceInstance> instances, String requestVersion) {if (StringUtils.isEmpty(requestVersion)) {return instances;}return instances.stream().filter(f -> requestVersion.equals(f.getMetadata().get(version))).collect(Collectors.toList());}}

新建配置类VersionLoadBalancerConfig

import com.personnel.common.load.VersionLoadBalancer;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.loadbalancer.core.ReactorLoadBalancer;
import org.springframework.cloud.loadbalancer.core.ServiceInstanceListSupplier;
import org.springframework.cloud.loadbalancer.support.LoadBalancerClientFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.core.env.Environment;/*** 注意: 这里不能使用@Configuration !!!* 参考 {@link org.springframework.cloud.loadbalancer.annotation.LoadBalancerClientConfiguration}** @Author * @Date 2024/4/18 11:50* @Description**/public class VersionLoadBalancerConfig {@Beanpublic ReactorLoadBalancer<ServiceInstance> reactorServiceInstanceLoadBalancer(Environment environment,LoadBalancerClientFactory loadBalancerClientFactory) {String name = environment.getProperty(LoadBalancerClientFactory.PROPERTY_NAME);return new VersionLoadBalancer(loadBalancerClientFactory.getLazyProvider(name,ServiceInstanceListSupplier.class), name);}}

在启动类中添加@LoadBalancerClients(defaultConfiguration = {VersionLoadBalancerConfig.class})

3.2 ribbon负载

如果使用ribbon则使用以下配置

import com.alibaba.cloud.nacos.ribbon.NacosServer;
import com.google.common.base.Optional;
import com.netflix.loadbalancer.Server;
import com.netflix.loadbalancer.ZoneAvoidanceRule;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;import java.util.ArrayList;
import java.util.List;/*** openfeign调用根据版本选择对应的实例* @Author * @Date 2024/1/18 14:16* @Description**/
@Component
public class VersionReleaseRule extends ZoneAvoidanceRule {private final String version = "version";@Overridepublic Server choose(Object key) {ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();String requestVersion = requestAttributes.getRequest().getHeader(version);if (!StringUtils.isEmpty(requestVersion)) {List<Server> serverList = this.getLoadBalancer().getAllServers();List<Server> versionServers = new ArrayList<>();for (Server server : serverList) {NacosServer nacosServer = (NacosServer) server;if (requestVersion.equals(nacosServer.getMetadata().get(version))) {versionServers.add(server);}}if (!CollectionUtils.isEmpty(versionServers)) {Optional<Server> serverOptional = this.getPredicate().chooseRoundRobinAfterFiltering(versionServers, key);return serverOptional.isPresent() ? serverOptional.get() : null;}}return super.choose(key);}
}

四、openfeign携带token

在openfeign调用时如果没有携带token则添加以下配置

import feign.RequestInterceptor;
import feign.RequestTemplate;
import org.springframework.context.annotation.Bean;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;/***  openfeign请求添加token* @Author SYLIANG* @Date 2024/4/25 11:22* @Description**/
@Configuration
public class FeignClientInterceptorConfig {private final String authorization = "Authorization";@Beanpublic RequestInterceptor requestInterceptor() {return new RequestInterceptor() {@Overridepublic void apply(RequestTemplate requestTemplate) {// 从当前请求的 Header 中获取 tokenServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();if (attributes != null) {String token = attributes.getRequest().getHeader(authorization);if (token != null && !token.isEmpty()) {// 添加 token 到请求头部requestTemplate.header(authorization, token);}}}};}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/pingmian/4083.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

C++ 哈希

&#x1f493;博主CSDN主页:麻辣韭菜&#x1f493;   ⏩专栏分类&#xff1a;C知识分享⏪   &#x1f69a;代码仓库:C高阶&#x1f69a;   &#x1f339;关注我&#x1faf5;带你学习更多C知识   &#x1f51d;&#x1f51d; ​ 前言 1. unordered系列关联式容器 1.1 unor…

必应bing国内广告开户注册教程!

今天搜索引擎广告成为企业推广产品与服务、提升品牌知名度的重要渠道之一。作为全球第二大搜索引擎&#xff0c;必应Bing凭借其高质量的用户群体和广泛的国际覆盖&#xff0c;为广告主提供了独特的市场机遇。在中国&#xff0c;虽然必应的市场份额相对较小&#xff0c;但对于寻…

磁密固定下的三次谐波与电压谐波的关系

同相位或者相位差为180的情况下&#xff0c;磁通密度三次谐波含量占比 α \alpha α&#xff0c;则电压三次谐波含量占比为 3 α 3\alpha 3α 同相位&#xff0c;磁通密度三次谐波含量占比 α \alpha α情况下&#xff0c; B B 0 sin ⁡ ( ω t ) α B 0 sin ⁡ ( 3 ω t )…

航空企业数字化解决方案(207页PPT)

一、资料描述 航空企业数字化解决方案是一项针对航空公司在数字化转型过程中所面临挑战的全面应对策略&#xff0c;旨在通过先进的信息技术提升航空企业的运营效率、客户服务水平以及市场竞争力。这份207页的PPT详细介绍了航空企业数字化的各个方面&#xff0c;包括关键技术的…

Web3技术解析:区块链在去中心化应用中的角色

引言 在过去几年中&#xff0c;Web3技术已经成为了互联网领域的一个热门话题。作为区块链技术的延伸&#xff0c;Web3不仅仅是数字货币的代名词&#xff0c;更是一个能够为各种应用提供去中心化解决方案的强大工具。本文将深入探讨区块链在Web3去中心化应用中的关键角色&#…

ubuntu查看opencveigen

ubuntu查看opencv&eigen&cmake版本的方法 eigen eigen版本号在/usr/include/eigen3/Eigen/src/Core/util/Macros.h文件中&#xff0c;下图代表版本3.3.7 opencv版本 pkg-config --modversion opencv4也可能最后的字符串是opencv2&#xff0c;opencv

W801学习笔记十二:掌机进阶V3版本之驱动(PSRAM/SD卡)

本次升级添加了两个模块&#xff0c;现在要把他们驱动起来。 一&#xff1a;PSRAM 使用SDK自带的驱动&#xff0c;我们只需要写一个初始化函数&#xff0c;并在其中添加一些自检代码。 void psram_heap_init(){wm_psram_config(0);//实际使用的psram管脚选择0或者1&#xff…

Java学习路线及自我规划

荒废了一段时间&#xff0c;这段时间的总结开始了JavaWeb的学习但是困难重重&#xff0c;例如Maven&#xff0c;Vue的路由等&#xff0c;所以我反省了一段时间&#xff0c;因为基础薄弱&#xff0c;加之学习的资源是速成视频&#xff0c;导致大厦将倾的局面&#xff08;也算不上…

RabbitMQ工作模式(5) - 主题模式

概念 主题模式&#xff08;Topic Exchange&#xff09;是 RabbitMQ 中一种灵活且强大的消息传递模式&#xff0c;它允许生产者根据消息的特定属性将消息发送到一个交换机&#xff0c;并且消费者可以根据自己的需求来接收感兴趣的消息。主题交换机根据消息的路由键和绑定队列的路…

盲人地图使用的革新体验:助力视障人士独立、安全出行

在我们日常生活中&#xff0c;地图导航已经成为不可或缺的出行工具。而对于盲人群体来说&#xff0c;盲人地图使用这一课题的重要性不言而喻&#xff0c;它不仅关乎他们的出行便利性&#xff0c;更是他们追求生活独立与品质的重要一环。 近年来&#xff0c;一款名为蝙蝠…

echarts地图叠加百度地图底板实现数据可视化

这里写自定义目录标题 echarts地图叠加百度地图实现数据可视化echarts地图叠加百度地图实现数据可视化 实现数据可视化时,个别情况下需要在地图上实现数据的可视化,echarts加载geojson数据可以实现以地图形式展示数据,例如分层设色或者鼠标hover展示指标值,但如果要将echa…

运筹系列91:vrp算法包PyVRP

1. 介绍 PyVRP使用HGS&#xff08;hybrid genetic search&#xff09;算法求解VRP类问题。在benchmark上的评测结果如下&#xff0c;看起来还不错&#xff1a; 2. 使用例子 2.1 CVRP COORDS [(456, 320), # location 0 - the depot(228, 0), # location 1(912, 0), …

通往AGI路上,DPU将如何构建生成式AI时代的坚实算力基石?

4月19日&#xff0c;在以“重构世界 奔赴未来”为主题的2024中国生成式AI大会上&#xff0c;中科驭数作为DPU新型算力基础设施代表&#xff0c;受邀出席了中国智算中心创新论坛&#xff0c;发表了题为《以网络为中心的AI算力底座构建之路》主题演讲&#xff0c;勾勒出在通往AGI…

Xcode 15构建问题

构建时出现的异常&#xff1a; 解决方式&#xff1a; 将ENABLE_USER_SCRIPT_SANDBOXING设为“no”即可&#xff01;

GateWay具体的使用!!!

一、全局Token过滤器 在Spring Cloud Gateway中&#xff0c;实现全局过滤器的目的是对所有进入系统的请求或响应进行统一处理&#xff0c;比如添加日志、鉴权等。下面是如何创建一个全局过滤器的基本步骤&#xff1a; 步骤1: 创建过滤器类 首先&#xff0c;你需要创建一个实现…

表---商场 nine

CREATE TABLE gao25 (id int(11) NOT NULL AUTO_INCREMENT COMMENT 自增ID,shopId int(11) NOT NULL COMMENT 店铺ID,goodsId int(11) NOT NULL COMMENT 商品ID,attrId int(11) NOT NULL COMMENT 属性名称,attrVal text NOT NULL COMMENT 属性值,createTime datetime NOT NULL …

实验 1--SQL Server2008数据库开发环境

文章目录 实验 1--SQL Server2008数据库开发环境2.4.1 实验目的2.4.2 实验准备2.4.3 实验内容1.利用 SSMS 访问系统自带的Report Server 数据库。2.熟悉了解 SMSS对象资源管理器树形菜单相关选择项的功能。(1)右键单击数据库Report Server&#xff0c;查看并使用相关功能;(2)选…

[C++基础学习]----02-C++运算符详解

前言 C中的运算符用于执行各种数学或逻辑运算。下面是一些常见的C运算符及其详细说明&#xff1a;下面详细解释一些常见的C运算符类型&#xff0c;包括其原理和使用方法。 正文 01-运算符简介 算术运算符&#xff1a; a、加法运算符&#xff08;&#xff09;&#xff1a;对两个…

基于openwrt交叉编译opencv4.9.0版本

源码包的获取 源码获取有两种方式&#xff0c;一种是通过编译时在makefile指定它的git地址&#xff0c;在编译时下载&#xff0c;这种很依赖网速&#xff0c;网速不好时&#xff0c;编译会失败。另一种是我们将源码的压缩包下载到本地&#xff0c;放到我们的SDK中&#xff0c;…

以场景驱动CMDB数据治理经验分享

数据治理是 CMDB 项目实施中难度最大、成本最高的环节&#xff0c;是一个长期治理的过程&#xff0c;而行业很少提出 CMDB 数据治理的技术实现方案。CMDB 数据治理不仅需要解决配置管理工程性的技术问题&#xff0c;还要基于运维组织的特点&#xff0c;建立适应性的配置运营能力…