日升时奋斗,日落时自省
目录
1、实现框架
2、负载均衡模块
3、封装负载均衡器
4、网关模块
5、服务模块
5.1、注册为灰度服务实例
5.2、设置负载均衡器
5.3、传递灰度标签
1、实现框架
Spring Cloud全链路灰色发布实现构架:
灰度发布的具体实现 :
前端程序在灰度测试用户Header头中打上标签,例如Header中添加一个参数"gray-tay:true",其表示要进行会灰度测试的(访问灰度服务),而其他则为正式服务
在负载均衡器Spring Cloud LoadBalancer中,拿到Header中的"gray-tag"进行判断,如果此标签不为空,并等于"true"的话,表示要访问灰度发布的服务,否则只访问正式的服务
在网关Spring Cloud Gateway中,Header标签"gray-tag:true"继续往下一服务中传传递(因为通过网关的时候,是从新发送请求,Header中是没有灰度标签的)
在后续的调用服务中,需要实现以下两个关键功能:
- 在负载均衡器Spring Cloud LoadBalancer中,判断灰度发布标签,将请求分发到对应服务
- 将灰度发布标签(如果存在),继续传递给下一个调用的服务
经过第四步的反复传递之后,整个Spring Cloud全链路的灰度发布就完成了
2、负载均衡模块
实现ReactorServiceInstanceLoadBalancer接口
public class GrayLoadBalancer implements ReactorServiceInstanceLoadBalancer{
private static final Log log = LogFactory.getLog(GrayLoadBalancer.class);
private final String serviceId;
private AtomicInteger position; // 位置,下标
private ObjectProvider<ServiceInstanceListSupplier> serviceInstanceListSupplierProvider;
public GrayLoadBalancer(ObjectProvider<ServiceInstanceListSupplier> serviceInstanceListSupplierProvider, String serviceId) {
this.serviceId = serviceId;
this.serviceInstanceListSupplierProvider = serviceInstanceListSupplierProvider;
this.position = new AtomicInteger(new Random().nextInt(1000)); //随机进行设置一个值
}
public Mono<Response<ServiceInstance>> choose(Request request) {
// 提供备选的服务实例列表
ServiceInstanceListSupplier supplier = (ServiceInstanceListSupplier) this.serviceInstanceListSupplierProvider.getIfAvailable(NoopServiceInstanceListSupplier::new);
// 选择服务实例
return supplier.get(request).next().map((serviceInstances) -> {
return this.processInstanceResponse(supplier, serviceInstances, request);
});
}
private Response<ServiceInstance> processInstanceResponse(ServiceInstanceListSupplier supplier,
List<ServiceInstance> serviceInstances,
Request request) {
// 从备选的服务列表中选择一个具体的服务实例
Response<ServiceInstance> serviceInstanceResponse = this.getInstanceResponse(serviceInstances,
request);
if (supplier instanceof SelectedInstanceCallback && serviceInstanceResponse.hasServer()) {
((SelectedInstanceCallback) supplier).selectedServiceInstance((ServiceInstance) serviceInstanceResponse.getServer());
}
return serviceInstanceResponse;
}
private Response<ServiceInstance> getInstanceResponse(List<ServiceInstance> instances,
Request request) {
// 实例为空 首先进行实例判空
if (instances.isEmpty()) {
if (log.isWarnEnabled()) {
//判空后 给予警告
log.warn("No servers available for service: " + this.serviceId);
}
//返回响应
return new EmptyResponse();
} else { // 服务不为空
// 灰度节点的业务实现
// 0.得到 Request 对象[通过方法参数的传递得到此对象]
// 1.从 Request 对象的 Header 中得到灰度标签
RequestDataContext requestContext = (RequestDataContext) request.getContext();
HttpHeaders headers = requestContext.getClientRequest().getHeaders();
List<String> headersList = headers.get(GlobalVariable.GRAY_TAGE);
if (headersList != null && headersList.size() > 0 &&
headersList.get(0).equals("true")) { // 灰度请求
// 灰度列表 这里采用lambda的方法进行过滤
List<ServiceInstance> grayList = instances.stream().
filter(i -> i.getMetadata().get(GlobalVariable.GRAY_TAGE) != null &&
i.getMetadata().get(GlobalVariable.GRAY_TAGE).equals("true")).
toList();
if (grayList.size() > 0) { // 存在灰度服务节点
instances = grayList;
}
} else { // 正式节点
// 2.将实例进行分组【正式服务列表|灰度服务列表】
instances = instances.stream().
filter(i -> i.getMetadata().get(GlobalVariable.GRAY_TAGE) == null ||
!i.getMetadata().get(GlobalVariable.GRAY_TAGE).equals("true")).
toList();
}
// 3.使用负载均衡算法选择上一步列表中的某一个节点
int pos = this.position.incrementAndGet() & Integer.MAX_VALUE;
ServiceInstance instance = (ServiceInstance)instances.get(pos % instances.size());
return new DefaultResponse(instance);
}
}
}
3、封装负载均衡器
负载均衡器修改之后,我们需要使用修改后的负载均衡器
public class GrayLoadBalancerConfig {
@Bean
public ReactorLoadBalancer<ServiceInstance> GrayLoadBalancer(Environment environment, LoadBalancerClientFactory loadBalancerClientFactory) {
String name = environment.getProperty(LoadBalancerClientFactory.PROPERTY_NAME);
return new GrayLoadBalancer(loadBalancerClientFactory.getLazyProvider(name, ServiceInstanceListSupplier.class), name);
}
}
4、网关模块
网关是能收到灰度发布标识,但是调用的服务是不能获得Header中的灰度标签,需要在网关这里传递给调用的服务
@Component // 实现全局过滤器 进行过滤设置
public class LoadBalancerFilter implements GlobalFilter {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
// 事件处理 exchange 获取请求头
ServerHttpRequest request=exchange.getRequest();
// 事件处理 exchange 获取响应头
ServerHttpResponse response = exchange.getResponse();
// 请求头参数 获取的参数 获取 key 对应的 value值
if(request.getQueryParams().getFirst(GlobalVariable.GRAY_TAGE)!=null){
//获取响应请求头 设置灰度标签的参数 为 true
response.getHeaders().set(GlobalVariable.GRAY_TAGE,"true");
}
// 返回 一个调用链 过滤条件的
return chain.filter(exchange);
}
}
5、服务模块
5.1、注册为灰度服务实例
在负载均衡器上获得了metaData数据,这个数据就是从灰色服务实例中获取,数据存储在nacos中
spring:
application:
name: user-service-gray
cloud:
nacos:
discovery:
server-addr: localhost:8848
username: nacos
password: nacos
metadata: {"gray-tag":"true"} #metadata可以自定义一些元数据
server:
port: 0
5.2、设置负载均衡器
在服务启动类设置负载均衡器和开启OpenFeign服务:
defaultConfiguration = GrayLoadBalancerConfig.class全局负载均衡器的配置
@SpringBootApplication
@EnableFeignClients //启用Fegin客户端使用
//这里需要调用我们写的负载均衡器,我们自定义的负载均衡器主要就是为了检测gray参数
@LoadBalancerClients(defaultConfiguration = GrayLoadBalancerConfig.class)
public class NewUserServiceApplication {
public static void main(String[] args) {
SpringApplication.run(NewUserServiceApplication.class, args);
}
}
5.3、传递灰度标签
针对其他服务实例需要处理请求头信息
RequestInterceptor是一个Spring Cloud Feign中的接口,它用于在发起Feign请求前和请求后执行一些自定义的操作,例如添加请求头信息、记录请求日志、实现认证授权等
RequestInterceptor接口中包含一个方法:文章来源:https://www.toymoban.com/news/detail-810259.html
apply方法:在发送请求之前执行的操作,比如添加请求头、修改请求路径或请求参数等文章来源地址https://www.toymoban.com/news/detail-810259.html
@Component
public class FeignRequestInterceptor implements RequestInterceptor {
//相当于是一个前置操作 处理请求头信息,记录请求日志、实现认证授权
@Override
public void apply(RequestTemplate requestTemplate) {
//获取请求参数
ServletRequestAttributes attributes = (ServletRequestAttributes)RequestContextHolder.getRequestAttributes();
//拿到请求
HttpServletRequest request = attributes.getRequest();
//获取请求头参数 当然调用上面这条代码的 request是一样的
Enumeration<String> headerNames = attributes.getRequest().getHeaderNames();
//将获取的请求中的所有参数
while(headerNames.hasMoreElements()){
//将key先获取
String key=headerNames.nextElement();
//通过key 获得 value
String value=request.getHeader(key);
//将获取的参数 放到header头中采用键值 key:value 放到请求模板中
requestTemplate.header(key,value);
}
}
}
到了这里,关于SpringCloud全链路灰度发布的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!