第29集SpringCloudAlibaba全网最全讲解之Sleuth和Zipkin | 字数总计: 4.4k | 阅读时长: 21分钟 | 阅读量:
引言 在微服务架构中,一个用户请求往往需要经过多个服务节点的处理,如何追踪请求在分布式系统中的完整调用链路,分析系统性能瓶颈,定位问题根源,是微服务架构面临的重要挑战。SpringCloudAlibaba Sleuth和Zipkin提供了完整的分布式追踪解决方案。
Sleuth负责在微服务中自动生成和传播追踪信息,Zipkin则提供可视化的链路追踪界面,两者结合使用可以实现微服务系统的全链路监控和性能分析。
本文将深入讲解Sleuth和Zipkin的核心概念、配置方式、追踪机制以及实际应用场景,帮助开发者掌握分布式追踪系统的设计与实现。
分布式追踪核心概念 1. 什么是分布式追踪 分布式追踪是一种用于分析和监控分布式系统的方法,通过记录和关联跨多个服务的请求信息,构建完整的请求调用链路。主要解决的问题包括:
请求链路可视化 :清晰展示请求在系统中的完整路径
性能瓶颈分析 :识别系统中的性能热点和瓶颈
问题快速定位 :快速定位分布式系统中的异常和错误
依赖关系分析 :了解服务间的依赖关系和调用频率
2. 分布式追踪架构 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 graph TB A[客户端请求] --> B[服务A] B --> C[服务B] B --> D[服务C] C --> E[服务D] D --> F[数据库] E --> F G[Sleuth] --> B G --> C G --> D G --> E H[Zipkin Server] --> G I[Zipkin UI] --> H
核心组件:
Trace :一次完整的请求调用链路
Span :链路中的每个操作单元
SpanId :单个操作的唯一标识
TraceId :整个链路的唯一标识
Parent Span :父级操作单元
Child Span :子级操作单元
3. Sleuth和Zipkin关系
Sleuth :Spring Cloud的分布式追踪工具,负责生成和传播追踪信息
Zipkin :Twitter开源的分布式追踪系统,提供数据收集、存储和可视化
关系 :Sleuth将追踪数据发送到Zipkin,Zipkin提供Web界面展示追踪结果
Zipkin Server安装与配置 1. 环境准备 系统要求:
JDK 1.8+
内存:2GB+
存储:根据数据量确定
下载Zipkin:
1 2 3 4 5 wget https://search.maven.org/remote_content?g=io.zipkin&a=zipkin-server&v=LATEST&c=exec -O zipkin-server.jar docker run -d -p 9411:9411 openzipkin/zipkin:latest
2. 启动Zipkin Server 方式1:直接启动
1 2 3 4 5 6 7 8 9 10 11 java -jar zipkin-server.jar java -jar zipkin-server.jar \ --STORAGE_TYPE=mysql \ --MYSQL_HOST=localhost \ --MYSQL_TCP_PORT=3306 \ --MYSQL_DB=zipkin \ --MYSQL_USER=root \ --MYSQL_PASS=password
方式2:Docker启动
1 2 3 4 5 6 7 8 9 10 11 12 docker run -d -p 9411:9411 openzipkin/zipkin:latest docker run -d -p 9411:9411 \ -e STORAGE_TYPE=mysql \ -e MYSQL_HOST=mysql \ -e MYSQL_TCP_PORT=3306 \ -e MYSQL_DB=zipkin \ -e MYSQL_USER=root \ -e MYSQL_PASS=password \ openzipkin/zipkin:latest
3. 数据库配置(可选) 创建数据库:
1 2 3 4 5 6 7 8 CREATE DATABASE zipkin DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;USE zipkin; source / path/ to / zipkin/ mysql.sql;
访问Zipkin UI:
Spring Boot集成Sleuth 1. 项目依赖配置 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 <dependencies > <dependency > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-starter-web</artifactId > </dependency > <dependency > <groupId > org.springframework.cloud</groupId > <artifactId > spring-cloud-starter-sleuth</artifactId > </dependency > <dependency > <groupId > org.springframework.cloud</groupId > <artifactId > spring-cloud-sleuth-zipkin</artifactId > </dependency > <dependency > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-starter-actuator</artifactId > </dependency > <dependency > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-starter-webflux</artifactId > </dependency > </dependencies > <dependencyManagement > <dependencies > <dependency > <groupId > org.springframework.cloud</groupId > <artifactId > spring-cloud-dependencies</artifactId > <version > 2022.0.0</version > <type > pom</type > <scope > import</scope > </dependency > </dependencies > </dependencyManagement >
2. 配置文件设置 application.yml:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 server: port: 8080 spring: application: name: sleuth-demo-service sleuth: sampler: probability: 1.0 zipkin: base-url: http://localhost:9411 sender: type: web web: client: enabled: true jdbc: enabled: true redis: enabled: true kafka: enabled: true logging: level: org.springframework.cloud.sleuth: DEBUG brave: DEBUG pattern: console: "%d{yyyy-MM-dd HH:mm:ss} [%X{traceId:-},%X{spanId:-}] %-5level %logger{36} - %msg%n"
3. 启动类配置 1 2 3 4 5 6 7 @SpringBootApplication @EnableDiscoveryClient public class SleuthDemoApplication { public static void main (String[] args) { SpringApplication.run(SleuthDemoApplication.class, args); } }
基础追踪功能 1. HTTP请求追踪 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 @RestController @RequestMapping("/api") @Slf4j public class UserController { @Autowired private UserService userService; @Autowired private OrderService orderService; @GetMapping("/user/{id}") public ResponseEntity<User> getUser (@PathVariable Long id) { log.info("获取用户信息,用户ID: {}" , id); Span span = tracer.nextSpan() .name("get-user-operation" ) .tag("user.id" , String.valueOf(id)) .start(); try (Tracer.SpanInScope ws = tracer.withSpanInScope(span)) { User user = userService.findById(id); List<Order> orders = orderService.getUserOrders(id); user.setOrders(orders); span.tag("user.name" , user.getName()); span.tag("orders.count" , String.valueOf(orders.size())); return ResponseEntity.ok(user); } catch (Exception e) { span.tag("error" , true ); span.tag("error.message" , e.getMessage()); throw e; } finally { span.end(); } } @PostMapping("/user") public ResponseEntity<User> createUser (@RequestBody User user) { log.info("创建用户: {}" , user.getName()); User createdUser = userService.create(user); return ResponseEntity.ok(createdUser); } }
2. 数据库操作追踪 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 @Service @Slf4j public class UserService { @Autowired private UserRepository userRepository; @Autowired private Tracer tracer; public User findById (Long id) { log.info("查询用户,ID: {}" , id); Span span = tracer.nextSpan() .name("database-query" ) .tag("db.operation" , "SELECT" ) .tag("db.table" , "users" ) .tag("db.query.id" , String.valueOf(id)) .start(); try (Tracer.SpanInScope ws = tracer.withSpanInScope(span)) { User user = userRepository.findById(id) .orElseThrow(() -> new UserNotFoundException ("用户不存在" )); span.tag("db.result.found" , "true" ); span.tag("user.name" , user.getName()); return user; } catch (Exception e) { span.tag("error" , true ); span.tag("error.message" , e.getMessage()); throw e; } finally { span.end(); } } public User create (User user) { log.info("创建用户: {}" , user.getName()); Span span = tracer.nextSpan() .name("database-insert" ) .tag("db.operation" , "INSERT" ) .tag("db.table" , "users" ) .start(); try (Tracer.SpanInScope ws = tracer.withSpanInScope(span)) { User savedUser = userRepository.save(user); span.tag("db.result.id" , String.valueOf(savedUser.getId())); return savedUser; } catch (Exception e) { span.tag("error" , true ); span.tag("error.message" , e.getMessage()); throw e; } finally { span.end(); } } }
3. 异步操作追踪 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 @Service @Slf4j public class AsyncService { @Autowired private Tracer tracer; @Async public CompletableFuture<String> processAsync (String data) { log.info("异步处理数据: {}" , data); Span span = tracer.nextSpan() .name("async-processing" ) .tag("async.data" , data) .start(); try (Tracer.SpanInScope ws = tracer.withSpanInScope(span)) { Thread.sleep(1000 ); String result = "processed-" + data; span.tag("async.result" , result); return CompletableFuture.completedFuture(result); } catch (Exception e) { span.tag("error" , true ); span.tag("error.message" , e.getMessage()); throw e; } finally { span.end(); } } }
高级追踪功能 1. 自定义Span 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 @Component @Slf4j public class CustomTracingService { @Autowired private Tracer tracer; public String processWithCustomSpan (String input) { Span span = tracer.nextSpan() .name("custom-processing" ) .tag("input" , input) .tag("service" , "custom-tracing" ) .start(); try (Tracer.SpanInScope ws = tracer.withSpanInScope(span)) { log.info("开始自定义处理: {}" , input); String step1Result = processStep1(input); span.tag("step1.result" , step1Result); String step2Result = processStep2(step1Result); span.tag("step2.result" , step2Result); String finalResult = processStep3(step2Result); span.tag("final.result" , finalResult); log.info("自定义处理完成: {}" , finalResult); return finalResult; } catch (Exception e) { span.tag("error" , true ); span.tag("error.message" , e.getMessage()); log.error("自定义处理失败" , e); throw e; } finally { span.end(); } } private String processStep1 (String input) { return "step1-" + input; } private String processStep2 (String input) { return "step2-" + input; } private String processStep3 (String input) { return "step3-" + input; } }
2. 批量操作追踪 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 @Service @Slf4j public class BatchProcessingService { @Autowired private Tracer tracer; public List<String> processBatch (List<String> items) { log.info("批量处理,项目数量: {}" , items.size()); Span batchSpan = tracer.nextSpan() .name("batch-processing" ) .tag("batch.size" , String.valueOf(items.size())) .start(); try (Tracer.SpanInScope ws = tracer.withSpanInScope(batchSpan)) { List<String> results = new ArrayList <>(); for (int i = 0 ; i < items.size(); i++) { String item = items.get(i); Span itemSpan = tracer.nextSpan() .name("batch-item-processing" ) .tag("item.index" , String.valueOf(i)) .tag("item.value" , item) .start(); try (Tracer.SpanInScope itemWs = tracer.withSpanInScope(itemSpan)) { String result = processItem(item); results.add(result); itemSpan.tag("item.result" , result); } catch (Exception e) { itemSpan.tag("error" , true ); itemSpan.tag("error.message" , e.getMessage()); log.error("处理项目失败: {}" , item, e); } finally { itemSpan.end(); } } batchSpan.tag("batch.results.count" , String.valueOf(results.size())); return results; } finally { batchSpan.end(); } } private String processItem (String item) { return "processed-" + item; } }
3. 错误追踪 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 @RestController @RequestMapping("/api/error") @Slf4j public class ErrorController { @Autowired private Tracer tracer; @GetMapping("/simulate") public ResponseEntity<String> simulateError () { log.info("模拟错误场景" ); Span span = tracer.nextSpan() .name("error-simulation" ) .start(); try (Tracer.SpanInScope ws = tracer.withSpanInScope(span)) { String result = processBusinessLogic(); if (result.contains("error" )) { span.tag("error" , true ); span.tag("error.type" , "business-error" ); span.tag("error.message" , "业务处理失败" ); throw new BusinessException ("业务处理失败" ); } return ResponseEntity.ok(result); } catch (Exception e) { span.tag("error" , true ); span.tag("error.type" , e.getClass().getSimpleName()); span.tag("error.message" , e.getMessage()); log.error("处理失败" , e); throw e; } finally { span.end(); } } private String processBusinessLogic () { return "business-result-error" ; } }
微服务间调用追踪 1. Feign客户端追踪 1 2 3 4 5 6 7 8 9 @FeignClient(name = "order-service", url = "http://localhost:8081") public interface OrderServiceClient { @GetMapping("/api/orders/user/{userId}") List<Order> getUserOrders (@PathVariable("userId") Long userId) ; @PostMapping("/api/orders") Order createOrder (@RequestBody Order order) ; }
配置类:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 @Configuration public class FeignConfig { @Bean public FeignRequestInterceptor feignRequestInterceptor () { return new FeignRequestInterceptor (); } } @Component public class FeignRequestInterceptor implements RequestInterceptor { @Autowired private Tracer tracer; @Override public void apply (RequestTemplate template) { Span currentSpan = tracer.currentSpan(); if (currentSpan != null ) { template.header("X-Trace-Id" , currentSpan.context().traceId()); template.header("X-Span-Id" , currentSpan.context().spanId()); } } }
2. RestTemplate追踪 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 @Service @Slf4j public class ExternalApiService { @Autowired private RestTemplate restTemplate; @Autowired private Tracer tracer; public String callExternalApi (String endpoint) { log.info("调用外部API: {}" , endpoint); Span span = tracer.nextSpan() .name("external-api-call" ) .tag("api.endpoint" , endpoint) .start(); try (Tracer.SpanInScope ws = tracer.withSpanInScope(span)) { HttpHeaders headers = new HttpHeaders (); headers.set("Content-Type" , "application/json" ); Span currentSpan = tracer.currentSpan(); if (currentSpan != null ) { headers.set("X-Trace-Id" , currentSpan.context().traceId()); headers.set("X-Span-Id" , currentSpan.context().spanId()); } HttpEntity<String> entity = new HttpEntity <>(headers); ResponseEntity<String> response = restTemplate.exchange( endpoint, HttpMethod.GET, entity, String.class ); span.tag("api.status" , String.valueOf(response.getStatusCodeValue())); span.tag("api.response.size" , String.valueOf(response.getBody().length())); return response.getBody(); } catch (Exception e) { span.tag("error" , true ); span.tag("error.message" , e.getMessage()); throw e; } finally { span.end(); } } }
3. WebClient追踪 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 @Service @Slf4j public class WebClientService { @Autowired private WebClient webClient; @Autowired private Tracer tracer; public Mono<String> callExternalService (String url) { log.info("使用WebClient调用外部服务: {}" , url); Span span = tracer.nextSpan() .name("webclient-call" ) .tag("service.url" , url) .start(); try (Tracer.SpanInScope ws = tracer.withSpanInScope(span)) { return webClient.get() .uri(url) .header("X-Trace-Id" , span.context().traceId()) .header("X-Span-Id" , span.context().spanId()) .retrieve() .bodyToMono(String.class) .doOnSuccess(result -> { span.tag("response.size" , String.valueOf(result.length())); }) .doOnError(error -> { span.tag("error" , true ); span.tag("error.message" , error.getMessage()); }) .doFinally(signalType -> { span.end(); }); } } }
采样策略配置 1. 概率采样 1 2 3 4 spring: sleuth: sampler: probability: 0.1
2. 自定义采样器 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 @Configuration public class SamplingConfig { @Bean public Sampler customSampler () { return new Sampler () { @Override public boolean isSampled (long traceId) { return traceId % 10 == 0 ; } }; } }
3. 条件采样 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 @Component public class ConditionalSampler implements Sampler { @Override public boolean isSampled (long traceId) { HttpServletRequest request = getCurrentRequest(); if (request != null ) { String path = request.getRequestURI(); return path.startsWith("/api/" ); } return false ; } private HttpServletRequest getCurrentRequest () { try { RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes(); if (requestAttributes instanceof ServletRequestAttributes) { return ((ServletRequestAttributes) requestAttributes).getRequest(); } } catch (Exception e) { } return null ; } }
性能优化 1. 异步发送 1 2 3 4 5 6 7 8 spring: sleuth: zipkin: sender: type: web connect-timeout: 1000 read-timeout: 10000
2. 批量发送 1 2 3 4 5 6 7 8 9 10 11 12 13 @Configuration public class ZipkinConfig { @Bean public Sender zipkinSender () { return OkHttpSender.create("http://localhost:9411/api/v2/spans" ); } @Bean public AsyncReporter<Span> spanReporter () { return AsyncReporter.create(zipkinSender()); } }
3. 本地存储 1 2 3 4 5 6 7 8 @Configuration public class LocalStorageConfig { @Bean public SpanHandler spanHandler () { return new SimpleSpanHandler (); } }
监控与告警 1. 指标监控 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 @Component @Slf4j public class TracingMetrics { private final MeterRegistry meterRegistry; private final Counter traceCounter; private final Timer traceTimer; public TracingMetrics (MeterRegistry meterRegistry) { this .meterRegistry = meterRegistry; this .traceCounter = Counter.builder("tracing.traces.total" ) .description("Total number of traces" ) .register(meterRegistry); this .traceTimer = Timer.builder("tracing.trace.duration" ) .description("Trace duration" ) .register(meterRegistry); } public void recordTrace (String serviceName, String operation, Duration duration) { traceCounter.increment( Tags.of( "service" , serviceName, "operation" , operation ) ); traceTimer.record(duration); } }
2. 健康检查 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 @Component public class ZipkinHealthIndicator implements HealthIndicator { @Autowired private ZipkinProperties zipkinProperties; @Override public Health health () { try { RestTemplate restTemplate = new RestTemplate (); String url = zipkinProperties.getBaseUrl() + "/api/v2/services" ; ResponseEntity<String> response = restTemplate.getForEntity(url, String.class); if (response.getStatusCode().is2xxSuccessful()) { return Health.up() .withDetail("zipkin-server" , "connected" ) .withDetail("url" , url) .build(); } else { return Health.down() .withDetail("zipkin-server" , "unavailable" ) .withDetail("status" , response.getStatusCode()) .build(); } } catch (Exception e) { return Health.down() .withDetail("zipkin-server" , "error" ) .withDetail("error" , e.getMessage()) .build(); } } }
3. 告警配置 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 @Component @Slf4j public class TracingAlertService { @Autowired private MeterRegistry meterRegistry; @EventListener public void handleTraceEvent (TraceEvent event) { if (event.hasError()) { log.warn("检测到追踪异常: {}" , event.getErrorMessage()); sendAlert("追踪异常" , event.getErrorMessage()); } if (event.getDuration() > Duration.ofSeconds(5 )) { log.warn("检测到慢请求: {}ms" , event.getDuration().toMillis()); sendAlert("慢请求告警" , "请求耗时: " + event.getDuration().toMillis() + "ms" ); } } private void sendAlert (String title, String message) { log.info("发送告警: {} - {}" , title, message); } }
常见问题与解决方案 1. 追踪数据丢失 问题描述: 部分请求的追踪数据没有出现在Zipkin中
解决方案:
1 2 3 4 5 6 7 8 9 10 11 12 spring: sleuth: sampler: probability: 1.0 zipkin: base-url: http://localhost:9411 sender: type: web connect-timeout: 1000 read-timeout: 10000
2. 性能影响 问题描述: 启用追踪后系统性能下降
解决方案:
1 2 3 4 5 6 7 8 9 10 11 spring: sleuth: sampler: probability: 0.1 zipkin: sender: type: web
3. 内存泄漏 问题描述: 长时间运行后出现内存泄漏
解决方案:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 @Component public class SpanManager { @Autowired private Tracer tracer; public void processWithSpan (String operation) { Span span = tracer.nextSpan().name(operation).start(); try (Tracer.SpanInScope ws = tracer.withSpanInScope(span)) { doProcess(); } catch (Exception e) { span.tag("error" , true ); span.tag("error.message" , e.getMessage()); throw e; } finally { span.end(); } } }
4. 跨服务追踪中断 问题描述: 微服务间调用的追踪链路不连续
解决方案:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 @Configuration public class TracingPropagationConfig { @Bean public FeignRequestInterceptor feignRequestInterceptor () { return new FeignRequestInterceptor (); } } @Component public class FeignRequestInterceptor implements RequestInterceptor { @Autowired private Tracer tracer; @Override public void apply (RequestTemplate template) { Span currentSpan = tracer.currentSpan(); if (currentSpan != null ) { template.header("X-Trace-Id" , currentSpan.context().traceId()); template.header("X-Span-Id" , currentSpan.context().spanId()); } } }
最佳实践总结 1. 追踪设计原则
合理采样 :根据系统负载调整采样率
关键路径 :重点追踪核心业务路径
性能考虑 :避免过度追踪影响系统性能
数据质量 :确保追踪数据的准确性和完整性
2. 命名规范
服务命名 :使用清晰的服务名称
操作命名 :使用动词+名词的格式
标签命名 :使用小写字母和下划线
错误处理 :统一错误标签的命名规范
3. 监控策略
实时监控 :监控关键指标和异常
趋势分析 :分析性能趋势和瓶颈
告警机制 :建立完善的告警体系
容量规划 :基于追踪数据进行容量规划
总结 SpringCloudAlibaba Sleuth和Zipkin为微服务架构提供了完整的分布式追踪解决方案。通过本文的详细讲解,我们了解了:
核心概念 :分布式追踪的基本原理和架构
安装配置 :Zipkin Server的安装和配置方法
集成使用 :Spring Boot与Sleuth的集成配置
追踪功能 :HTTP请求、数据库操作、异步操作的追踪
高级特性 :自定义Span、批量操作、错误追踪
微服务调用 :Feign、RestTemplate、WebClient的追踪
采样策略 :概率采样、自定义采样、条件采样
性能优化 :异步发送、批量发送、本地存储
监控告警 :指标监控、健康检查、告警配置
问题解决 :常见问题的排查和解决方案
在实际应用中,建议:
根据系统特点选择合适的采样策略
建立完善的监控和告警机制
定期分析追踪数据,优化系统性能
注意追踪对系统性能的影响,合理配置
通过掌握这些知识和技能,开发者可以构建高效、可观测的微服务系统,快速定位和解决分布式系统中的问题。
参考资料
Spring Cloud Sleuth官方文档
Zipkin官方文档
分布式追踪最佳实践
微服务监控与追踪
Spring Cloud Sleuth示例