分布式系统中,系统往往被拆分成许多模块单独部署,为了响应一个客户端请求,服务端可能需要调用多个上游服务。此种多服务调用模式会增加故障排查的难度,针对这种问题可以通过分布式链路追踪技术来解决。
设想一个最简单的分布式微服务调用,consumer服务通过feign调用provider服务的接口来响应客户端请求(架构图如下),日志采用logback。

/**
* @description http请求拦截
*/
public class RequestInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
String traceId = request.getHeader(CfgConst.TRACE_KEY);
if (traceId == null){
traceId = UUID.randomUUID().toString().replace("-", "");
}
MDC.put(CfgConst.TRACE_KEY, traceId);
return true;
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
MDC.remove(CfgConst.TRACE_KEY);
}
}
/**
* @description feign拦截器,设置traceId
*/
@Component
public class FeignInterceptor implements RequestInterceptor {
@Override
public void apply(RequestTemplate requestTemplate) {
String key = CfgConst.TRACE_KEY;
String traceId = MDC.get(key);
if ( !StringUtils.isEmpty(traceId) ){
requestTemplate.header(CfgConst.TRACE_KEY, traceId);
}
}
}
<springProfile name="dev">
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d{HH:mm:ss} [%thread] %-5level |%X{traceId}| %logger{36} - %msg%npattern>
encoder>
appender>
<root level="DEBUG">
<appender-ref ref="CONSOLE"/>
root>
springProfile>
/**
* @description 统一封装返回格式,带上统一日志追踪
*/
@RestControllerAdvice
@Slf4j
public class ResultAdvice implements ResponseBodyAdvice {
@Resource
private ObjectMapper objectMapper;
@Override
public boolean supports(MethodParameter returnType, Class converterType) {
return true;
}
@Override
public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {
JsonResult jsonResult = JsonResult.success(body);
String traceId = MDC.get(CfgConst.TRACE_KEY);
jsonResult.setTraceId(traceId);
if (body instanceof String){
try {
return objectMapper.writeValueAsString(jsonResult);
} catch (JsonProcessingException e) {
log.error("system error", e);
return e.getMessage();
}
}
return jsonResult;
}
}



<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-sleuthartifactId>
<version>2.1.3.RELEASEversion>
dependency>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-zipkinartifactId>
<version>2.1.3.RELEASEversion>
dependency>
java -jar zipkin-server-xxx-exec.jar
spring:
zipkin:
base-url: http://localhost:9411
sender:
#web/kafka/RabbitMQ
type: web
sleuth:
sampler:
#所有请求都采集
probability: 1.0
web:
enabled: true
<include resource="org/springframework/boot/logging/logback/defaults.xml"/>
<springProperty scope="context" name="springAppName" source="spring.application.name"/>
<property name="LOG_FILE" value="${BUILD_FOLDER:-build}/${springAppName}"/>
<property name="CONSOLE_LOG_PATTERN"
value="%clr(%d{yyyy-MM-dd HH:mm:ss.SSS}){faint} %clr(${LOG_LEVEL_PATTERN:-%5p}) %clr(${PID:- }){magenta} %clr(---){faint} %clr([%15.15t]){faint} %clr(%-40.40logger{39}){cyan} %clr(:){faint} %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}"/>
<appender name="console" class="ch.qos.logback.core.ConsoleAppender">
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<level>DEBUGlevel>
filter>
<encoder>
<pattern>${CONSOLE_LOG_PATTERN}pattern>
<charset>utf8charset>
encoder>
appender>
<root level="DEBUG">
<appender-ref ref="console"/>
root>



