[原始碼分析] OpenTracing之跟蹤Redis
阿新 • • 發佈:2020-09-12
# [原始碼分析] OpenTracing之跟蹤Redis
[toc]
## 0x00 摘要
本文將通過研究OpenTracing的Redis埋點外掛來進一步深入理解OpenTracing。
## 0x01 總體邏輯
### 1.1 相關概念
Tracer是用來管理Span的統籌者,負責建立span和傳播span。它表示從頭到尾的一個請求的呼叫鏈,是一次完整的跟蹤,從請求到伺服器開始,伺服器返回response結束,跟蹤每次rpc呼叫的耗時。它的識別符號是“traceID”。
Span是一個更小的單位,表示一個RPC呼叫過程。一個“trace”包含有許多跨度(span),每個跨度捕獲呼叫鏈內的一個工作單元(系統或服務節點),並由“spanId”標識。 每個跨度具有一個父跨度,並且一個“trace”的所有跨度形成有向無環圖(DAG)。
### 1.2 埋點外掛
對一個應用的跟蹤要關注的無非就是 `客戶端——>web 層——>rpc 服務——>dao 後端儲存、cache 快取、訊息佇列 mq 等這些基礎元件`。OpenTracing 外掛的作用實際上也就是對不同元件進行埋點,以便基於這些元件採集應用的鏈路資料。
不同元件有不同的應用場景和擴充套件點,因此針對不同的框架,需要開發對應的OpenTracing API 外掛用來實現自動埋點。
對於Redis來說各種外掛更是層出不窮,所以OpenTracing 對與 Redis 各種外掛也做了不同處理,比如 Jedis,Redisson,Spring Data Redis 2.x。本文主要是以 Redisson 為例說明,最後用spring-cloud-redis進行補充對照**。
### 1.3 總體邏輯
總體思路是使用代理模式。因為 Redis 並沒有提供像 Servlet 那樣的過濾器或者攔截器,所以 Redis OpenTracing 外掛沒有進行常規埋點,而是通過組合的方式自定義若干代理類,比如 TracingRedissonClient 和 TracingRList .....。
- TracingRedissonClient 代理了 Redis Client。
- TracingRList 代理了Redis List 資料結構。
- 還有其他類代理其他Redis資料結構,比如TracingRMap。
這些代理類將具體完成Tracing 功能。比如代理類 TracingRedissonClient 包含了兩個成員變數:
- private final RedissonClient redissonClient; 是真正的 Redis Client。
- private final TracingRedissonHelper tracingRedissonHelper; 是具體針對 Redission 的Tracing 功能類,比如構建Span。
最後各種代理對 Redis 進行攔截:
- 在執行具體的連線操作之前建立相關的 Span。
- 在操作結束之後結束 Span,並進行上報。
具體可以見下圖
```java
+--------------------------+ +-------------------------+ +-------------------------+
| TracingRedissonClient | | TracingRMap | | TracingRList |
| +----------------------+ | | +---------------------+ | | +---------------------+ |
| | RedissonClient | | | | RMap | | | | RList | | ....
| | | | | | | | | | | |
| | TracingRedissonHelper| | | |TracingRedissonHelper| | | |TracingRedissonHelper| |
| +----------------------+ | | +---------------------+ | | +---------------------+ |
+--------------------------+ +-------------------------+ +-------------------------+
| | |
| | |
| | |
| | |
| v |
| +---------------+-----------------+ |
+-----------> | TracingRedissonHelper | <--------+
| +-----------------------------+ |
| | Tracer +-----+
| +-----------------------------+ | |
+---------------------------------+ |
|
+---------------------------------+ |
| TracingConfiguration | |
| +----------------------------+ | |
| | Tracer <-------+
| +----------------------------| |
+---------------------------------+
```
下圖是為了手機觀看。
![](https://img2020.cnblogs.com/blog/1850883/202009/1850883-20200912110338696-948482231.png)
## 0x02 示例程式碼
我們使用程式碼自帶的test來做說明。我們可以看到有兩個代理類 `TracingRedissonClient` 和 `TracingRList`。
- beforeClass 起到了系統啟動的作用。
- 首先定義了一個tracer(這裡是MockTracer,真正使用時候會用到其他Tracer)。
- 然後使用這個Tracer來構建一個代理類 `TracingRedissonClient`。
- 後續各種測試操作都是使用這個client在進行Redis操作。
- 會通過代理類 `TracingRedissonClient` 得到一個 `org.redisson.api.RList` 以備後續操作。這個 RList 實際是OpenTracing 進行修改的另一個代理類 `TracingRList`。
- 會對這個 `TracingRList` 進行操作 :`list.add("key");`
- 針對 Redisson 的非同步操作,也進行了操作測試。
具體程式碼如下:
```java
public class TracingRedissonTest {
private static final MockTracer tracer = new MockTracer();
private static RedisServer redisServer;
private static RedissonClient client;
@BeforeClass
public static void beforeClass() {
redisServer = RedisServer.builder().setting("bind 127.0.0.1").build();
redisServer.start();
Config config = new Config();
config.useSingleServer().setAddress("redis://127.0.0.1:6379");
client = new TracingRedissonClient(Redisson.create(config),
new TracingConfiguration.Builder(tracer).build());
}
@Test
public void test_list() {