前言
上一章我们增加了Logback
配置,这里我们来记录请求和响应的报文日志,进一步完善我们的微服务网关。
一、准备
1. 提供者服务
这里我们使用之前的提供者服务
package org.example.nacos.provider.controller;
import org.example.common.model.Result;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* Create by zjg on 2024/7/21
*/
@RestController
@RequestMapping("/provider/")
public class HelloController {
@RequestMapping("hello")
public Result<String> hello(){
return Result.success("请求成功","hello provider");
}
}
2. 启动服务
这里我们启动三个服务:
- 用户服务:完成用户认证
- 网关服务:应用统一入口
- 提供者服务:提供服务接口
二、日志全局过滤器
这里的日志过滤器需要往前放,要打印请求报文,防止在处理过滤器链的时候发生异常,无法正常记录。
package org.example.gateway.filter;
import org.example.common.model.Result;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.cloud.gateway.filter.factory.rewrite.ModifyResponseBodyGatewayFilterFactory;
import org.springframework.core.Ordered;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.reactive.function.server.HandlerStrategies;
import org.springframework.web.reactive.function.server.ServerRequest;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
/**
* Create by zjg on 2024/8/4
*/
@Component
public class LogGlobalFilter implements GlobalFilter, Ordered {
Logger logger = LoggerFactory.getLogger(LogGlobalFilter.class);
@Autowired
private ModifyResponseBodyGatewayFilterFactory modifyResponseBodyFilter;
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
ServerHttpRequest request = exchange.getRequest();
String method = request.getMethod().name();
MediaType contentType = request.getHeaders().getContentType();
String requestId = request.getId();
if (HttpMethod.GET.name().equals(method)) {
logger.debug("\n[GET请求" + requestId + "]:" + request.getURI());
} else if (HttpMethod.POST.name().equals(method)&&contentType.isCompatibleWith(MediaType.APPLICATION_JSON)) {
ServerRequest sr = ServerRequest.create(exchange, HandlerStrategies.withDefaults().messageReaders());
sr.bodyToMono(String.class).flatMap(requestBody -> {
logger.debug("\n[POST请求" + requestId + "]:" + request.getURI()+",[Body]:"+ StringUtils.trimAllWhitespace(requestBody));
return Mono.just(requestBody);
}).subscribe();
}
return modifyResponseBodyFilter.apply(
new ModifyResponseBodyGatewayFilterFactory.Config().setRewriteFunction(Object.class, Object.class, (swe, responseBody)->{
try {
if(swe.getResponse().getHeaders().getContentType().isCompatibleWith(MediaType.APPLICATION_JSON)){
logger.debug("\n[响应" + requestId + "]" + "["+swe.getResponse().getStatusCode()+"][Response]:"+ new ObjectMapper().writeValueAsString(responseBody));
}
} catch (JsonProcessingException e) {
throw new RuntimeException(e);
}
return Mono.just(responseBody);
})).filter(exchange, chain);
}
@Override
public int getOrder() {
return -2;
}
}
三、单元测试
这里我们以
GET
和POST
请求为例,分别查看这两笔交易的日志内容。
1. GET
[GET请求f6de8016-2]:http://localhost:8888/provider/hello?a=1
2024-08-04 20:59:19.840 DEBUG 860 [gateway-service] [ctor-http-nio-5] o.e.gateway.filter.LogGlobalFilter :54 :
[响应f6de8016-2][200 OK][Response]:{"code":0,"message":"请求成功","data":"hello provider"}
2. POST
2024-08-04 21:00:31.562 DEBUG 860 [gateway-service] [ctor-http-nio-3] o.e.gateway.filter.LogGlobalFilter :42 :
[POST请求f6de8016-3]:http://localhost:8888/provider/hello,[Body]:{"type":"ssr"}
2024-08-04 21:00:31.571 DEBUG 860 [gateway-service] [ctor-http-nio-6] o.e.gateway.filter.LogGlobalFilter :54 :
[响应f6de8016-3][200 OK][Response]:{"code":0,"message":"请求成功","data":"hello provider"}
大家可以看到我们可以根据内部请求ID轻松地关联到请求报文和响应报文,进一步增强了我们网关的健壮性
总结
微服务的网络请求是复杂的,有时候一个请求可能会串联多个服务,后面会通过链路追踪的方式来观察和分析一个请求的生命周期,敬请期待,落之。