前言
隨著應用愈發複雜,請求的鏈路也愈發複雜,微服務化下,更是使得不同的服務分佈在不同的機器,地域,語言也不盡相同。因此需要藉助工具幫助分析,跟蹤,定位請求中出現的若干問題,以此來保障服務治理,鏈路追蹤也就出現了。
OpenTracing協議
OpenTracing是一套分散式追蹤協議,與平臺,語言、廠商無關的Trace協議,統一介面,使得開發人員能夠方便的新增或更換更換不同的分散式追蹤系統。
同樣作為分散式追蹤協議的還有OpenCensus,以及兩者的合併體OpenTelemetry。
Jaeger介紹
Jaeger[ˈdʒɛgər]是Uber推出的一款開源分散式追蹤系統,相容OpenTracing API,已在Uber大規模使用,且已加入CNCF開源組織(Cloud Native Computing Foundation-雲原生計算基金會)。其主要功能是聚合來自各個異構系統的實時監控資料。
Jager提供了一套完整的追蹤系統包括Jaeger-client、Jaeger-agent、Jaeger-collector、Database和Jaeger-query UI等基本元件。
- Jaeger-client:為不同開發語言實現了符合OpenTracing協議的客戶端。
- Jaeger-agent:一個監聽在UDP埠上接收鏈路資料的網路守護程式,它從應用程式收集,批處理,併傳送給Collector,(也可以沒有這個,client直接上報)。
- Jaeger-collector:負責接收Jaeger-client或Jaeger-agent上報的呼叫鏈路資料,並透過處理管道執行它們,該管道驗證跟蹤、對它們進行索引、執行任何轉換並最終儲存到記憶體或外部儲存系統中,供UI展示。
- Jaeger-query:查詢服務從儲存中檢索跟蹤並呈現 UI 來顯示它們。
Jaeger安裝
在個人使用或者測試上,Jaeger提供了jaegertracing/all-in-one映象,搭建過程十分簡單,資料儲存在記憶體中,但需要注意容器掛了後資料就沒了。
docker run -d -p 6831:6831/udp -p 16686:16686 jaegertracing/all-in-one:latest
建立容器執行後,可以訪問ip:16686檢視Jaeger的儀表皮膚
Jaeger應用
服務設計
簡化大部分服務設計,整個結構上差不多是如下所示,服務層常見金字塔結構,服務上下游明確,以避免服務間的迴圈依賴。
此處建立四個服務以及一個BFF閘道器層,以滿足服務同步呼叫,服務間上下游呼叫,以及服務間事件通訊。
- JaegerDemo.BFF.Host
- JaegerDemo.AService.Host
- JaegerDemo.BService.Host
- JaegerDemo.CService.Host
- JaegerDemo.DService.Host
為這幾個服務設定期望如下
- 執行Get請求時,從Gateway呼叫,請求A服務,在同步請求B和C服務,拿到結果組裝後對外返回。
- 執行Post請求時,從Gateway呼叫,請求A服務,在釋出事件到MQ中,D服務訂閱事件,資料寫入到Sqlite中。
Nuget包引用
- Jaeger,用來上傳資料到Jaeger。
- OpenTracing.Contrib.NetCore,基於OpenTracing.Net的增強,用來採集應用資料。
- MassTransit和MassTransit.RabbitMQ,用來完成事件的釋出訂閱。
<ItemGroup>
<PackageReference Include="OpenTracing" Version="0.12.1" />
<PackageReference Include="Jaeger" Version="1.0.3" />
<PackageReference Include="MassTransit" Version="8.0.8" />
<PackageReference Include="MassTransit.RabbitMQ" Version="8.0.8" />
</ItemGroup>
服務註冊
將服務註冊到容器中,設定上報地址,注意此處上報地址是UDP型別,因此在雲伺服器中開安全組時需要是UDP型別
builder.Services.AddOpenTracing();
builder.Services.AddSingleton<ITracer>(serviceProvider =>
{
var serviceName = serviceProvider.GetRequiredService<IWebHostEnvironment>().ApplicationName;
var loggerFactory = serviceProvider.GetRequiredService<ILoggerFactory>();
var sampler = new ConstSampler(sample: true);
var reporter = new RemoteReporter.Builder()
.WithLoggerFactory(loggerFactory)
.WithSender(new UdpSender("xxx.xxx.xxx.xxx", 6831, 0))
.Build();
var tracer = new Tracer.Builder(serviceName)
.WithLoggerFactory(loggerFactory)
.WithSampler(sampler)
.WithReporter(reporter)
.Build();
GlobalTracer.Register(tracer);
return tracer;
});
此處我在雲伺服器中開放6831的埠,注意是UDP
Http請求
在BFF處發起Http呼叫A服務,以及A服務發起Http呼叫B和C。
[HttpGet]
public async Task<string> GetAsync()
{
using var httpClient = _httpClientFactory.CreateClient();
httpClient.BaseAddress = new Uri("https://localhost:7001");
var aServiceResult = await httpClient.GetStringAsync("/AValue");
return aServiceResult;
}
請求傳送完畢,從Jaeger的儀表皮膚檢視監控資料,能夠看到一個請求的發起時間,所經過的服務數量、所呼叫服務的依賴關係、消耗的時長等資訊。整個請求鏈路也就看到了,B和C的同步請求,A和B,A和C的上下游請求也明瞭。
Jaeger提供了有向圖描述請求鏈路,來方便理清節點間的通訊邊界,整個請求鏈路也便清晰了。
事件驅動
在BFF處發起Http呼叫A服務,以及A服務往RabbitMQ傳送整合事件。
[HttpPost]
public async Task<IActionResult> CreateAsync(string value)
{
var actionName = ControllerContext.ActionDescriptor.DisplayName;
using var scope = _tracer.BuildSpan(actionName).StartActive(finishSpanOnDispose: true);
var span = scope.Span.SetTag(Tags.SpanKind, Tags.SpanKindClient);
var dictionary = new Dictionary<string, string>();
_tracer.Inject(span.Context, BuiltinFormats.TextMap, new TextMapInjectAdapter(dictionary));
// Do something
// ...
// Send integration event
await _publishEndpoint.Publish(new ValueCreatedIntegrationEvent()
{
Value = value,
TrackingKeys = dictionary
});
return Ok();
}
D服務中消費整合事件,並寫入Sqlite庫中
public async Task Consume(ConsumeContext<ValueCreatedIntegrationEvent> context)
{
using var scope = TracingExtension.StartServerSpan(_tracer, context.Message.TrackingKeys, "Value created integration event handler");
var value = context.Message.Value;
Console.WriteLine($"Value:{value}");
await _dbContext.ValueAggregates.AddAsync(new ValueAggregate(value));
await _dbContext.SaveChangesAsync();
}
當請求傳送完畢,事件消費完畢後,可以在Jaeger上看到在事件驅動下的鏈路呼叫過程,以及在呼叫過程中增加的tags和logs,寫入Sqlite的Sql。
在原有鏈路結構上,便又多了一個D服務。
參考
- https://developer.aliyun.com/article/514488
- https://www.cnblogs.com/wucy/p/13642289.html
- https://www.cnblogs.com/catcher1994/p/10662999.html
2022-11-28,望技術有成後能回來看見自己的腳步