2026/4/16 19:02:37
网站建设
项目流程
邯郸有建网站的吗,平潭县建设局网站,seo分析网站,wordpress 即将跳转场景#xff1a;用户下单慢#xff0c;该找谁#xff1f;
用户反馈#xff1a;”我下单等了10秒才成功#xff01;”
没有链路追踪时#xff1a;
开发A#xff1a;”我订单服务没问题啊#xff01;”开发B#xff1a;”我商品服务响应很快#xff01;”开发C…场景用户下单慢该找谁用户反馈”我下单等了10秒才成功”没有链路追踪时开发A”我订单服务没问题啊”开发B”我商品服务响应很快”开发C”我库存服务1秒就返回了”运维”网关看着也正常啊…”结果互相甩锅问题定位像无头苍蝇有链路追踪时看链路图网关(1s) - 订单服务(6s) - 商品服务(0.5s) - 库存服务(0.5s) - 用户服务(2s)一眼看出订单服务花了6秒卡在数据库查询精准定位优化订单服务的SQL语句解决问题响应时间降到2秒二、链路追踪是啥就像快递追踪系统每个包裹有个唯一单号Trace ID经过每个站点都扫码记录Span你能看到包裹的完整路线Trace知道在哪个站点停留多久耗时核心概念Trace一次完整的请求链路比如一次下单Span链路中的每个环节比如调用订单服务Trace ID整个链路的唯一IDSpan ID每个环节的唯一IDParent Span ID父环节ID形成调用树三、Sleuth Zipkin 黄金搭档1. Sleuth侦探Spring Cloud的”侦探”自动给请求打标签Trace ID、Span ID把追踪信息传递给下游服务不存储数据只收集和传递2. Zipkin档案馆存储和展示追踪数据漂亮的Web界面支持多种存储内存、MySQL、Elasticsearch分析工具看链路、查问题工作流程用户请求 - Sleuth生成Trace ID - 经过各个服务 - 每个服务Sleuth记录Span - 数据发送到Zipkin - Zipkin存储展示 - 我们在界面查看四、搭建Zipkin服务端方式1Docker最简单# 拉取Zipkin镜像 docker pull openzipkin/zipkin # 运行用内存存储适合测试 docker run -d \ --name zipkin \ -p 9411:9411 \ openzipkin/zipkin # 或者用MySQL存储生产环境 docker run -d \ --name zipkin \ -p 9411:9411 \ -e STORAGE_TYPEmysql \ -e MYSQL_HOSTlocalhost \ -e MYSQL_PORT3306 \ -e MYSQL_USERroot \ -e MYSQL_PASS123456 \ -e MYSQL_DBzipkin \ openzipkin/zipkin方式2Java直接运行# 下载jar包 curl -sSL https://zipkin.io/quickstart.sh | bash -s # 运行 java -jar zipkin.jar方式3Spring Boot项目集成!-- 新建zipkin-server项目 -- dependency groupIdio.zipkin.java/groupId artifactIdzipkin-server/artifactId /dependency dependency groupIdio.zipkin.java/groupId artifactIdzipkin-autoconfigure-ui/artifactId /dependency访问http://localhost:9411看到Zipkin界面就中了五、给微服务加链路追踪步骤1所有服务加依赖!-- Sleuth链路追踪 -- dependency groupIdorg.springframework.cloud/groupId artifactIdspring-cloud-starter-sleuth/artifactId /dependency !-- Zipkin客户端上报数据 -- dependency groupIdorg.springframework.cloud/groupId artifactIdspring-cloud-starter-zipkin/artifactId /dependency步骤2配置文件spring: application: name: order-service # 服务名很重要Zipkin上显示这个 sleuth: sampler: probability: 1.0 # 采样率1.0表示100%采样生产环境可以设0.1 # 可以自定义一些标签 tags: application: ${spring.application.name} environment: ${spring.profiles.active:dev} zipkin: base-url: http://localhost:9411 # Zipkin服务器地址 sender: type: web # 使用HTTP上报也可以用RabbitMQ、Kafka # 连接配置 connect-timeout: 5000 read-timeout: 10000 # 压缩数据减少网络传输 compression: enabled: true # 本地服务名 service: name: ${spring.application.name} # 定位信息IP locator: discovery: enabled: true步骤3启动服务测试启动Zipkin9411端口启动user-service、order-service访问几次order-service接口打开Zipkin界面http://localhost:9411六、看看追踪效果1. 查看服务依赖图在Zipkin首页点Dependencies依赖关系看到服务之间的调用关系像下面这样gateway (网关) ├── order-service (订单服务) │ ├── user-service (用户服务) │ └── product-service (商品服务) └── product-service (商品服务)2. 查看具体链路点Find Traces查找追踪选择服务名order-service点Run Query运行查询看到一堆追踪记录点一个进去看到的信息Trace ID: 7a1b3c4d5e6f7g8h Duration: 1.234s (总耗时) Services: 3 (涉及3个服务) Timeline (时间轴): ├── [0ms-50ms] gateway: /order/1 │ └── [10ms-45ms] order-service: OrderController.getOrder │ ├── [15ms-30ms] user-service: UserClient.getUserById │ └── [35ms-40ms] product-service: ProductClient.getProduct └── [60ms-80ms] gateway: 返回响应3. 看详细信息点开一个Span能看到基本信息服务名、接口、耗时标签HTTP方法、状态码、URL时间线开始时间、结束时间日志如果有额外日志七、实际项目中的应用场景1慢查询定位RestController RequestMapping(/order) Slf4j public class OrderController { Autowired private OrderService orderService; GetMapping(/slow/{id}) public Order getOrderSlow(PathVariable Long id) { // Sleuth会自动给这个请求加上Trace ID log.info(开始查询订单订单ID{}, id); // 模拟慢查询 simulateSlowQuery(); Order order orderService.getOrderById(id); log.info(订单查询完成{}, order); return order; } private void simulateSlowQuery() { try { // 模拟耗时操作 Thread.sleep(3000); log.warn(查询耗时3秒需要优化); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } } }在Zipkin里能看到order-service有个3秒的Span日志里有”查询耗时3秒需要优化”轻松定位到慢接口场景2异常追踪RestController RequestMapping(/product) Slf4j public class ProductController { GetMapping(/{id}) public Product getProduct(PathVariable Long id) { log.info(查询商品ID{}, id); if (id 999) { // 模拟异常 log.error(商品不存在ID{}, id); throw new RuntimeException(商品不存在); } return productService.getProductById(id); } }在Zipkin里看到失败的请求红色标记点开看到错误信息知道是哪个参数引起的场景3自定义SpanService Slf4j public class OrderService { Autowired private Tracer tracer; // Sleuth的Tracer public Order createOrder(OrderDTO orderDTO) { // 创建自定义Span ScopedSpan dbSpan tracer.startScopedSpan(database-operation); try { dbSpan.tag(operation, insert-order); dbSpan.event(start-db-insert); // 数据库操作 Order order saveOrderToDB(orderDTO); dbSpan.event(end-db-insert); return order; } catch (Exception e) { dbSpan.error(e); // 记录错误 throw e; } finally { dbSpan.end(); // 结束Span } } public ListOrder batchProcessOrders(ListLong orderIds) { // 批量处理每个订单一个Span return orderIds.stream() .map(orderId - { ScopedSpan span tracer.startScopedSpan(process-order- orderId); try { return processSingleOrder(orderId); } finally { span.end(); } }) .collect(Collectors.toList()); } }八、高级功能1. 采样率控制生产环境重要spring: sleuth: sampler: # 按比例采样0.1表示10%的请求被追踪 probability: 0.1 # 或者按速率采样每秒最多10个 # rate: 10为啥不全量采样数据量太大存储成本高对性能有影响约3-5%性能损耗10%采样率足够发现问题2. 集成Logback在日志中显示Trace IDlogback-spring.xml?xml version1.0 encodingUTF-8? configuration include resourceorg/springframework/boot/logging/logback/defaults.xml/ !-- 在日志中显示Trace ID和Span ID -- property nameCONSOLE_LOG_PATTERN value%d{yyyy-MM-dd HH:mm:ss.SSS} [%X{traceId:-},%X{spanId:-}] [%thread] %-5level %logger{36} - %msg%n/ appender nameCONSOLE classch.qos.logback.core.ConsoleAppender encoder pattern${CONSOLE_LOG_PATTERN}/pattern /encoder /appender root levelINFO appender-ref refCONSOLE/ /root /configuration日志输出2024-01-13 10:00:00.123 [7a1b3c4d5e6f7g8h,9i0j1k2l3m4n5o6] [http-nio-8081-exec-1] INFO c.e.OrderController - 开始处理订单好处根据Trace ID搜日志一找一个准看整个请求链路的日志3. 集成ELK日志分析spring: sleuth: # 把Trace ID、Span ID加到MDCMapped Diagnostic Context # Logstash会自动收集 propagation-keys: traceId,spanId # Logstash配置 logging: logstash: enabled: true host: localhost port: 50004. 集成OpenTelemetry新标准!-- OpenTelemetry是新一代追踪标准 -- dependency groupIdio.opentelemetry/groupId artifactIdopentelemetry-api/artifactId /dependency dependency groupIdio.opentelemetry/groupId artifactIdopentelemetry-sdk/artifactId /dependency九、实际项目最佳实践1. 给网关加追踪Configuration public class GatewayTraceConfig { Bean public GlobalFilter traceFilter(Tracer tracer) { return (exchange, chain) - { ServerHttpRequest request exchange.getRequest(); // 获取或生成Trace ID String traceId tracer.currentSpan().context().traceId(); // 添加到响应头方便前端追踪 ServerHttpResponse response exchange.getResponse(); response.getHeaders().add(X-Trace-Id, traceId); return chain.filter(exchange); }; } }2. 数据库调用追踪Configuration public class DataSourceTraceConfig { Bean public DataSource dataSource(DataSource dataSource, Tracer tracer) { // 包装DataSource追踪SQL执行 return new TraceDataSource(dataSource, tracer); } }3. HTTP客户端追踪Configuration public class RestTemplateTraceConfig { Bean public RestTemplate restTemplate(Tracer tracer) { RestTemplate restTemplate new RestTemplate(); // 添加追踪拦截器 restTemplate.getInterceptors().add((request, body, execution) - { // 传递Trace ID String traceId tracer.currentSpan().context().traceId(); request.getHeaders().add(X-B3-TraceId, traceId); return execution.execute(request, body); }); return restTemplate; } }4. 异步调用追踪Service public class AsyncService { Autowired private Tracer tracer; Async public CompletableFutureString asyncMethod() { // 异步方法中需要手动传递Trace Context try (Tracer.SpanInScope ws tracer.withSpan(tracer.currentSpan())) { // 异步操作 return CompletableFuture.completedFuture(done); } } }十、生产环境部署方案方案1Zipkin MySQL小规模# Zipkin用MySQL存储 docker run -d \ --name zipkin \ -p 9411:9411 \ -e STORAGE_TYPEmysql \ -e MYSQL_HOSTmysql-host \ -e MYSQL_TCP_PORT3306 \ -e MYSQL_USERzipkin \ -e MYSQL_PASSzipkin \ -e MYSQL_DBzipkin \ openzipkin/zipkin方案2Zipkin Elasticsearch大规模# 先启动Elasticsearch docker run -d --name elasticsearch -p 9200:9200 elasticsearch:7.17.0 # 启动Zipkin docker run -d \ --name zipkin \ -p 9411:9411 \ -e STORAGE_TYPEelasticsearch \ -e ES_HOSTShttp://elasticsearch:9200 \ openzipkin/zipkin方案3通过消息队列上报解耦spring: zipkin: sender: type: rabbit # 或kafka rabbitmq: host: localhost port: 5672 username: admin password: admin123十一、常见问题解决1. Zipkin看不到数据检查服务连上Zipkin没spring.zipkin.base-url采样率是不是0spring.sleuth.sampler.probability网络通不通Zipkin服务正常不2. Trace ID不连续# 在网关统一生成Trace ID spring: sleuth: # 使用128位Trace ID默认是64位 trace-id128: true # 自定义ID生成器 id-generator: simple: # 使用更随机的ID random: enabled: true3. 性能影响太大# 调整采样率 spring: sleuth: sampler: probability: 0.01 # 1%采样 # 或者用速率限制 sampler: rate: 100 # 每秒最多100个4. 数据保留时间# Zipkin用Elasticsearch可以设置保留策略 # 在Elasticsearch创建生命周期策略 PUT _ilm/policy/zipkin-retention-policy { policy: { phases: { hot: { actions: {} }, delete: { min_age: 30d, actions: { delete: {} } } } } }十二、监控告警1. 慢请求告警Component public class SlowRequestAlert { Autowired private Tracer tracer; EventListener public void handleSpanExported(SpanExportedEvent event) { Span span event.getSpan(); // 检查耗时 long duration span.getDurationMicros() / 1000; // 转成毫秒 if (duration 3000) { // 超过3秒 // 发送告警 sendAlert(慢请求警告, 服务: span.getLocalServiceName() , 接口: span.getName() , 耗时: duration ms , Trace ID: span.getTraceId()); } } private void sendAlert(String title, String message) { // 发送到钉钉、企业微信、邮件等 System.out.println(告警: title - message); } }2. 错误率监控Component public class ErrorRateMonitor { private MapString, AtomicInteger errorCounts new ConcurrentHashMap(); private MapString, AtomicInteger totalCounts new ConcurrentHashMap(); EventListener public void handleSpanExported(SpanExportedEvent event) { Span span event.getSpan(); String serviceName span.getLocalServiceName(); // 统计总数 totalCounts.computeIfAbsent(serviceName, k - new AtomicInteger(0)) .incrementAndGet(); // 检查是否有错误标签 if (span.getTags().containsKey(error)) { errorCounts.computeIfAbsent(serviceName, k - new AtomicInteger(0)) .incrementAndGet(); // 计算错误率 double errorRate (double) errorCounts.get(serviceName).get() / totalCounts.get(serviceName).get(); if (errorRate 0.05) { // 错误率超过5% sendAlert(错误率过高, 服务: serviceName , 错误率: String.format(%.2f, errorRate * 100) %); } } } }十三、今儿个总结学会了啥✅ 链路追踪的重要性不再甩锅✅ Sleuth Zipkin 工作原理✅ 搭建Zipkin服务端✅ 集成到Spring Cloud微服务✅ 查看和分析链路数据✅ 生产环境最佳实践关键点Trace ID贯穿整个请求链路Zipkin存储展示Sleuth收集传递采样率控制性能影响日志集成方便问题定位自定义Span细化追踪