自定义Ribbon的IRule实现服务之间的灰度发布
一、实现思路
- 考虑如何在服务之间实现灰度,切入点在哪里?
服务与服务之间的调用时通过HTTP协议,如果要实现灰度,只能在调用的时候来实现。在Springcloud体系中,负责服务间调用的组件就是Ribbon。
Ribbon是负责客户端的负载均衡,默认的规则是区域轮训。
因此,如果要实现服务间的灰度发布,则需要重新定义Ribbon的调度规则即可。
Ribbon的规则,则是通过IRule实现(ribbon的loadbalance)
二、实现方式
2.1 实现思路解析
要想实现服务间的灰度,则需要考虑以下几个问题:
- 服务调用者的身份怎么确定?换言之,负载均衡规则中,如何获得当前用户的信息?
因为只有知道了当前用户的信息才能确定用户应该命中哪个规则。
由于服务调用者和Ribbon之间是属于同一个线程,所以可以使用ThreadLocal来实现用户信息的传递
因此,可以考虑使用AOP来实现在用户请求时,将用户信息拦截解析并存放到Threadlocal中 - 被调用的服务如何区分版本?
如果注册中心使用的是eureka,那么可以将服务器信息存放与metadata中 - 灰度服务的自定义metadata怎么获取?
从注册中心获取到可用服务列表后,就能够从每个服务实例中获取到metadata
2.2 实现方式一:自定Ribbon规则
- 自定义Threadlocal,用于存放上下文信息
package com.yang.apipassenger.gray;
import org.springframework.stereotype.Component;
@Component
public class PassengerContext {
private static final ThreadLocal local=new ThreadLocal();
public static <T> T get(){
return (T) local.get();
}
public static <T> void set(T t){
local.set(t);
}
}
- 自定义切面,将用户请求信息解析后存放与Threadlocal中
package com.yang.apipassenger.aspect;
import com.alibaba.fastjson.JSON;
import com.yang.apipassenger.entity.GrayStrategy;
import com.yang.apipassenger.gray.PassengerContext;
import com.yang.apipassenger.service.IGrayStrategyService;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.http.HttpServletRequest;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
@Aspect
@Component
@Slf4j
public class RequestAspect {