SpringCloud2023配置本地灰度调试
本文最后更新于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";
    }
}

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

暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇