面试题答案
一键面试基于ThreadLocal的通用解决方案设计
- 数据结构设计
- 设计一个上下文对象,例如
TraceContext
,用于存储需要传递的上下文数据,如用户标识和请求追踪ID。
public class TraceContext { private String userId; private String traceId; // 构造函数、getter和setter方法 public TraceContext(String userId, String traceId) { this.userId = userId; this.traceId = traceId; } public String getUserId() { return userId; } public void setUserId(String userId) { this.userId = userId; } public String getTraceId() { return traceId; } public void setTraceId(String traceId) { this.traceId = traceId; } }
- 使用
ThreadLocal
来存储该上下文对象,定义一个工具类TraceContextHolder
。
public class TraceContextHolder { private static final ThreadLocal<TraceContext> threadLocal = new ThreadLocal<>(); public static void setTraceContext(TraceContext context) { threadLocal.set(context); } public static TraceContext getTraceContext() { return threadLocal.get(); } public static void clearTraceContext() { threadLocal.remove(); } }
- 设计一个上下文对象,例如
- 跨线程传递机制
- 在分布式微服务架构中,当一个服务调用另一个服务时,需要将上下文数据传递过去。如果是基于HTTP调用,可以将上下文数据放在HTTP头中。例如,在发起HTTP请求前,从
ThreadLocal
中获取上下文数据,并设置到HTTP头中。
TraceContext context = TraceContextHolder.getTraceContext(); if (context != null) { HttpHeaders headers = new HttpHeaders(); headers.set("UserId", context.getUserId()); headers.set("TraceId", context.getTraceId()); // 使用RestTemplate或其他HTTP客户端发起请求,并带上headers }
- 在接收端服务,从HTTP头中提取上下文数据,并重新设置到
ThreadLocal
中。
@RestController public class SomeController { @RequestMapping("/someEndpoint") public String handleRequest(HttpServletRequest request) { String userId = request.getHeader("UserId"); String traceId = request.getHeader("TraceId"); TraceContext context = new TraceContext(userId, traceId); TraceContextHolder.setTraceContext(context); // 处理业务逻辑 TraceContextHolder.clearTraceContext(); return "Response"; } }
- 对于非HTTP调用(如消息队列),同样需要将上下文数据作为消息的一部分传递,在消费者端重新设置到
ThreadLocal
。
- 在分布式微服务架构中,当一个服务调用另一个服务时,需要将上下文数据传递过去。如果是基于HTTP调用,可以将上下文数据放在HTTP头中。例如,在发起HTTP请求前,从
- 与Spring Cloud的整合思路
- 过滤器(Filter):在Spring Cloud中,可以利用
Zuul
或Spring Web的过滤器来处理上下文数据的传递。例如,在Zuul
的pre
过滤器中,从ThreadLocal
获取上下文数据并设置到HTTP头中;在post
过滤器中,清除ThreadLocal
中的数据。
import com.netflix.zuul.ZuulFilter; import com.netflix.zuul.context.RequestContext; import org.springframework.stereotype.Component; import javax.servlet.http.HttpServletRequest; @Component public class TraceFilter extends ZuulFilter { @Override public String filterType() { return "pre"; } @Override public int filterOrder() { return 1; } @Override public boolean shouldFilter() { return true; } @Override public Object run() { RequestContext ctx = RequestContext.getCurrentContext(); HttpServletRequest request = ctx.getRequest(); TraceContext context = TraceContextHolder.getTraceContext(); if (context != null) { ctx.addZuulRequestHeader("UserId", context.getUserId()); ctx.addZuulRequestHeader("TraceId", context.getTraceId()); } return null; } }
- Feign客户端:如果使用Feign进行服务间调用,可以通过
RequestInterceptor
来设置上下文数据到HTTP头。
import feign.RequestInterceptor; import feign.RequestTemplate; import org.springframework.stereotype.Component; @Component public class TraceFeignInterceptor implements RequestInterceptor { @Override public void apply(RequestTemplate template) { TraceContext context = TraceContextHolder.getTraceContext(); if (context != null) { template.header("UserId", context.getUserId()); template.header("TraceId", context.getTraceId()); } } }
- 全局异常处理:在Spring Cloud应用中,全局异常处理可以在异常发生时,将上下文数据(如请求追踪ID)记录到日志中,方便问题排查。
import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.ControllerAdvice; import org.springframework.web.bind.annotation.ExceptionHandler; @ControllerAdvice public class GlobalExceptionHandler { @ExceptionHandler(Exception.class) public ResponseEntity<String> handleException(Exception ex) { TraceContext context = TraceContextHolder.getTraceContext(); String traceId = context != null? context.getTraceId() : "unknown"; // 记录异常日志,包含traceId return new ResponseEntity<>("Internal Server Error", HttpStatus.INTERNAL_SERVER_ERROR); } }
- 过滤器(Filter):在Spring Cloud中,可以利用
通过以上设计,可以在分布式微服务架构中基于ThreadLocal
实现上下文数据的有效传递,并与Spring Cloud等分布式框架进行良好的整合。