记录一次线上网关bug---gateway

问题描述

最近一次压测的过程中发现一个奇怪的bug,调A服务的接口,但是ip和端口却是B服务的,调B服务的接口,但是ip和端口却是A服务的。

比如调用A服务的接口,本来应该是 http://192.168.10.10:8100/api/testA,调用B服务的接口,应该是 http://192.168.10.10:8200/api/testB,结果却变成了 http://192.168.10.10:8100/api/testB和 http://192.168.10.10:8200/api/testA。

而且这是偶发事件,只有在压测的时候才会出现

排查

我们首先怀疑的是服务的端口是不是谁配置错了,检查一遍很快排除掉了。因为这是偶发现象,所以如果是配置错了,那肯定是每次请求都报错,而事实上是偶尔报错。后来同事发现了调用微服务的WebClient是个单例,那么好,问题肯定是出在单例上面了。下面是我们原先的代码:

原先的代码

注册WebClient

    @Bean
    @LoadBalanced     // 如果不添加,无法通过服务名进行调用,只能通过ip调用
    public WebClient.Builder serviceA() {
        return WebClient.builder();
    }

微服务调用WebClient

    public Mono<Result<ClaimsObjVO>> requestA(String accessToken) {
        return authWebBuilder.baseUrl("http://demo-serviceA").build().get().uri(uriBuilder ->
                uriBuilder.path("/api/testA").queryParam("accessToken", accessToken).build()
        ).retrieve().bodyToMono(new ParameterizedTypeReference<Result<ClaimsObjVO>>() {
        });

        Result<ClaimsObjVO> claimsObjVOResult = authFeignClient.verifyToken(accessToken);
        return Mono.just(claimsObjVOResult);
    }

第二个服务

    public Mono<Result<String>> requestB(Long userId) {
        return webBuilder.baseUrl("http://demo-serviceB").build().get().uri(uriBuilder ->
                uriBuilder.path("/api/testB").queryParam("userId", userId).build()
        ).retrieve().bodyToMono(new ParameterizedTypeReference<Result<String>>() {
        });
    }

从上面的代码可以看出来,两个服务用的是同一个WebClient,高并发的情况下,当线程1完成了authWebBuilder.baseUrl("http://demo-serviceA").build()以后就已经把WebClient的请求地址给确定了,这时候线程2调用B服务的接口时就会使用A的WebClient去调用,导致异常的发生。

解决方案

解决方案有两种,一种是不要用WebClient去请求微服务,这个当时是因为我们升级了高版本的springcloud导致feign调用有问题,所以改成了WebClient。
第二种是给每个微服务地址创建一个webclient的实例,代码如下

改造后代码

    @Bean("serviceA")
    @LoadBalanced     // 如果不添加,无法通过服务名进行调用,只能通过ip调用
    public WebClient.Builder serviceA() {
        return WebClient.builder();
    }

    @Bean("serviceB")
    @LoadBalanced     // 如果不添加,无法通过服务名进行调用,只能通过ip调用
    public WebClient.Builder serviceB() {
        return WebClient.builder();
    }

调用的时候指定注入不同的对象即可

    private final WebClient.Builder webBuilder;

    public UserWebClient(@Qualifier("serviceA") WebClient.Builder webBuilder) {
        this.webBuilder = webBuilder;
    }
posted @ 2024-09-19 16:09  leecoders  阅读(22)  评论(0)    收藏  举报