链路追踪:Sleuth
什么是链路追踪

OpenTracing标准语义
OpenTracing的关键概念
Span
:可以被理解为一次方法调用,或者一个程序块的调用,或者一次网络/数据库访问。只要是一个具有完整时间周期的程序访问,都可以被认为是一个span
。Trace
:由Span
组成。References
:表示Span
间的关系。有两种References
:ChildOf(父子)
和FollowsFrom(跟随)
。
单个Trace中Span间的References
[Span A] ←←←(根 span)
|
+------+------+
| |
[Span B] [Span C] ←←←(Span C 是 Span A 的孩子节点,也就是ChildOf)
| |
[Span D] +---+-------+
| |
[Span E] [Span F] >>> [Span G] >>> [Span H]
↑
↑
↑
(Span G 在 Span F 后被调用,也就是FollowsFrom)
基于时间轴的Trace时序图
––|–––––––|–––––––|–––––––|–––––––|–––––––|–––––––|–––––––|–> time
[Span A···················································]
[Span B··············································]
[Span D··········································]
[Span C········································]
[Span E·······] [Span F··] [Span G··] [Span H··]
Spring Cloud Sleuth以及所有实现链路追踪
功能的组件都符合OpenTracing标准语义。
部署Zipkin
> docker run -d -p 9411:9411 --privileged=true openzipkin/zipkin
如果一切正常,那么此时就可以在浏览器中访问它了:http://172.16.185.176:9411/zipkin/。

引入依赖
因为是调用链路,所以至少需要部署两个服务。这里部署三个,其调用关系为如下。
funcun-payment-center
-> funcun-order-center
-> funcun-message-center
funcun-payment-center
的作用是支付金额
。funcun-order-center
的作用是生成订单
。funcun-message-center
的作用是发送短信
它们引用的依赖都是相同的。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-loadbalancer</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>
添加配置
分别为每个服务添加bootstrap.yml
配置。
server:
port: 9001
spring:
application:
# 应用名称
name: funcun-payment-center
cloud:
nacos:
discovery:
# 服务注册地址
server-addr: localhost:8848
zipkin:
base-url: http://172.16.185.176:9411
sender:
type: web # 设置使用 http 的方式传输数据
这三个服务除了端口
和应用名称
不同之外,其他都一样。
funcun-payment-center
PaymentController
类的代码。
package com.funcun.cloud.controller;
import com.funcun.cloud.feign.OrderFeignService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.*;
/**
* 支付Controller
*
*/
@Slf4j
@RequiredArgsConstructor
@RequestMapping("/payment")
@RestController
public class PaymentController {
private final OrderFeignService orderFeignService;
@GetMapping(value = "/pay")
public String pay(@RequestParam(value = "money") Integer money) {
if (money < 0) {
throw new RuntimeException("支付失败,请重新请求!");
}
// 调用创建订单的服务
return orderFeignService.createOrder(money);
}
}
OrderFeignService
类的代码。
package com.funcun.cloud.feign;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
/**
* 调用订单服务
*
*/
@FeignClient(value = "funcun-order-center")
public interface OrderFeignService {
@GetMapping(value = "/order/createOrder")
String createOrder(@RequestParam(value = "money") Integer money);
}
funcun-order-center
OrderController
类的代码。
package com.funcun.cloud.controller;
import com.funcun.cloud.feign.MessageFeignService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
/**
* 订单Controller
*
*/
@Slf4j
@RequiredArgsConstructor
@RequestMapping("/order")
@RestController
public class OrderController {
private final MessageFeignService messageFeignService;
@GetMapping("/createOrder")
public String createOrder(@RequestParam("money") Integer money) {
// 调用库存服务进行库存扣减
String result = messageFeignService.sendMessage(money);
log.info("{}", result);
return "下单成功!";
}
}
MessageFeignService
类的代码。
package com.funcun.cloud.feign;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
/**
* 调用短信服务
*
*/
@FeignClient(value = "funcun-message-center")
public interface MessageFeignService {
@GetMapping(value = "/message/sendMessage/{money}")
String sendMessage(@PathVariable(value = "money") Integer money);
}
funcun-message-center
MessageController
类的代码。
package com.funcun.cloud.controller;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* 短信Controller
*
*/
@Slf4j
@RequestMapping("/message")
@RestController
public class MessageController {
@GetMapping(value = "/sendMessage/{money}")
public String sendMessage(@PathVariable(value = "money") Integer money) {
if (money < 1 || money > 999999) {
throw new RuntimeException("支付金额异常,请重新请求!");
}
return "订单创建成功!";
}
}
因为funcun-message-center
不再调用其他服务,因此它没有OpenFeign
相关的类。
调用结果
启动服务后,就可以通过Postman或其他接口测试工具进行测试了:http://localhost:9001/payment/pay?money=10。
如果接口测试成功,那么就可以运行Zipkin中的RUN QUERY
看到下面的结果。


上面的两张图可以清晰地显示出Spring Cloud Sleuth的调用链路。
感谢支持
更多内容,请移步《超级个体》。