本文最后更新于52 天前,其中的信息可能已经过时,如有错误请发送邮件到17671220626@139.com
在云服务器上使用springcloud+nacos配置微服务的时候,如果更新了某一个微服务,想在本地运行看效果,但是一次运行整个服务需要启动很多微服务,通过配置灰度请求头,并自定义负载均衡实现线上网关转发到本地服务,即可实现本地灰度调试
前置条件:需要服务器能够访问到本地主机,若不在一个局域网可以配置虚拟组网(Zerotier/Tailscale),或配置内网穿透。
服务器配置
gateway网关主类:
@SpringBootApplication(scanBasePackages = {
"cn.edu.cqjtu.cs.credit.gateway",
"cn.edu.cqjtu.cs.credit.common"
})
@EnableDiscoveryClient
// 关键:在这里直接引用内部的配置类
@LoadBalancerClients(value = {
@LoadBalancerClient(name = "credit-trade", configuration = TradeGrayLoadBalancerConfiguration.class),
@LoadBalancerClient(name = "credit-user", configuration = UserGrayLoadBalancerConfiguration.class),
@LoadBalancerClient(name = "credit-auth", configuration = AuthGrayLoadBalancerConfiguration.class),
@LoadBalancerClient(name = "credit-dispute", configuration = DisputeGrayLoadBalancerConfiguration.class)
}
)
public class CreditGatewayApplication {
public static void main(String[] args) {
SpringApplication.run(CreditGatewayApplication.class, args);
}
}
common包添加灰度负载均衡:
public class GrayLoadBalancer implements ReactorServiceInstanceLoadBalancer {
private final ObjectProvider<ServiceInstanceListSupplier> serviceInstanceListSupplierProvider;
private final String serviceId;
public GrayLoadBalancer(ObjectProvider<ServiceInstanceListSupplier> serviceInstanceListSupplierProvider, String serviceId) {
this.serviceInstanceListSupplierProvider = serviceInstanceListSupplierProvider;
this.serviceId = serviceId;
}
@Override
public Mono<Response<ServiceInstance>> choose(Request request) {
var serviceInstanceListSupplier = serviceInstanceListSupplierProvider.getIfAvailable(NoopServiceInstanceListSupplier::new);
return serviceInstanceListSupplier.get(request).next()
.map(instances -> processInstanceResponse(instances, request));
}
private Response<ServiceInstance> processInstanceResponse(List<ServiceInstance> instances, Request request) {
if (CollectionUtils.isEmpty(instances)) {
return new EmptyResponse();
}
// 1. 提取请求头中的 gray-tag
String grayTag = null;
if (request.getContext() instanceof RequestDataContext) {
HttpHeaders headers = ((RequestDataContext) request.getContext()).getClientRequest().getHeaders();
grayTag = headers.getFirst("gray-tag");
}
final String finalGrayTag = grayTag;
// 2. 核心过滤逻辑
if (StringUtils.hasText(finalGrayTag)) {
// 【场景A】携带灰度头:优先转发到匹配的本地实例
List<ServiceInstance> grayInstances = instances.stream()
.filter(inst -> finalGrayTag.equals(inst.getMetadata().get("gray-tag")))
.collect(Collectors.toList());
if (!grayInstances.isEmpty()) {
return new DefaultResponse(grayInstances.get(0));
}
// 如果没找到匹配的本地实例,退化回第一个可用实例(防止请求直接挂掉)
return new DefaultResponse(instances.get(0));
} else {
// 【场景B】普通请求(无头):剔除所有带 gray-tag 的本地实例
List<ServiceInstance> serverInstances = instances.stream()
.filter(inst -> !inst.getMetadata().containsKey("gray-tag")
|| !StringUtils.hasText(inst.getMetadata().get("gray-tag")))
.collect(Collectors.toList());
// 如果只剩下本地实例了(服务器全挂),则返回全部;否则只返回服务器实例
List<ServiceInstance> finalInstances = serverInstances.isEmpty() ? instances : serverInstances;
return new DefaultResponse(finalInstances.get(0));
}
}
}
配置类(只展示user服务的负载均衡策略,其他同理)
public class UserGrayLoadBalancerConfiguration {
@Bean
ReactorLoadBalancer<ServiceInstance> userGrayLoadBalancer(
LoadBalancerClientFactory loadBalancerClientFactory) {
return new GrayLoadBalancer(
loadBalancerClientFactory.getLazyProvider("credit-user", ServiceInstanceListSupplier.class),
"credit-user");
}
}
将负载均衡配置到common模块中,这样不仅是网关,所有服务都可以使用这个灰度调试负载均衡策略,保证使用openfeign调用服务的时候也能找到我们定义的LoadBalancer。
客户端配置
浏览器中搜索插件ModHeader,启用插件添加自定义灰度头

启动本地服务,带式虚拟机参数-Dspring.cloud.nacos.discovery.metadata.gray-tag=wjf,查看nacos

看到这个灰度头说明启动成功,如果没开启ModHeader,访问线上服务
线上服务代码
@RestController
@RequestMapping("/test-gray")
public class TestGrayController {
@GetMapping("/test")
public String test() {
return "test-gray";
}
}

开启ModHeader后,访问线上服务
本地服务代码
@RestController
@RequestMapping("/test-gray-1")
public class TestGrayController {
@GetMapping("/test")
public String test() {
return "test-gray";
}
}

证明灰度头能够让网关将流量转发到本地,测试通过