Spring Cloud Gate Way 灰度发布
接之前的Spring Cloud Gate Way 利用自定义的Filter配合自定义的ReactorServiceInstanceLoadBalancer负载方式实现灰度发布后,我们还会遇到一些后续的问题, 流量打到网关后实现了灰度路由,请求到了A服务(灰度版本),但是A服务请求B服务,如果B服务也上了灰度版本,该怎么办;
实现方式:
网关和OpenFeign 在引入 spring cloud loadbalance 依赖的情况下, 都会使用LoadBalance 进行负载均衡,我们继续对它进行更改;
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-loadbalancer</artifactId>
</dependency>
@Override
public Mono<Response<ServiceInstance>> choose(Request request) {
ServiceInstanceListSupplier supplier = serviceInstanceListSupplierProvider
.getIfAvailable(org.springframework.cloud.loadbalancer.core.NoopServiceInstanceListSupplier::new);
return supplier.get(request).next()
.map(serviceInstances -> processInstanceResponse(supplier, serviceInstances, request));
}
private Response<ServiceInstance> processInstanceResponse(ServiceInstanceListSupplier supplier,
List<ServiceInstance> serviceInstances, Request request) {
HttpHeaders context = (HttpHeaders) request.getContext();
Map<Boolean, List<ServiceInstance>> collect = serviceInstances.stream()
.collect(Collectors.groupingBy(e -> e.getMetadata().containsKey(loadBalanceConfig.headTag)));
List<ServiceInstance> nextInstances = serviceInstances;
String version = context.getFirst(loadBalanceConfig.headTag);
if (version != null) {
nextInstances = collect.get(grayV); //灰度标记实列
} else {
nextInstances = collect.get(normal);
}
if (nextInstances.isEmpty()) { // 没能获取到灰度版本的实列,全实例兜底
nextInstances =serviceInstances;
}
Response<ServiceInstance> serviceInstanceResponse = getInstanceResponse(nextInstances);
if (supplier instanceof SelectedInstanceCallback && serviceInstanceResponse.hasServer()) {
((SelectedInstanceCallback) supplier).selectedServiceInstance(serviceInstanceResponse.getServer());
}
return serviceInstanceResponse;
}
LoadBalance实现
@Override
public Mono<Response<ServiceInstance>> choose(Request request) {
ServiceInstanceListSupplier supplier = serviceInstanceListSupplierProvider
.getIfAvailable(org.springframework.cloud.loadbalancer.core.NoopServiceInstanceListSupplier::new);
return supplier.get(request).next()
.map(serviceInstances -> processInstanceResponse(supplier, serviceInstances, request));
}
Boolean grayV = Boolean.TRUE;
Boolean normal = Boolean.FALSE;
private Response<ServiceInstance> processInstanceResponse(ServiceInstanceListSupplier supplier,
List<ServiceInstance> serviceInstances, Request request) {
DefaultRequestContext context = (DefaultRequestContext) request.getContext();
RequestData clientRequest = (RequestData)context.getClientRequest();
HttpHeaders headers = clientRequest.getHeaders();
Map<Boolean, List<ServiceInstance>> collect = serviceInstances.stream()
.collect(Collectors.groupingBy(e -> e.getMetadata().containsKey(loadBalanceConfig.headTag)));
List<ServiceInstance> nextInstances;
String version = headers.getFirst(loadBalanceConfig.headTag);
if (version != null) {
nextInstances = collect.get(grayV); //灰度标记实列
} else {
nextInstances = collect.get(normal);
}
if (nextInstances.isEmpty()) { // 没能获取到灰度版本的实列
nextInstances =serviceInstances;
}
Response<ServiceInstance> serviceInstanceResponse = getInstanceResponse(nextInstances);
if (supplier instanceof SelectedInstanceCallback && serviceInstanceResponse.hasServer()) {
((SelectedInstanceCallback) supplier).selectedServiceInstance(serviceInstanceResponse.getServer());
}
return serviceInstanceResponse;
}
与网关实现的差异在与 request.getContext();不在是Header 对象; 这一步需要注意否则会出现类转换异常;
实现FeignClient 需要的配置类
public class LoadBalancerConfig {
@Bean
public ReactorLoadBalancer<ServiceInstance> randomLoadBalancer(Environment environment, LoadBalancerClientFactory loadBalancerClientFactory, GrayLoadBalanceProperties grayLoadBalanceConfig){
String name = environment.getProperty(LoadBalancerClientFactory.PROPERTY_NAME);
return new GrayLoadBalance(loadBalancerClientFactory.getLazyProvider(name, ServiceInstanceListSupplier.class), name,grayLoadBalanceConfig);
}
}
使用@LoadBalancerClient(configuration = LoadBalancerConfig.class) 对Loadbalance进行配置;