前言
在日常使用ASP.NET Core的開發(fā)或?qū)W習(xí)中,如果有需要使用鏈路跟蹤系統(tǒng),大多數(shù)情況下會(huì)優(yōu)先選擇SkyAPM。我們之前也說過SkyAPM設(shè)計(jì)確實(shí)比較優(yōu)秀,巧妙的利用DiagnosticSource診斷跟蹤日志,可以做到對(duì)項(xiàng)目無入侵方式的集成。其實(shí)還有一款比較優(yōu)秀的鏈路跟蹤系統(tǒng),也可以支持ASP.NET Core,叫Zipkin。它相對(duì)于SkyWalking來說相對(duì)輕量級(jí),使用相對(duì)來說比較偏原生的方式,而且支持Http的形式查詢和提交鏈路數(shù)據(jù)。因?yàn)槲覀兛偸窍M軗碛卸嘁环N的解決方案方便對(duì)比和參考,所以接下來我們就來學(xué)習(xí)一下關(guān)于Zipkin的使用方式。
Zipkin簡(jiǎn)介
Zipkin是由Twitter開源的一款基于Java語言開發(fā)的分布式實(shí)時(shí)數(shù)據(jù)追蹤系統(tǒng)(Distributed Tracking System),其主要功能是采集來自各個(gè)系統(tǒng)的實(shí)時(shí)監(jiān)控?cái)?shù)據(jù)。該系統(tǒng)讓開發(fā)者可通過一個(gè) Web 前端輕松的收集和分析數(shù)據(jù),例如用戶每次請(qǐng)求服務(wù)的處理時(shí)間等,可方便的監(jiān)測(cè)系統(tǒng)中存在的瓶頸。它大致可以分為三個(gè)核心概念
- 首先是上報(bào)端,它主要通過代碼的形式集成到程序中,用于上報(bào)Trace數(shù)據(jù)到Collector端。
- Collector負(fù)責(zé)接收客戶端發(fā)送過來的數(shù)據(jù),保存到內(nèi)存或外部存儲(chǔ)系統(tǒng)中,供UI展示。
- 存儲(chǔ)端可以是基于zipkin內(nèi)存完全不依賴外部存儲(chǔ)的In-Memory形式或依賴外部存儲(chǔ)系統(tǒng)的形式,一般采用外部存儲(chǔ)系統(tǒng)存儲(chǔ)鏈路數(shù)據(jù),畢竟內(nèi)存有限。它可支持的存儲(chǔ)數(shù)據(jù)庫(kù)有MySQL、Cassandra、Elasticsearch。
- UI負(fù)責(zé)展示采集的鏈路數(shù)據(jù),及系統(tǒng)之間的依賴關(guān)系。
相對(duì)來說還是比較清晰的,如果用一張圖表示整體架構(gòu)的話,大致如下圖所示(圖片來源于網(wǎng)絡(luò))
在學(xué)習(xí)鏈路跟蹤的過程中會(huì)設(shè)計(jì)到相關(guān)概念,我們接下來介紹鏈路跟蹤幾個(gè)相關(guān)的概念
- TranceId,一般一次全局的請(qǐng)求會(huì)有一個(gè)唯一的TraceId,用于代表一次唯一的請(qǐng)求。比如我請(qǐng)求了訂單管理系統(tǒng),而訂單管理系統(tǒng)內(nèi)部還調(diào)用了商品管理系統(tǒng),而商品管理系統(tǒng)還調(diào)用了緩存系統(tǒng)或數(shù)據(jù)庫(kù)系統(tǒng)。但是對(duì)全局或外部來說這是一次請(qǐng)求,所以會(huì)有唯一的一個(gè)TraceId。
- SpanId,雖然全局的來說是一次大的請(qǐng)求,但是在這個(gè)鏈路中內(nèi)部間還會(huì)發(fā)起別的請(qǐng)求,這種內(nèi)部間的每次請(qǐng)求會(huì)生成一個(gè)SpanId。
- 如果將整條鏈路串聯(lián)起來的話,我們需要記錄全局的TraceId,代表當(dāng)前節(jié)點(diǎn)的SpanId和發(fā)起對(duì)當(dāng)前節(jié)點(diǎn)調(diào)用的的父級(jí)ParentId。
- 然后基于鏈路跟蹤的核心概念,然后介紹一下Zipkin衍生出來了幾個(gè)相關(guān)概念
- cs:Clent Sent 客戶端發(fā)起請(qǐng)求的時(shí)間,比如 dubbo 調(diào)用端開始執(zhí)行遠(yuǎn)程調(diào)用之前。
- cr:Client Receive 客戶端收到處理完請(qǐng)求的時(shí)間。
- ss:Server Receive 服務(wù)端處理完邏輯的時(shí)間。
sr - cs = 請(qǐng)求在網(wǎng)絡(luò)上的耗時(shí)
ss - sr = 服務(wù)端處理請(qǐng)求的耗時(shí)
cr - ss = 回應(yīng)在網(wǎng)絡(luò)上的耗時(shí)
cr - cs = 一次調(diào)用的整體耗時(shí)
關(guān)于zipkin概念相關(guān)的就介紹這么多,接下來我們介紹如何部署Zipkin。
部署ZipKin
關(guān)于Zipkin常用的部署方式大概有兩種,一種是通過下載安裝JDK,然后運(yùn)行zipkin.jar的方式,另一種是基于Docker的方式。為了方便我采用的是基于Docker的方式部署,因?yàn)椴捎迷姆绞饺ゲ渴疬€需要安裝JDK,而且操作相對(duì)比較麻煩。咱們上面說過,雖然Zipkin可以將鏈路數(shù)據(jù)存放到內(nèi)存中,但是這種操作方式并不實(shí)用,實(shí)際使用過程中多采用ElasticSearch存儲(chǔ)鏈路數(shù)據(jù)。所以部署的時(shí)候需要依賴Zipkin和ElasticSearch,對(duì)于這種部署形式采用docker-compose的方式就再合適不過了,大家可以在Zipkin官方Github中找到docker的部署方式,地址是https://github.com/openzipkin/zipkin/tree/master/docker,官方使用的方式相對(duì)比較復(fù)雜,下載下來docker-compose相關(guān)文件之后我簡(jiǎn)化了它的使用方式,最終修改如下
version: "3.6"
services:
elasticsearch:
# 我使用的是7.5.0版本
image: elasticsearch:7.5.0
container_name: elasticsearch
restart: always
#暴露es端口
ports:
- 9200:9200
environment:
- discovery.type=single-node
- bootstrap.memory_lock=true
#es有內(nèi)存要求
- "ES_JAVA_OPTS=-Xms512m -Xmx512m"
ulimits:
memlock:
soft: -1
hard: -1
networks:
default:
aliases:
- elasticsearch
zipkin:
image: openzipkin/zipkin
container_name: zipkin
restart: always
networks:
default:
aliases:
- zipkin
environment:
#存儲(chǔ)類型為es
- STORAGE_TYPE=elasticsearch
#es地址
- ES_HOSTS=elasticsearch:9200
ports:
- 9411:9411
#依賴es所以在es啟動(dòng)完成后在啟動(dòng)zipkin
depends_on:
- elasticsearch
通過docker-compose運(yùn)行編輯后的yaml文件,一條指令就可以運(yùn)行起來
PackageReference Include="zipkin4net" Version="1.5.0" />
PackageReference Include="zipkin4net.middleware.aspnetcore" Version="1.5.0" />
其中-f是指定文件名稱,如果是docker-compose.yml則可以直接忽略文件名稱,當(dāng)shell中出現(xiàn)如下界面
并且在瀏覽器中輸入http://localhost:9411/zipkin/出現(xiàn)如圖所示,則說明Zikpin啟動(dòng)成功
整合ASP.NET Core
ZipKin啟動(dòng)成功之后,我們就可以將程序中的數(shù)據(jù)采集到Zipkin中去了,我新建了兩個(gè)ASP.NET Core的程序,一個(gè)是OrderApi,另一個(gè)是ProductApi方便能體現(xiàn)出調(diào)用鏈路,其中OrderApi調(diào)用ProductApi接口,在兩個(gè)項(xiàng)目中分別引入Zipkin依賴包
PackageReference Include="zipkin4net" Version="1.5.0" />
PackageReference Include="zipkin4net.middleware.aspnetcore" Version="1.5.0" />
其中zipkin4net為核心包,zipkin4net.middleware.aspnetcore是集成ASP.NET Core的程序包。然后我們?cè)赟tartup文件中添加如下方法
public void RegisterZipkinTrace(IApplicationBuilder app, ILoggerFactory loggerFactory, IHostApplicationLifetime lifetime)
{
lifetime.ApplicationStarted.Register(() =>
{
//記錄數(shù)據(jù)密度,1.0代表全部記錄
TraceManager.SamplingRate = 1.0f;
//鏈路日志
var logger = new TracingLogger(loggerFactory, "zipkin4net");
//zipkin服務(wù)地址和內(nèi)容類型
var httpSender = new HttpZipkinSender("http://localhost:9411/", "application/json");
var tracer = new ZipkinTracer(httpSender, new JSONSpanSerializer(), new Statistics());
var consoleTracer = new zipkin4net.Tracers.ConsoleTracer();
TraceManager.RegisterTracer(tracer);
TraceManager.RegisterTracer(consoleTracer);
TraceManager.Start(logger);
});
//程序停止時(shí)停止鏈路跟蹤
lifetime.ApplicationStopped.Register(() => TraceManager.Stop());
//引入zipkin中間件,用于跟蹤服務(wù)請(qǐng)求,這邊的名字可自定義代表當(dāng)前服務(wù)名稱
app.UseTracing(Configuration["nacos:ServiceName"]);
}
然后我們?cè)贑onfigure方法中調(diào)用RegisterZipkinTrace方法即可。由于我們要在OrderApi項(xiàng)目中采用HttpClient的方式調(diào)用ProductAPI,默認(rèn)zipkin4net是支持采集HttpClient發(fā)出請(qǐng)求的鏈路數(shù)據(jù)(由于在ProductApi中我們并不發(fā)送Http請(qǐng)求,所以可以不用集成一下操作),具體集成形式如下,如果使用的是HttpClientFactory的方式,在ConfigureServices中配置如下
public void ConfigureServices(IServiceCollection services)
{
//由于我使用了Nacos作為服務(wù)注冊(cè)中心
services.AddNacosAspNetCore(Configuration);
services.AddScopedNacosDiscoveryDelegatingHandler>();
services.AddHttpClient(ServiceName.ProductService,client=> {
client.BaseAddress = new Uri($"http://{ServiceName.ProductService}");
})
.AddHttpMessageHandlerNacosDiscoveryDelegatingHandler>()
//引入zipkin trace跟蹤httpclient請(qǐng)求,名稱配置當(dāng)前服務(wù)名稱即可
.AddHttpMessageHandler(provider =>TracingHandler.WithoutInnerHandler(Configuration["nacos:ServiceName"]));
services.AddControllers();
}
如果是直接是使用HttpClient的形式調(diào)用則可以采用以下方式
using (HttpClient client = new HttpClient(new TracingHandler("OrderApi")))
{
}
然后我們?cè)贠rderApi中寫一段調(diào)用ProductApi的代碼
[Route("orderapi/[controller]")]
public class OrderController : ControllerBase
{
private ListOrderDto> orderDtos = new ListOrderDto>();
private readonly IHttpClientFactory _clientFactory;
public OrderController(IHttpClientFactory clientFactory)
{
orderDtos.Add(new OrderDto { Id = 1, TotalMoney=222,Address="北京市",Addressee="me",From="淘寶",SendAddress="武漢" });
_clientFactory = clientFactory;
}
/// summary>
/// 獲取訂單詳情接口
/// /summary>
/// param name="id">訂單id/param>
/// returns>/returns>
[HttpGet("getdetails/{id}")]
public async TaskOrderDto> GetOrderDetailsAsync(long id)
{
OrderDto orderDto = orderDtos.FirstOrDefault(i => i.Id == id);
if (orderDto != null)
{
OrderDetailDto orderDetailDto = new OrderDetailDto
{
Id = orderDto.Id,
TotalMoney = orderDto.TotalMoney,
Address = orderDto.Address,
Addressee = orderDto.Addressee,
From = orderDto.From,
SendAddress = orderDto.SendAddress
};
//調(diào)用ProductApi服務(wù)接口
var client = _clientFactory.CreateClient(ServiceName.ProductService);
var response = await client.GetAsync($"/productapi/product/getall");
var result = await response.Content.ReadAsStringAsync();
orderDetailDto.Products = JsonConvert.DeserializeObjectListOrderProductDto>>(result);
return orderDetailDto;
}
return orderDto;
}
}
在ProductApi中我們只需要編寫調(diào)用RegisterZipkinTrace方法即可,和OrderApi一樣,我們就不重復(fù)粘貼了。因?yàn)镻roductApi不需要調(diào)用別的服務(wù),所以可以不必使用集成HttpClient,只需要提供簡(jiǎn)單的接口即可
[Route("productapi/[controller]")]
public class ProductController : ControllerBase
{
private ListProductDto> productDtos = new ListProductDto>();
public ProductController()
{
productDtos.Add(new ProductDto { Id = 1,Name="酒精",Price=22.5m });
productDtos.Add(new ProductDto { Id = 2, Name = "84消毒液", Price = 19.9m });
}
/// summary>
/// 獲取所有商品信息
/// /summary>
/// returns>/returns>
[HttpGet("getall")]
public IEnumerableProductDto> GetAll()
{
return productDtos;
}
}
啟動(dòng)這兩個(gè)項(xiàng)目,調(diào)用OrderApi的getdetails接口,完成后打開zipkin界面點(diǎn)擊進(jìn)去可查看鏈路詳情
總結(jié)起來核心操作其實(shí)就兩個(gè),一個(gè)是在發(fā)送請(qǐng)求的地方,使用TracingHandler記錄發(fā)起端的鏈路情況,然后在接收請(qǐng)求的服務(wù)端使用UseTracing記錄來自于客戶端請(qǐng)求的鏈路情況。
改進(jìn)集成方式
其實(shí)在上面的演示中,我們可以明顯的看到明顯的不足,就是很多時(shí)候其實(shí)我們沒辦法去設(shè)置HttpClient相關(guān)的參數(shù)的,很多框架雖然也是使用的HttpClient或HttpClientFactory相關(guān),但是在外部我們沒辦法通過自定義的方式去設(shè)置他們的相關(guān)操作,比如Ocelot其實(shí)也是使用HttpClient相關(guān)發(fā)起的轉(zhuǎn)發(fā)請(qǐng)求,但是對(duì)外我們沒辦法通過我們的程序去設(shè)置HttpClient的參數(shù)。還有就是在.Net Core中WebRequest其實(shí)也是對(duì)HttpClient的封裝,但是我們同樣沒辦法在我們的程序中給他們傳遞類似TracingHandler的操作?,F(xiàn)在我們從TracingHandler源碼開始解讀看看它的內(nèi)部到底是如何工作的,zipkin官方提供的.net core插件zipkin4net的源碼位于
https://github.com/openzipkin/zipkin4net,我們找到TracingHandler類所在的位置[點(diǎn)擊查看源碼👈],由于TracingHandler本身就是DelegatingHandler的子類,所以我們主要看SendAsync方法,大致抽離出來如下
protected override async TaskHttpResponseMessage> SendAsync(HttpRequestMessage request, System.Threading.CancellationToken cancellationToken)
{
FuncHttpRequestMessage, string> _getClientTraceRpc = _getClientTraceRpc = getClientTraceRpc ?? (request => request.Method.ToString());
IInjectorHttpHeaders> _injector = Propagations.B3String.InjectorHttpHeaders>((carrier, key, value) => carrier.Add(key, value));
//記錄發(fā)起請(qǐng)求客戶端鏈路信息的類是ClientTrace
using (var clientTrace = new ClientTrace(_serviceName, _getClientTraceRpc(request)))
{
if (clientTrace.Trace != null)
{
_injector.Inject(clientTrace.Trace.CurrentSpan, request.Headers);
}
var result = await clientTrace.TracedActionAsync(base.SendAsync(request, cancellationToken));
//AddAnnotation是記錄標(biāo)簽信息,我們可以在zipkin鏈路詳情中看到這些標(biāo)簽
if (clientTrace.Trace != null)
{
//記錄請(qǐng)求路徑
clientTrace.AddAnnotation(Annotations.Tag(zipkinCoreConstants.HTTP_PATH, result.RequestMessage.RequestUri.LocalPath));
//記錄請(qǐng)求的http方法
clientTrace.AddAnnotation(Annotations.Tag(zipkinCoreConstants.HTTP_METHOD, result.RequestMessage.Method.Method));
if (_logHttpHost)
{
//記錄主機(jī)
clientTrace.AddAnnotation(Annotations.Tag(zipkinCoreConstants.HTTP_HOST, result.RequestMessage.RequestUri.Host));
}
if (!result.IsSuccessStatusCode)
{
clientTrace.AddAnnotation(Annotations.Tag(zipkinCoreConstants.HTTP_STATUS_CODE, ((int)result.StatusCode).ToString()));
}
}
return result;
}
}
實(shí)現(xiàn)方式比較簡(jiǎn)單,就是借助ClientTrace記錄一些標(biāo)簽,其他的相關(guān)操作都是由zipkin4net提供的。我們?cè)谥暗奈恼?Net Core中的診斷日志DiagnosticSource講解中層說道HttpClient底層會(huì)有發(fā)出診斷日志,我們可以借助這個(gè)思路,來對(duì)HttpClient進(jìn)行鏈路跟蹤埋點(diǎn)。
我們結(jié)合Microsoft.Extensions.DiagnosticAdapter擴(kuò)展包定義如下類
public class HttpDiagnosticListener: ITraceDiagnosticListener
{
public string DiagnosticName => "HttpHandlerDiagnosticListener";
private ClientTrace clientTrace;
private readonly IInjectorHttpHeaders> _injector = Propagations.B3String.InjectorHttpHeaders>((carrier, key, value) => carrier.Add(key, value));
[DiagnosticName("System.Net.Http.Request")]
public void HttpRequest(HttpRequestMessage request)
{
clientTrace = new ClientTrace("apigateway", request.Method.Method);
if (clientTrace.Trace != null)
{
_injector.Inject(clientTrace.Trace.CurrentSpan, request.Headers);
}
}
[DiagnosticName("System.Net.Http.Response")]
public void HttpResponse(HttpResponseMessage response)
{
if (clientTrace.Trace != null)
{
clientTrace.AddAnnotation(Annotations.Tag(zipkinCoreConstants.HTTP_PATH, response.RequestMessage.RequestUri.LocalPath));
clientTrace.AddAnnotation(Annotations.Tag(zipkinCoreConstants.HTTP_METHOD, response.RequestMessage.Method.Method));
clientTrace.AddAnnotation(Annotations.Tag(zipkinCoreConstants.HTTP_HOST, response.RequestMessage.RequestUri.Host));
if (!response.IsSuccessStatusCode)
{
clientTrace.AddAnnotation(Annotations.Tag(zipkinCoreConstants.HTTP_STATUS_CODE, ((int)response.StatusCode).ToString()));
}
}
}
[DiagnosticName("System.Net.Http.Exception")]
public void HttpException(HttpRequestMessage request,Exception exception)
{
}
}
ITraceDiagnosticListener是我們方便操作DiagnosticListener定義的接口,接口僅包含DiagnosticName用來表示DiagnosticListener監(jiān)聽的名稱,有了這個(gè)接口接下來的操作我們會(huì)方便許多,接下來我們來看訂閱操作的實(shí)現(xiàn)。
public class TraceObserver :IObserverDiagnosticListener>
{
private IEnumerableITraceDiagnosticListener> _traceDiagnostics;
public TraceObserver(IEnumerableITraceDiagnosticListener> traceDiagnostics)
{
_traceDiagnostics = traceDiagnostics;
}
public void OnCompleted()
{
}
public void OnError(Exception error)
{
}
public void OnNext(DiagnosticListener listener)
{
//這樣的話我們可以更輕松的擴(kuò)展其他DiagnosticListener的操作
var traceDiagnostic = _traceDiagnostics.FirstOrDefault(i=>i.DiagnosticName==listener.Name);
if (traceDiagnostic!=null)
{
//適配訂閱
listener.SubscribeWithAdapter(traceDiagnostic);
}
}
}
通過這種操作我們就無需關(guān)心如何將自定義的DiagnosticListener訂閱類適配到DiagnosticAdapter中去,方便我們自定義其他DiagnosticListener的訂閱類,這樣的話我們只需注冊(cè)自定義的訂閱類即可。
services.AddSingletonTraceObserver>();
services.AddSingletonITraceDiagnosticListener, HttpDiagnosticListener>();
通過這種改進(jìn)方式,我們可以解決類似HttpClient封裝到框架中,并且我們我們無法通過外部程序去修改設(shè)置的時(shí)候。比如我們?cè)诩軜?gòu)中引入了Ocelot網(wǎng)關(guān),我們就可以采用類似這種方式,在網(wǎng)關(guān)層集成zipkin4net。
自定義埋點(diǎn)
通過上面我們查看TracingHandler的源碼我們得知埋點(diǎn)主要是通過ClientTrace進(jìn)行的,它是在發(fā)起請(qǐng)求的客戶端進(jìn)行埋點(diǎn)。在服務(wù)端埋點(diǎn)的方式我們可以通過TracingMiddleware中間件中的源碼查看到[點(diǎn)擊查看源碼👈]叫ServerTrace。有了ClientTrace和ServerTrace我們可以非常輕松的實(shí)現(xiàn)一次完整的客戶端和服務(wù)端埋點(diǎn),只需要通過它們打上一些標(biāo)簽即可。其實(shí)它們都是對(duì)Trace類的封裝,我們找到它們的源碼進(jìn)行查看
public class ClientTrace : BaseStandardTrace, IDisposable
{
public ClientTrace(string serviceName, string rpc)
{
if (Trace.Current != null)
{
Trace = Trace.Current.Child();
}
Trace.Record(Annotations.ClientSend());
Trace.Record(Annotations.ServiceName(serviceName));
Trace.Record(Annotations.Rpc(rpc));
}
public void Dispose()
{
Trace.Record(Annotations.ClientRecv());
}
}
public class ServerTrace : BaseStandardTrace, IDisposable
{
public override Trace Trace
{
get
{
return Trace.Current;
}
}
public ServerTrace(string serviceName, string rpc)
{
Trace.Record(Annotations.ServerRecv());
Trace.Record(Annotations.ServiceName(serviceName));
Trace.Record(Annotations.Rpc(rpc));
}
public void Dispose()
{
Trace.Record(Annotations.ServerSend());
}
}
因此,如果你想通過更原始的方式去記錄跟蹤日志可以采用如下方式
var trace = Trace.Create();
trace.Record(Annotations.ServerRecv());
trace.Record(Annotations.ServiceName(serviceName));
trace.Record(Annotations.Rpc("GET"));
trace.Record(Annotations.ServerSend());
trace.Record(Annotations.Tag("http.url", "url>"));
示例Demo
由于上面說的比較多,而且有一部分關(guān)于源碼的解讀,為了防止由本人文筆有限,給大家?guī)砝斫庹`區(qū),另一方面也為了更清晰的展示Zipkin的集成方式,我自己做了一套Demo,目錄結(jié)構(gòu)如下
ApiGateway為網(wǎng)關(guān)項(xiàng)目可以轉(zhuǎn)發(fā)針對(duì)OrderApi的請(qǐng)求,OrderApi和ProductApi用于模擬業(yè)務(wù)系統(tǒng),這三個(gè)項(xiàng)目都集成了zipkin4net鏈路跟蹤,他們之間是通過Nacos實(shí)現(xiàn)服務(wù)的注冊(cè)和發(fā)現(xiàn)。這個(gè)演示Demo我本地是可以直接運(yùn)行成功的,如果有下載下來運(yùn)行不成功的,可以評(píng)論區(qū)給我留言。由于博客園有文件上傳大小的限制,所以我將Demo上傳到了百度網(wǎng)盤中
下載鏈接:鏈接: https://pan.baidu.com/s/1LDyoRQehaE0FzedFTC4_Og 提取碼: i45x
總結(jié)
以上就是關(guān)于Zipkin以及ASP.NET Core整合Zipkin的全部?jī)?nèi)容,希望能給大家?guī)硪欢ǖ膸椭?。如果你有?shí)際需要也可以繼續(xù)自行研究。Zipkin相對(duì)于我們常用的Skywalking而且,它的使用方式比較原生,許多操作都需要自行通過代碼操作,而SkyAPM可以做到對(duì)代碼無入侵的方式集成。Skywalking是一款A(yù)PM(應(yīng)用性能管理),鏈路跟蹤只是它功能的一部分。而Zipkin是一款專注于鏈路跟蹤的系統(tǒng),個(gè)人感覺就鏈路跟蹤這一塊而言,Zipkin更輕量級(jí)(如果使用ES作為存儲(chǔ)數(shù)據(jù)庫(kù)的話,Skywalking默認(rèn)會(huì)生成一堆索引,而Zipkin默認(rèn)是每天創(chuàng)建一個(gè)索引),而且鏈路信息檢索、詳情展示、鏈路數(shù)據(jù)上報(bào)形式等相對(duì)于Skywalking形式也更豐富一些。但是整體而言Skywalking更強(qiáng)大,比如應(yīng)用監(jiān)控、調(diào)用分析、集成方式等。技術(shù)并無好壞之分,適合自己的才是更好的,多一個(gè)解決方案,就多一個(gè)解決問題的思路,我覺得這是對(duì)于我們程序開發(fā)人員來說都應(yīng)該具備的認(rèn)知。
到此這篇關(guān)于ASP.NET Core整合Zipkin鏈路跟蹤的實(shí)現(xiàn)方法的文章就介紹到這了,更多相關(guān)ASP.NET Core整合Zipkin鏈路跟蹤內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
您可能感興趣的文章:- 在 .NET Core 中使用 Diagnostics (Diagnostic Source) 記錄跟蹤信息