这里我们首先通过请求属性获取到 span context,这里我们添加了两个属性,如果出现了异常则会记录异常信息,最后在 finally
代码块中结束 Span 。
现在我们重新启动容器,当我们访问订单列表后就可以看到 Jaeger UI 中多了一个 GET /api/orders
的 span 了,并且该 span 和前端 frontend 服务的 span 关联起来了 。
文章插图
当然这还不够,因为我们的订单列表接口还会去请求
user-service
服务来获取用户信息,还会去请求 catalog-service
服务获取书籍信息,所以我们还需要在这两个请求中也注入我们这里的 span,这样就可以将整个链路串联起来了 。首先针对
TokenInterceptor
拦截器我们先创建一个子 span,代码如下所示:// src/main/java/com/youdianzhishi/orderservice/interceptor/TokenInterceptor.javapackage com.youdianzhishi.orderservice.interceptor;// ......@Componentpublic class TokenInterceptor implements HandlerInterceptor { @Autowired private WebClient webClient; @Autowired private Tracer tracer; @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) { // 先获取 Span Span currentSpan = (Span) request.getAttribute("currentSpan"); Context context = Context.current().with(currentSpan); // 创建新的 Span,作为子 Span Span span = tracer.spanBuilder("GET /api/userinfo") .setParent(context).startSpan(); // 将子 Span 设置为当前上下文,相当于切换上下文到子 Span try (Scope scope = span.makeCurrent()) { try { String token = request.getHeader("Authorization"); if (token == null) { response.setStatus(HttpStatus.UNAUTHORIZED.value()); span.addEvent("Token is null").setStatus(StatusCode.ERROR); return false; } // 从环境变量中获取 userServiceUrl String userServiceEnv = System.getenv("USER_SERVICE_URL"); String userServiceUrl = userServiceEnv != null ? userServiceEnv : "http://localhost:8080"; User user = webClient.get() .uri(userServiceUrl + "/api/userinfo") .header(HttpHeaders.AUTHORIZATION, token) .retrieve() .onStatus(httpStatus -> httpStatus.equals(HttpStatus.UNAUTHORIZED), clientResponse -> Mono.error(new RuntimeException("Unauthorized"))) .onStatus( httpStatus -> httpStatus.is4xxClientError() && !httpStatus.equals(HttpStatus.UNAUTHORIZED), clientResponse -> Mono.error(new RuntimeException("Other Client Error"))) .bodyToMono(User.class) .block(); if (user != null) { request.setAttribute("user", user); span.setAttribute("user_id", user.getId()); return true; } else { response.setStatus(HttpStatus.UNAUTHORIZED.value()); span.addEvent("User is null").setStatus(StatusCode.ERROR); return false; } } catch (RuntimeException e) { span.recordException(e).setStatus(StatusCode.ERROR, e.getMessage()); if (e.getMessage().equals("Unauthorized")) { response.setStatus(HttpStatus.UNAUTHORIZED.value()); } else { response.setStatus(HttpStatus.BAD_REQUEST.value()); } return false; } catch (Exception e) { span.recordException(e).setStatus(StatusCode.ERROR, e.getMessage()); response.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value()); return false; } finally { request.setAttribute("parentSpan", span); span.end(); } } }}
推荐阅读
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- 通过诗经楚辞取的公司名字
- 动量定理的应用 动量定理的应用视频讲解
- 深入浅出、玩转Java多线程
- 揭秘十个必知的 JavaScript 3D 库,打造顶级3D炫酷效果!
- 图灵测试已死!ChatGPT通过人类考试也不算,超强AI评估新秀「逻辑谜题」
- 王者荣耀的段位排行榜是通过Redis实现的?
- 我不再写Javascript了
- Java 单元测试及常用语句
- 构建高效的Java网络应用,体验秒级响应
- 谷歌推出 AlloyDB AI,帮助开发者创建生成式 AI 应用