WebClient 日志记录入站和出站流量问题

WebClient Logging Inbound and Outbound Traffice Problem

提问人:Yusuf Erdoğan 提问时间:10/21/2023 最后编辑:Yusuf Erdoğan 更新时间:10/24/2023 访问量:49

问:

我的 WebClient 有问题。我正在将 Http 接口与 WebClient 一起使用,我必须将入站/出站流量记录到数据库中。我写了这个日志过滤器,但有些服务抛出了.我尝试了机器人不工作。我尝试了更多的东西,但我无法解决这个问题。你可以帮我吗?NullPointerExceptionsubscribeblock

Spring Boot版本:3.1.1

这是我的过滤器;

public ExchangeFilterFunction logFilter() {

    return (request, next) ->
        next.exchange(request)
            .publishOn(Schedulers.boundedElastic())
            .doOnNext(
                clientResponse -> {
                  clientResponse
                      .bodyToMono(String.class)
                      .doOnNext(
                          clientResponseAsString -> {
                            try {
                              // save request and response to database
                            } catch (Exception e) {
                              log.error(
                                  "Something went wrong in saveOutboundLog method", e.getCause());
                            }
                          })
                      .block();
                });
  }

我的客户 bean;

@Bean
public BaseApiClient client(HttpClient httpClient) {
    final var webClient =
        WebClient.builder()
            .codecs(
                clientCodecConfigurer ->
                    clientCodecConfigurer.defaultCodecs().maxInMemorySize(32 * 1024 * 1024))
            .baseUrl(clientProperties.getBaseUrl())
            .clientConnector(new ReactorClientHttpConnector(httpClient))
            .filter(logFilter())
            .build();
    final var factory =
        HttpServiceProxyFactory.builder(WebClientAdapter.forClient(webClient))
            .blockTimeout(Duration.ofSeconds(60))
            .build();
    return factory.createClient(MyClient.class);
  }
java http spring-web客户端

评论

0赞 K.Nicholas 10/23/2023
我曾经做过这个,在 WebFlux 中这不是一个简单的答案。您必须读取整个响应,将其写入数据库,然后创建一个新的 Flux 来传递。您知道当响应乘以并发请求时,响应将适合内存吗?
0赞 Yusuf Erdoğan 10/23/2023
由于内存问题,我没有建议使用数据库。但客户想要这个。我认为他们会花更多的钱
0赞 K.Nicholas 10/23/2023
我没有整理好的代码。如果你挖,它就在附近。

答:

0赞 Yusuf Erdoğan 10/24/2023 #1

我解决了这个问题。 首先,在客户端界面中使用 Mono 更改了响应类型

旧代码:

@PostExchange("/login")
LoginClientResponse login(@RequestBody LoginClientRequest loginClientRequest);

新代码:

@PostExchange("/login")
Mono<LoginClientResponse> login(@RequestBody LoginClientRequest loginClientRequest);

我也不得不改变服务。我们应该使用 doOnSuccessdoOrNext,并且必须订阅此进程。

旧服务:

final var loginClientResponse = loginClient.login(loginClientRequest);
final var token = loginClientResponse.token();

新服务:

final var loginClientResponse = loginClient.login(loginClientRequest);
    loginClientResponse.doOnSuccess(response->{
    log.info("Token is successfully retrieved for user: '{}'.", 
    loginRequest.username());
    final var token = response.token();
      //do login things..
    }).subscribe();

最后,我用这段代码更改了日志过滤器;

public ExchangeFilterFunction logFilter() {
      return ExchangeFilterFunction.ofRequestProcessor(clientRequest -> 
      {
          log.info("Request: {}",clientRequest.method() + " " + 
          clientRequest.url());
          return Mono.just(clientRequest);
      })
      .andThen(ExchangeFilterFunction.ofResponseProcessor(clientResponse -> 
         clientResponse.bodyToMono(String.class)
        .doOnNext(responseBody -> 
           {
             log.info("Response Body: {}" , responseBody);
             // log request and response anywhere 
           }
       ).thenReturn(clientResponse)));
    }

一切正常。