跳到主要内容Spring Cloud Gateway 集成 SkyWalking 实现分布式追踪 | 极客日志Javajava
Spring Cloud Gateway 集成 SkyWalking 实现分布式追踪
SkyWalking 集成 Spring Cloud Gateway 可实现微服务架构下的分布式链路追踪。通过配置 OAP 服务器与 Agent,无需修改业务代码即可自动采集调用链数据。涵盖基础环境搭建、依赖引入、路由配置及自定义过滤器增强,深入解析上下文传播机制与性能监控指标。结合动态路由管理与故障排查实践,提供从开发到运维的全链路可观测性解决方案,助力定位性能瓶颈与服务依赖问题。
清酒独酌1 浏览 Spring Cloud Gateway 集成 SkyWalking 实现分布式追踪
在现代微服务架构中,Spring Cloud Gateway 作为 API 网关扮演着至关重要的角色。它不仅是外部请求进入系统的统一入口,还承担着路由转发、负载均衡、安全认证、限流熔断等多种职责。然而,随着系统复杂度的提升,一次用户请求可能经过多个服务节点,如何准确追踪请求路径、定位性能瓶颈、分析调用链路成为运维和开发人员面临的重大挑战。
Apache SkyWalking 是一款优秀的应用性能监控(APM)工具,专为微服务、云原生和容器化环境设计。它提供了分布式追踪、服务拓扑图、指标分析、告警等功能,能够帮助我们全面了解系统的运行状态。本文将深入探讨如何在 Spring Cloud Gateway 中集成 SkyWalking,并实现对路由请求的完整追踪。
分布式追踪的重要性
在单体应用时代,所有的业务逻辑都集中在一个进程中,调试和排查问题相对简单。但进入微服务时代后,一个完整的业务流程往往需要多个服务协同完成。例如,用户下单操作可能涉及用户服务、商品服务、订单服务、支付服务等多个微服务组件。
这种架构虽然带来了更好的可扩展性和灵活性,但也引入了新的复杂性:
- 请求路径不透明:无法直观看到一个请求经过了哪些服务
- 性能瓶颈难定位:某个环节响应慢,难以快速定位具体是哪个服务的问题
- 错误传播难追踪:异常在服务间传递时,上下文信息容易丢失
- 依赖关系不清晰:服务间的调用关系随时间变化,缺乏可视化展示
分布式追踪技术正是为了解决这些问题而诞生的。通过为每个请求分配唯一的追踪 ID,并在服务调用过程中传递这个 ID,我们可以将分散在各个服务中的日志和指标关联起来,形成完整的调用链路。
SkyWalking 作为 CNCF 毕业项目,在分布式追踪领域表现出色。它支持自动探针(Agent)方式,无需修改业务代码即可实现追踪功能,这对于网关这类基础设施组件尤为重要。
Spring Cloud Gateway 架构概览
Spring Cloud Gateway 是 Spring Cloud 生态中的官方网关组件,基于 Spring Framework 5、Project Reactor 和 Spring Boot 2.x 构建。它采用响应式编程模型,具有高性能、高并发的特点。
核心概念
- Route(路由):网关的基本构建块,定义了请求匹配规则和目标 URI
- Predicate(断言):用于匹配 HTTP 请求的各种条件,如路径、方法、头信息等
- Filter(过滤器):可以在请求被路由前后对请求和响应进行修改
工作流程
后端服务 -> Spring Cloud Gateway -> 客户端
发送 HTTP 请求 -> 执行 Global Filters -> 匹配 Route Predicates
-> 执行 Route-specific Filters (pre) -> 转发请求到后端服务
-> 返回响应 -> 执行 Route-specific Filters (post)
-> 执行 Global Filters (post) -> 返回最终响应
从上面的序列图可以看出,Spring Cloud Gateway 的处理流程分为几个阶段:全局过滤器预处理、路由匹配、路由特定过滤器预处理、转发到后端服务、接收响应、路由特定过滤器后处理、全局过滤器后处理。
这种分层处理机制为我们集成分布式追踪提供了天然的切入点。我们可以在不同的处理阶段记录追踪信息,确保整个请求链路的完整性。
SkyWalking 核心概念解析
在开始集成之前,我们需要了解 SkyWalking 的一些核心概念:
Trace(追踪)
Trace 表示一次完整的请求链路,从客户端发起请求到最终返回响应的全过程。每个 Trace 都有一个全局唯一的 Trace ID。
Span(跨度)
Span 是 Trace 中的基本单元,表示一个具体的操作或方法调用。每个 Span 包含操作名称、开始时间、持续时间、标签(Tags)等信息。
Segment(片段)
在 SkyWalking 中,Segment 是一个特殊概念,表示单个进程内的 Span 集合。当请求跨越多个服务时,每个服务都会生成自己的 Segment,这些 Segment 通过相同的 Trace ID 关联起来。
Context Propagation(上下文传播)
为了让分布式追踪正常工作,必须在服务调用过程中传递追踪上下文。SkyWalking 支持多种传播协议,包括:
- SW8 Header:SkyWalking 自定义的传播头
- W3C Trace Context:标准化的追踪上下文格式
- B3 Propagation:Zipkin 使用的传播格式
对于 Spring Cloud Gateway 这样的网关组件,正确配置上下文传播至关重要,因为它需要将接收到的追踪上下文传递给下游服务。
环境准备与依赖配置
在开始编码之前,我们需要准备好运行环境和相关依赖。
SkyWalking 后端服务部署
首先需要部署 SkyWalking OAP(Observability Analysis Platform)服务器和 UI 界面。可以通过 Docker 快速启动:
docker run --name skywalking-oap -d -p11800:11800 -p12800:12800 apache/skywalking-oap-server:9.7.0
docker run --name skywalking-ui -d --link skywalking-oap:oap -p8080:8080 apache/skywalking-ui:9.7.0
启动完成后,可以通过浏览器访问 http://localhost:8080 查看 SkyWalking UI 界面。
Spring Cloud Gateway 项目依赖
创建一个新的 Spring Boot 项目,并添加必要的依赖。以下是 pom.xml 文件的关键部分:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.7.0</version>
<relativePath/>
</parent>
<groupId>com.example</groupId>
<artifactId>gateway-skywalking-demo</artifactId>
<version>1.0.0</version>
<properties>
<java.version>11</java.version>
<spring-cloud.version>2021.0.3</spring-cloud.version>
<skywalking.version>9.7.0</skywalking.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<dependency>
<groupId>org.apache.skywalking</groupId>
<artifactId>apm-toolkit-trace</artifactId>
<version>${skywalking.version}</version>
</dependency>
<dependency>
<groupId>org.apache.skywalking</groupId>
<artifactId>apm-toolkit-logback-1.x</artifactId>
<version>${skywalking.version}</version>
</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>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
SkyWalking Agent 配置
SkyWalking 提供了 Java Agent 方式进行自动探针,这是最简单且推荐的方式。下载对应版本的 SkyWalking Agent:
wget https://archive.apache.org/dist/skywalking/9.7.0/apache-skywalking-apm-9.7.0.tar.gz
tar -xzf apache-skywalking-apm-9.7.0.tar.gz
解压后,在 agent/config/ 目录下找到 agent.config 文件,主要配置项包括:
# 服务名称,会在 SkyWalking UI 中显示
agent.service_name=${SW_AGENT_NAME:gateway-service}
# OAP 服务器地址
collector.backend_service=${SW_AGENT_COLLECTOR_BACKEND_SERVICES:localhost:11800}
# 日志级别
logging.level=${SW_LOGGING_LEVEL:INFO}
# 是否启用插件
plugin.springmvc.collect_http_params=false
plugin.springwebflux.collect_http_params=false
java -javaagent:/path/to/skywalking-agent/skywalking-agent.jar \
-Dskywalking.agent.service_name=gateway-service \
-jar gateway-skywalking-demo.jar
基础路由配置与自动追踪
现在让我们创建一个基础的 Spring Cloud Gateway 应用,并验证 SkyWalking 的自动追踪功能。
应用主类
@SpringBootApplication
public class GatewayApplication {
public static void main(String[] args) {
SpringApplication.run(GatewayApplication.class, args);
}
}
路由配置
创建 application.yml 文件,配置基本的路由规则:
server:
port: 8081
spring:
application:
name: gateway-service
cloud:
gateway:
routes:
- id: user-service
uri: http://localhost:8082
predicates:
- Path=/api/user/**
- id: order-service
uri: http://localhost:8083
predicates:
- Path=/api/order/**
default-filters:
- DedupeResponseHeader=Access-Control-Allow-Credentials Access-Control-Allow-Origin
logging:
level:
org.springframework.cloud.gateway: DEBUG
测试后端服务
为了验证追踪效果,我们需要创建简单的后端服务。这里以用户服务为例:
@RestController
@RequestMapping("/api/user")
public class UserController {
@GetMapping("/{id}")
public Mono<Map<String, Object>> getUser(@PathVariable String id) {
Map<String, Object> user = new HashMap<>();
user.put("id", id);
user.put("name", "User " + id);
user.put("email", "user" + id + "@example.com");
return Mono.just(user);
}
@PostMapping
public Mono<Map<String, Object>> createUser(@RequestBody Map<String, Object> user) {
user.put("id", "new-user-id");
return Mono.just(user);
}
}
@RestController
@RequestMapping("/api/order")
public class OrderController {
@GetMapping("/{id}")
public Mono<Map<String, Object>> getOrder(@PathVariable String id) {
Map<String, Object> order = new HashMap<>();
order.put("id", id);
order.put("userId", "user-123");
order.put("amount", 99.99);
order.put("status", "PAID");
return Mono.just(order);
}
}
启动验证
- 启动 SkyWalking OAP 和 UI
- 启动 User Service(端口 8082)
- 启动 Order Service(端口 8083)
- 启动 Gateway Service(端口 8081),使用 SkyWalking Agent
curl http://localhost:8081/api/user/123
curl http://localhost:8081/api/order/456
在 SkyWalking UI 中,你应该能够看到完整的调用链路,包含 Gateway Service → User Service 或 Gateway Service → Order Service 的调用关系。
HTTP Request -> Forward Request -> Response -> HTTP Response
Client -> Gateway Service -> User Service -> Client
自定义过滤器中的追踪增强
虽然 SkyWalking Agent 能够自动追踪大部分场景,但在某些情况下我们需要在自定义过滤器中添加额外的追踪信息。这有助于更好地理解网关内部的处理逻辑。
全局过滤器中的追踪
@Component
@Order(-1)
public class TracingGlobalFilter implements GlobalFilter {
private static final Logger logger = LoggerFactory.getLogger(TracingGlobalFilter.class);
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
String traceId = TraceContext.traceId();
String segmentId = TraceContext.segmentId();
ServerHttpRequest request = exchange.getRequest();
String path = request.getPath().pathWithinApplication().value();
String method = request.getMethodValue();
logger.info("Processing request: {} {}, TraceId: {}, SegmentId: {}", method, path, traceId, segmentId);
AbstractSpan span = SkywalkingContextManager.getSpan();
if (span != null) {
span.tag("gateway.route.path", path);
span.tag("gateway.request.method", method);
span.tag("gateway.client.ip", getClientIpAddress(request));
}
return chain.filter(exchange);
}
private String getClientIpAddress(ServerHttpRequest request) {
HttpHeaders headers = request.getHeaders();
String xForwardedFor = headers.getFirst("X-Forwarded-For");
if (xForwardedFor != null && !xForwardedFor.isEmpty()) {
return xForwardedFor.split(",")[0].trim();
}
return request.getRemoteAddress() != null ? request.getRemoteAddress().getAddress().getHostAddress() : "unknown";
}
}
路由特定过滤器中的追踪
@Component
public class AuthFilter implements GatewayFilter {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
ServerHttpRequest request = exchange.getRequest();
String authHeader = request.getHeaders().getFirst("Authorization");
AbstractSpan span = SkywalkingContextManager.getSpan();
if (span != null) {
span.tag("gateway.auth.header.present", String.valueOf(authHeader != null));
}
if (authHeader == null || !authHeader.startsWith("Bearer ")) {
ServerHttpResponse response = exchange.getResponse();
response.setStatusCode(HttpStatus.UNAUTHORIZED);
response.getHeaders().add("WWW-Authenticate", "Bearer realm=\"api\"");
return response.setComplete();
}
String token = authHeader.substring(7);
boolean valid = validateToken(token);
if (span != null) {
span.tag("gateway.auth.token.valid", String.valueOf(valid));
}
if (!valid) {
ServerHttpResponse response = exchange.getResponse();
response.setStatusCode(HttpStatus.FORBIDDEN);
return response.setComplete();
}
return chain.filter(exchange);
}
private boolean validateToken(String token) {
return token.length() > 10;
}
}
spring:
cloud:
gateway:
routes:
- id: user-service
uri: http://localhost:8082
predicates:
- Path=/api/user/**
filters:
- name: AuthFilter
异常处理中的追踪
在网关中处理异常时,我们也应该记录相关的追踪信息:
@RestControllerAdvice
public class GlobalExceptionHandler {
private static final Logger logger = LoggerFactory.getLogger(GlobalExceptionHandler.class);
@ExceptionHandler(Exception.class)
public ResponseEntity<Map<String, Object>> handleException(Exception ex, ServerWebExchange exchange) {
String traceId = TraceContext.traceId();
logger.error("Exception occurred, TraceId: {}", traceId, ex);
AbstractSpan span = SkywalkingContextManager.getSpan();
if (span != null) {
span.log(Instant.now(), Map.of(
"event", "error",
"error.kind", ex.getClass().getSimpleName(),
"message", ex.getMessage(),
"stack", ExceptionUtils.getStackTrace(ex)
));
span.errorOccurred();
}
Map<String, Object> errorResponse = new HashMap<>();
errorResponse.put("timestamp", Instant.now().toString());
errorResponse.put("traceId", traceId);
errorResponse.put("error", ex.getMessage());
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(errorResponse);
}
}
上下文传播的深入理解
在微服务架构中,正确的上下文传播是分布式追踪的关键。Spring Cloud Gateway 作为网关,必须确保接收到的追踪上下文能够正确传递给下游服务。
默认传播行为
SkyWalking Agent 默认会自动处理常见的 HTTP 客户端库的上下文传播,包括:
- WebClient(Spring WebFlux)
- RestTemplate(Spring MVC)
- Feign Client
- OkHttp
- Apache HttpClient
对于 Spring Cloud Gateway,它内部使用 WebClient 进行 HTTP 请求转发,因此 SkyWalking 能够自动注入和提取追踪头信息。
自定义传播头
在某些场景下,我们可能需要自定义传播头或者处理特殊的传播需求。可以通过实现 ContextCarrierCustomizer 接口:
@Component
public class CustomContextCarrierCustomizer implements ContextCarrierCustomizer {
@Override
public void customize(ContextCarrier carrier) {
carrier.baggageItems().put("tenant-id", getCurrentTenantId());
carrier.baggageItems().put("user-role", getCurrentUserRole());
}
private String getCurrentTenantId() {
return "default-tenant";
}
private String getCurrentUserRole() {
return "anonymous";
}
}
跨协议传播
如果网关需要与其他协议的服务通信(如 gRPC、Dubbo 等),SkyWalking 也提供了相应的插件支持。确保在 Agent 配置中启用了相应的插件:
# 启用 gRPC 插件
plugin.grpc.enabled=true
# 启用 Dubbo 插件
plugin.dubbo.enabled=true
传播头格式选择
SkyWalking 支持多种传播头格式,可以通过配置选择:
# 使用 W3C Trace Context 格式(推荐)
propagation.format=w3c
# 或者使用 SkyWalking 自定义格式
# propagation.format=sw8
W3C Trace Context 是标准化的格式,具有更好的兼容性,建议在新项目中使用。
OrderService -> UserService -> Gateway -> Client
(traceparent: 00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01)
HTTP Request (with traceparent header) -> Extract trace context
Forward Request (with traceparent header) -> Internal Call (with trace context)
Response -> Response -> Response
性能监控与指标分析
除了分布式追踪,SkyWalking 还提供了丰富的性能监控和指标分析功能。让我们看看如何利用这些功能来监控 Spring Cloud Gateway 的性能。
关键性能指标
- 吞吐量(Throughput):每秒处理的请求数
- 响应时间(Response Time):P99、P95、平均响应时间
- 成功率(Success Rate):成功请求占总请求的比例
- JVM 指标:内存使用、GC 次数、线程数等
自定义业务指标
我们还可以通过 SkyWalking 的 Meter API 添加自定义业务指标:
@Component
public class BusinessMetricsCollector {
private final MeterRegistry meterRegistry;
public BusinessMetricsCollector(MeterRegistry meterRegistry) {
this.meterRegistry = meterRegistry;
}
public void recordApiCall(String apiName, long durationMs, boolean success) {
Counter.builder("gateway.api.calls")
.tag("api", apiName)
.tag("success", String.valueOf(success))
.register(meterRegistry)
.increment();
Timer.builder("gateway.api.response.time")
.tag("api", apiName)
.register(meterRegistry)
.record(durationMs, TimeUnit.MILLISECONDS);
}
public void recordRateLimiting(String routeId) {
Counter.builder("gateway.rate.limited.requests")
.tag("route", routeId)
.register(meterRegistry)
.increment();
}
}
@Component
@Order(0)
public class MetricsGlobalFilter implements GlobalFilter {
private final BusinessMetricsCollector metricsCollector;
public MetricsGlobalFilter(BusinessMetricsCollector metricsCollector) {
this.metricsCollector = metricsCollector;
}
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
long startTime = System.currentTimeMillis();
String path = exchange.getRequest().getPath().pathWithinApplication().value();
return chain.filter(exchange).doOnTerminate(() -> {
long duration = System.currentTimeMillis() - startTime;
boolean success = exchange.getResponse().getStatusCode() != null
&& exchange.getResponse().getStatusCode().is2xxSuccessful();
metricsCollector.recordApiCall(path, duration, success);
});
}
}
告警配置
SkyWalking 支持基于指标的告警功能。可以在 alarm-settings.yml 中配置告警规则:
rules:
gateway_response_time_rule:
metrics-name: service_resp_time
op: ">"
threshold: 1000
period: 10
count: 3
silence-period: 5
message: Gateway response time is too high
gateway_error_rate_rule:
metrics-name: service_sla
op: "<"
threshold: 95
period: 10
count: 2
silence-period: 5
message: Gateway error rate is too high
高级场景:动态路由与追踪
在实际生产环境中,Spring Cloud Gateway 经常需要支持动态路由配置。让我们看看在这种场景下如何保持追踪的一致性。
动态路由配置
@Service
public class DynamicRouteService {
private final RouteDefinitionWriter routeDefinitionWriter;
private final ApplicationEventPublisher eventPublisher;
public DynamicRouteService(RouteDefinitionWriter routeDefinitionWriter, ApplicationEventPublisher eventPublisher) {
this.routeDefinitionWriter = routeDefinitionWriter;
this.eventPublisher = eventPublisher;
}
public void addRoute(RouteDefinition routeDefinition) {
routeDefinitionWriter.save(Mono.just(routeDefinition)).subscribe();
eventPublisher.publishEvent(new RefreshRoutesEvent(this));
AbstractSpan span = SkywalkingContextManager.getSpan();
if (span != null) {
span.log(Instant.now(), Map.of(
"event", "route.added",
"route.id", routeDefinition.getId(),
"route.uri", routeDefinition.getUri().toString()
));
}
}
public void removeRoute(String routeId) {
eventPublisher.publishEvent(new RefreshRoutesEvent(this));
AbstractSpan span = SkywalkingContextManager.getSpan();
if (span != null) {
span.log(Instant.now(), Map.of(
"event", "route.removed",
"route.id", routeId
));
}
}
}
路由变更的追踪
@RestController
@RequestMapping("/admin/routes")
public class RouteAdminController {
private final DynamicRouteService dynamicRouteService;
public RouteAdminController(DynamicRouteService dynamicRouteService) {
this.dynamicRouteService = dynamicRouteService;
}
@PostMapping
public ResponseEntity<String> addRoute(@RequestBody RouteDefinition routeDefinition) {
try {
dynamicRouteService.addRoute(routeDefinition);
return ResponseEntity.ok("Route added successfully");
} catch (Exception e) {
AbstractSpan span = SkywalkingContextManager.getSpan();
if (span != null) {
span.log(Instant.now(), Map.of(
"event", "route.add.failed",
"error", e.getMessage()
));
span.errorOccurred();
}
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body("Failed to add route: " + e.getMessage());
}
}
@DeleteMapping("/{routeId}")
public ResponseEntity<String> removeRoute(@PathVariable String routeId) {
dynamicRouteService.removeRoute(routeId);
return ResponseEntity.ok("Route removed successfully");
}
}
动态路由的监控
@Component
public class RouteMetricsCollector {
private final MeterRegistry meterRegistry;
private final AtomicInteger activeRoutesCount;
public RouteMetricsCollector(MeterRegistry meterRegistry) {
this.meterRegistry = meterRegistry;
this.activeRoutesCount = Gauge.builder("gateway.active.routes.count")
.register(meterRegistry, new AtomicInteger(0));
}
public void onRouteAdded(String routeId) {
activeRoutesCount.incrementAndGet();
Counter.builder("gateway.routes.added")
.tag("route.id", routeId)
.register(meterRegistry)
.increment();
}
public void onRouteRemoved(String routeId) {
activeRoutesCount.decrementAndGet();
Counter.builder("gateway.routes.removed")
.tag("route.id", routeId)
.register(meterRegistry)
.increment();
}
}
故障排查与最佳实践
在实际使用过程中,可能会遇到各种问题。以下是一些常见问题的排查方法和最佳实践。
常见问题排查
1. 追踪数据未显示
- SkyWalking Agent 未正确加载
- OAP 服务器地址配置错误
- 网络连接问题
java -javaagent:/path/to/agent/skywalking-agent.jar -jar app.jar
grep "SkyWalking Agent" application.log
telnet localhost 11800
2. 调用链路断裂
- 上下文传播头未正确传递
- 自定义 HTTP 客户端未被 Agent 拦截
- 异步处理导致上下文丢失
CompletableFuture.supplyAsync(() -> {
try (var ignored = SkywalkingContextManager.capture().restore()) {
return businessLogic();
}
});
3. 性能影响过大
# 降低采样率(默认是 -1,表示全量采集)
agent.sample_n_per_3_secs=1
# 禁用不需要的插件
plugin.mongodb.enabled=false
plugin.redis.enabled=false
最佳实践
1. 合理命名服务
agent.service_name=${SW_AGENT_NAME:api-gateway-prod}
2. 添加业务标签
AbstractSpan span = SkywalkingContextManager.getSpan();
if (span != null) {
span.tag("business.order.id", orderId);
span.tag("business.user.id", userId);
}
3. 监控关键路径
重点关注核心业务路径的性能指标,设置合理的告警阈值。
4. 定期清理数据
storage:
selector: ${SW_STORAGE:h2}
h2:
driver: ${SW_STORAGE_H2_DRIVER:org.h2.jdbcx.JdbcDataSource}
url: ${SW_STORAGE_H2_URL:jdbc:h2:mem:skywalking-oap-db}
user: ${SW_STORAGE_H2_USER:sa}
password: ${SW_STORAGE_H2_PASSWORD:}
metadata_query_max_size: ${SW_STORAGE_H2_METADATA_QUERY_MAX_SIZE:5000}
max_size_of_array_column: ${SW_STORAGE_H2_MAX_SIZE_OF_ARRAY_COLUMN:2000}
num_of_searchable_values_per_tag: ${SW_STORAGE_H2_NUM_OF_SEARCHABLE_VALUES_PER_TAG:2}
ttl: ${SW_CORE_TTL:7}
与其他监控工具的集成
虽然 SkyWalking 功能强大,但在某些场景下可能需要与其他监控工具集成。
与 Prometheus 集成
SkyWalking 支持将指标导出到 Prometheus:
# 启用 Prometheus exporter
metrics.exporter.prometheus.enabled=true
metrics.exporter.prometheus.port=9090
然后可以在 Prometheus 中配置抓取目标:
scrape_configs:
- job_name: 'skywalking'
static_configs:
- targets: ['localhost:9090']
与 Grafana 集成
使用 Grafana 可以创建更丰富的仪表板。SkyWalking 官方提供了 Grafana 仪表板模板,可以从 Grafana 官方网站搜索 "SkyWalking" 获取。
日志集成
将 SkyWalking 的 Trace ID 注入到日志中,便于日志关联:
<configuration>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder class="ch.qos.logback.core.encoder.LayoutWrappingEncoder">
<layout class="org.apache.skywalking.apm.toolkit.log.logback.v1.x.TraceIdPatternLogbackLayout">
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%tid] [%thread] %-5level %logger{36} -%msg%n</pattern>
</layout>
</encoder>
</appender>
<root level="INFO">
<appender-ref ref="STDOUT"/>
</root>
</configuration>
这样日志中就会包含 Trace ID,格式如:2023-10-01 12:00:00.000 [TID: 4bf92f3577b34da6a3ce929d0e0e4736.0.15847361234560001] [main] INFO ...
实际案例分析
让我们通过一个实际案例来展示 SkyWalking 在 Spring Cloud Gateway 中的应用价值。
场景描述
- API Gateway:Spring Cloud Gateway,处理所有外部请求
- User Service:用户服务,处理用户相关操作
- Product Service:商品服务,处理商品相关操作
- Order Service:订单服务,处理订单相关操作
- Payment Service:支付服务,处理支付相关操作
用户下单流程:Client → Gateway → Order Service → User Service + Product Service → Payment Service
问题发现
某天运营反馈用户下单成功率下降,需要快速定位问题。
排查过程
- 查看服务拓扑图:在 SkyWalking UI 中查看服务拓扑,发现 Order Service 到 Payment Service 的调用出现大量错误。
- 分析调用链路:选择一个失败的 Trace,查看详细调用链路。
- 查看指标趋势:发现 Payment Service 的响应时间在过去 1 小时内急剧上升,从平均 200ms 上升到 30s。
- 检查日志:通过 Trace ID 关联日志,发现 Payment Service 连接第三方支付网关超时。
解决方案
- 临时方案:增加 Payment Service 的超时时间
- 长期方案:优化第三方支付网关的连接池配置,添加熔断机制
预防措施
- 为 Payment Service 添加专门的告警规则
- 在网关层添加针对支付接口的限流策略
- 定期进行压力测试,监控关键路径性能
总结与展望
通过本文的详细介绍,我们深入了解了如何在 Spring Cloud Gateway 中集成 SkyWalking 实现分布式追踪。从基础配置到高级场景,从自动追踪到手动埋点,从性能监控到故障排查,SkyWalking 为我们提供了全方位的可观测性解决方案。
核心收获
- 无缝集成:SkyWalking Agent 能够自动追踪 Spring Cloud Gateway 的路由请求,无需修改大量业务代码
- 完整链路:通过正确的上下文传播,实现了从客户端到后端服务的完整调用链路追踪
- 深度监控:不仅能够追踪请求路径,还能监控性能指标、设置告警、分析依赖关系
- 灵活扩展:支持自定义标签、业务指标、异常日志等,满足各种监控需求
未来展望
随着云原生技术的发展,可观测性变得越来越重要。SkyWalking 也在不断演进,未来的方向包括:
- eBPF 支持:通过 eBPF 技术实现更底层的网络监控
- OpenTelemetry 兼容:更好地与 OpenTelemetry 生态集成
- AI 辅助分析:利用机器学习自动发现异常模式和根因分析
- 多语言支持:继续完善对各种编程语言的支持
对于 Spring Cloud Gateway 用户来说,合理使用 SkyWalking 不仅能够提升系统的可观测性,还能显著提高故障排查效率,保障业务稳定性。
如果你正在构建基于微服务的系统架构,强烈建议将 SkyWalking 纳入你的技术栈。它将成为你运维和开发过程中的得力助手,让你对系统的运行状态了如指掌。
记住,在复杂的分布式系统中,"看不见"往往比"有问题"更可怕。有了 SkyWalking 这样的工具,我们终于可以让微服务架构变得透明可见!
相关免费在线工具
- Keycode 信息
查找任何按下的键的javascript键代码、代码、位置和修饰符。 在线工具,Keycode 信息在线工具,online
- Escape 与 Native 编解码
JavaScript 字符串转义/反转义;Java 风格 \uXXXX(Native2Ascii)编码与解码。 在线工具,Escape 与 Native 编解码在线工具,online
- JavaScript / HTML 格式化
使用 Prettier 在浏览器内格式化 JavaScript 或 HTML 片段。 在线工具,JavaScript / HTML 格式化在线工具,online
- JavaScript 压缩与混淆
Terser 压缩、变量名混淆,或 javascript-obfuscator 高强度混淆(体积会增大)。 在线工具,JavaScript 压缩与混淆在线工具,online
- Base64 字符串编码/解码
将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online
- Base64 文件转换器
将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online