前言
在Spring Cloud微服务项目中,通常会有多个服务,如果只是修改其中一个服务,该如何本地调试呢,如下图:
在这个项目中,只是修改了服务C,而我现在要在本地调试服务C,需要在本地搭建一套完整的环境吗,答案是不需要,我们只需要修改几个地方,就可以将流量指定到本地的服务C,如下图:
1.前端改造
浏览器中打开F12,在本地存储中添加environment=dev,如下图:
然后前端在发起http请求的时候,读取上面配置,在每个请求中添加请求头environment=dev。至此,前端改造完成。
2.注册中心改造
这里注册中心用的是nacos,在nacos控制台上找到对应的实例,编辑元数据,加上environment=dev,如下图
3.Feign改造
实际上,前端传递的请求头environment只会通过网关传递到服务A,而服务A到服务B的链路中,Feign默认不会添加environment头,所以我们需要通过RequestInterceptor手动添加请求头,如下:
@Component
public class FeignInterceptor implements RequestInterceptor {
@Override
public void apply(RequestTemplate requestTemplate) {
try {
HttpServletRequest request = ((ServletRequestAttributes) (RequestContextHolder.getRequestAttributes())).getRequest();
String requestEnvironment = request.getHeader(HEADER_ENVIRONMENT);
if (StringUtils.isNotBlank(requestEnvironment)) {
requestTemplate.header(HEADER_ENVIRONMENT, requestEnvironment);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
4.Ribbon改造
Ribbon是微服务架构中的负载均衡组件,它提供了IRule接口作为扩展点,使我们很容易在服务转发中加入自定义逻辑,Nacos框架提供了NacosRule,我们可以作为参考,实现流量的指定,如下
public class NacosExtendRule extends AbstractLoadBalancerRule {
private static final Logger LOGGER = LoggerFactory.getLogger(NacosExtendRule.class);
@Autowired
private NacosDiscoveryProperties nacosDiscoveryProperties;
@Autowired
private NacosServiceManager nacosServiceManager;
public final static String HEADER_ENVIRONMENT = "environment";
public NacosExtendRule() {
}
public Server choose(Object key) {
try {
String requestEnvironment = null;
try {//尝试获取请求头中的environment
HttpServletRequest request = ((ServletRequestAttributes) (RequestContextHolder.getRequestAttributes())).getRequest();
requestEnvironment = request.getHeader(HEADER_ENVIRONMENT);
} catch (Exception ex) {
}
String environment = requestEnvironment;
String group = this.nacosDiscoveryProperties.getGroup();
DynamicServerListLoadBalancer loadBalancer = (DynamicServerListLoadBalancer) this.getLoadBalancer();
String name = loadBalancer.getName();
NamingService namingService = this.nacosServiceManager.getNamingService(this.nacosDiscoveryProperties.getNacosProperties());
List<Instance> instances = namingService.selectInstances(name, group, true);
List<Instance> instancesToChoose = null;
if (StringUtils.isNotBlank(environment)) { //如果环境配置不为空,先找对应环境的实例列表
instancesToChoose = instances.stream().filter(x -> Objects.equals(environment, x.getMetadata().get(HEADER_ENVIRONMENT))).collect(Collectors.toList());
}
if (CollectionUtils.isEmpty(instancesToChoose)) { //如果实例列表为空,则尝试找环境为空的实例列表
instancesToChoose = instances.stream().filter(x -> x.getMetadata().get(HEADER_ENVIRONMENT) == null).collect(Collectors.toList());
}
if (CollectionUtils.isEmpty(instancesToChoose)) {
LOGGER.warn("no instance in service {}", name);
return null;
}
Instance instance = ExtendBalancer.getHostByRandomWeight2(instancesToChoose);
return new NacosServer(instance);
} catch (Exception var10) {
LOGGER.warn("NacosRule error", var10);
return null;
}
}
public void initWithNiwsConfig(IClientConfig iClientConfig) {
}
}
NacosExtendRule的逻辑主要是获取HttpServletRequest的请求头environment,如果存在则匹配元数据中的environment,匹配不到就寻找元数据为空的注册实例。
扩展
其实不止本地调试场景需要指定流量,还有下面场景:
- 项目中有多个版本迭代,例如V1.1版本修改AB,V1.2版本修改C,两个版本都需要测试,这个时候就需要配置两个environment。
- 生产环境中需要灰度上线,这个时候可以根据用户或者其他条件指定部分流量到新上线的服务。