{"id":2602,"date":"2019-04-29T15:30:18","date_gmt":"2019-04-29T12:30:18","guid":{"rendered":"https:\/\/gokhan-gokalp.com\/?p=2602"},"modified":"2019-04-29T15:30:18","modified_gmt":"2019-04-29T12:30:18","slug":"distributed-tracing-with-opentracing-api-of-net-core-applications-on-kubernetes","status":"publish","type":"post","link":"https:\/\/gokhan-gokalp.com\/tr\/distributed-tracing-with-opentracing-api-of-net-core-applications-on-kubernetes\/","title":{"rendered":"Kubernetes \u00dczerindeki .NET Core Uygulamalar\u0131n\u0131n OpenTracing API ile Distributed Tracing \u0130\u015flemleri"},"content":{"rendered":"<p>Distributed tracing, microservice architecture&#8217;\u0131 olarak tasarlad\u0131\u011f\u0131m\u0131z sistem i\u00e7erisindeki uygulamalar\u0131m\u0131z\u0131n, nerede performans problemi ya\u015fad\u0131\u011f\u0131n\u0131 belirleyebilmemiz ve monitor edebilmemiz i\u00e7in harika bir method.<\/p>\n<p>Bir ba\u015fka de\u011fi\u015fle, hangi request nereye gidiyor, u\u00e7tan uca bir request ne kadar zaman harc\u0131yor gibi sorulara cevap alabilmemiz i\u00e7in implemente etmemiz gereken bir method.<\/p>\n<p>Bu makale kapsam\u0131nda ise, <strong><em>OpenTracing API<\/em>&#8216;<\/strong>\u0131n\u0131 ve <strong><em>Jaeger<\/em><\/strong> tracer&#8217;\u0131n\u0131 kullanarak <strong><em>.NET Core<\/em><\/strong> ile geli\u015ftirdi\u011fimiz <strong><em>kubernetes<\/em><\/strong> \u00fczerindeki microservice&#8217;lerin, distributed tracing i\u015flemlerine de\u011finece\u011fiz.<\/p>\n<h2>Senaryo<\/h2>\n<p>Bir e-ticaret sistemi \u00fczerinde \u00e7al\u0131\u015ft\u0131\u011f\u0131m\u0131z\u0131 ve uygulamalar\u0131m\u0131z\u0131 <em>kubernetes<\/em> \u00fczerinde host etti\u011fimizi d\u00fc\u015f\u00fcnelim. Kullan\u0131c\u0131lar ile ilgili i\u015flemlerden sorumlu bir <em>User<\/em>\u00a0<em>API<\/em>&#8216;\u0131m\u0131z var. Yeni bir kullan\u0131c\u0131 sisteme kay\u0131t oldu\u011funda ise, &#8220;<em>UserRegisteredEvent<\/em>&#8221; ad\u0131nda bir event publish ediliyor. Publish edilen bu event&#8217;in subscriber&#8217;lar\u0131ndan birisi ise, kullan\u0131c\u0131ya hesab\u0131n\u0131 aktifle\u015ftirebilmesi i\u00e7in e-posta g\u00f6ndermekle sorumlu bir service.<\/p>\n<p>Biz ise asenkron olarak ger\u00e7ekle\u015fen bu kullan\u0131c\u0131 kay\u0131t journey&#8217;inin, <em>OpenTracing API<\/em> ve <em>Jaeger<\/em> tracer&#8217;\u0131n\u0131 implemente ederek, <em>kubernetes<\/em> \u00fczerinde u\u00e7tan uca izleme i\u015flemini ger\u00e7ekle\u015ftirece\u011fiz.<\/p>\n<h2><em>OpenTracing<\/em> ve <em>Jaeger<\/em> Nedir?<\/h2>\n<p>K\u0131saca sizlere <em>OpenTracing<\/em> ve <em>Jaeger<\/em> hakk\u0131nda bilgi vermek istiyorum. <em>OpenTracing<\/em>, herhangi bir vendor&#8217;a ba\u011f\u0131ml\u0131 olmadan uygulamalar\u0131m\u0131za distributed tracing i\u00e7in bir instrumentation ekleyebilmemizi sa\u011flayan bir specification&#8217;d\u0131r. T\u0131pk\u0131, <em>OpenAPI<\/em> gibi.<\/p>\n<p><a href=\"\/wp-content\/uploads\/2019\/04\/jaeger-logo-1.png\"><img decoding=\"async\" class=\"aligncenter size-full wp-image-2607 lazyload\" data-src=\"\/wp-content\/uploads\/2019\/04\/jaeger-logo-1.png\" alt=\"\" width=\"1801\" height=\"587\" data-srcset=\"https:\/\/gokhan-gokalp.com\/wp-content\/uploads\/2019\/04\/jaeger-logo-1.png 1801w, https:\/\/gokhan-gokalp.com\/wp-content\/uploads\/2019\/04\/jaeger-logo-1-300x98.png 300w, https:\/\/gokhan-gokalp.com\/wp-content\/uploads\/2019\/04\/jaeger-logo-1-768x250.png 768w, https:\/\/gokhan-gokalp.com\/wp-content\/uploads\/2019\/04\/jaeger-logo-1-1024x334.png 1024w\" data-sizes=\"(max-width: 1801px) 100vw, 1801px\" src=\"data:image\/svg+xml;base64,PHN2ZyB3aWR0aD0iMSIgaGVpZ2h0PSIxIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjwvc3ZnPg==\" style=\"--smush-placeholder-width: 1801px; --smush-placeholder-aspect-ratio: 1801\/587;\" \/><\/a><\/p>\n<p><em>Jaeger<\/em> ise <em>Uber Technologies<\/em> taraf\u0131ndan geli\u015ftirilmi\u015f, <em>OpenTracing<\/em> standart&#8217;lar\u0131n\u0131 destekleyen ve microservice mimarimiz \u00fczerinde distributed tracing i\u015flemlerini yapabilmemizi sa\u011flayan harika bir tracer&#8217;d\u0131r. Jaeger hakk\u0131nda daha detayl\u0131 bilgiye ise, <em><a href=\"https:\/\/www.jaegertracing.io\/docs\/1.11\/\" target=\"_blank\" rel=\"noopener noreferrer\">buradan<\/a><\/em> ula\u015fabilirsiniz.<\/p>\n<p><em>Jaeger<\/em>&#8216;in <em>kubernetes<\/em> \u00fczerine kurulumu i\u00e7in, <em><a href=\"https:\/\/github.com\/jaegertracing\/jaeger-kubernetes\" target=\"_blank\" rel=\"noopener noreferrer\">\u015furadaki<\/a><\/em> dok\u00fcmanlar\u0131 takip edebilirsiniz. Ben bu makale i\u00e7in, development setup&#8217;\u0131n\u0131 uygulad\u0131m.<\/p>\n<pre class=\"lang:default decode:true \">kubectl create -f https:\/\/raw.githubusercontent.com\/jaegertracing\/jaeger-kubernetes\/master\/all-in-one\/jaeger-all-in-one-template.yml<\/pre>\n<p><a href=\"\/wp-content\/uploads\/2019\/04\/jaeger-architecture.png\"><img decoding=\"async\" class=\"aligncenter size-full wp-image-2625 lazyload\" data-src=\"\/wp-content\/uploads\/2019\/04\/jaeger-architecture.png\" alt=\"\" width=\"2239\" height=\"1202\" data-srcset=\"https:\/\/gokhan-gokalp.com\/wp-content\/uploads\/2019\/04\/jaeger-architecture.png 2239w, https:\/\/gokhan-gokalp.com\/wp-content\/uploads\/2019\/04\/jaeger-architecture-300x161.png 300w, https:\/\/gokhan-gokalp.com\/wp-content\/uploads\/2019\/04\/jaeger-architecture-768x412.png 768w, https:\/\/gokhan-gokalp.com\/wp-content\/uploads\/2019\/04\/jaeger-architecture-1024x550.png 1024w\" data-sizes=\"(max-width: 2239px) 100vw, 2239px\" src=\"data:image\/svg+xml;base64,PHN2ZyB3aWR0aD0iMSIgaGVpZ2h0PSIxIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjwvc3ZnPg==\" style=\"--smush-placeholder-width: 2239px; --smush-placeholder-aspect-ratio: 2239\/1202;\" \/><\/a><\/p>\n<p><em>Jaeger<\/em> genel hatlar\u0131yla, &#8220;<em>Agent<\/em>&#8220;, &#8220;<em>Collector<\/em>&#8221; ve &#8220;<em>Query<\/em>&#8221; den olu\u015fmaktad\u0131r. <em>Agent,<\/em> <em>UDP<\/em> \u00fczerinden kendisine gelen trace verilerini dinleyip, collector&#8217;e ileten bir network daemon&#8217;\u0131d\u0131r. <em>Trace<\/em> verileri ise &#8220;<em>Span<\/em>&#8221; olarak adland\u0131r\u0131lmaktad\u0131r. <em>Collector<\/em> ise kendisine iletilen trace verilerini, bir pipeline (validations, indexes, transformations) i\u00e7erisinde i\u015flemektedir. Daha sonra ise se\u00e7ti\u011finiz bir component (<em>Elasticsearch<\/em>, <em>Cassandra ve<\/em>\u00a0<em>Kafka<\/em>) t\u00fcr\u00fcne g\u00f6re store etmektedir.<\/p>\n<p><em>Query<\/em> ise isminden de anla\u015f\u0131labilece\u011fi \u00fczere, ilgili trace sonu\u00e7lar\u0131n\u0131 sorgulayabilece\u011fimiz bir <em>UI<\/em>.<\/p>\n<h2>Peki, Haydi Biraz Kodlayal\u0131m!<\/h2>\n<p>Kodlamaya ba\u015flamadan \u00f6nce platform i\u00e7in sahip olmam\u0131z gereken baz\u0131 tool&#8217;lar:<\/p>\n<ul>\n<li>Message Broker (ben <em>RabbitMQ<\/em> kullanaca\u011f\u0131m)<\/li>\n<li><em>Docker<\/em><\/li>\n<li>ve <em>Kuberentes<\/em><\/li>\n<\/ul>\n<blockquote><p><strong><em>NOT<\/em><\/strong>: Ana konumuz platformu olu\u015fturmak olmad\u0131\u011f\u0131 i\u00e7in, ben kurulum konular\u0131 \u00fczerinde durmayaca\u011f\u0131m.<\/p><\/blockquote>\n<p>\u0130lk olarak kullan\u0131c\u0131lar\u0131n sisteme kay\u0131t olabilmelerini sa\u011flayacak olan <em>User API<\/em>&#8216;\u0131n\u0131 develop edelim. Bunun i\u00e7in \u00f6ncelikle a\u015fa\u011f\u0131daki gibi bir proje olu\u015ftural\u0131m.<\/p>\n<pre class=\"lang:default decode:true\">dotnet new webapi -n User.API<\/pre>\n<p><a href=\"\/wp-content\/uploads\/2019\/04\/create-user-api.jpg\"><img decoding=\"async\" class=\"aligncenter size-full wp-image-2609 lazyload\" data-src=\"\/wp-content\/uploads\/2019\/04\/create-user-api.jpg\" alt=\"\" width=\"1018\" height=\"337\" data-srcset=\"https:\/\/gokhan-gokalp.com\/wp-content\/uploads\/2019\/04\/create-user-api.jpg 1018w, https:\/\/gokhan-gokalp.com\/wp-content\/uploads\/2019\/04\/create-user-api-300x99.jpg 300w, https:\/\/gokhan-gokalp.com\/wp-content\/uploads\/2019\/04\/create-user-api-768x254.jpg 768w\" data-sizes=\"(max-width: 1018px) 100vw, 1018px\" src=\"data:image\/svg+xml;base64,PHN2ZyB3aWR0aD0iMSIgaGVpZ2h0PSIxIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjwvc3ZnPg==\" style=\"--smush-placeholder-width: 1018px; --smush-placeholder-aspect-ratio: 1018\/337;\" \/><\/a><\/p>\n<p>Ard\u0131ndan &#8220;<em>User.Common.Contracts<\/em>&#8221; isminde bir class library olu\u015ftural\u0131m.<\/p>\n<pre class=\"lang:default decode:true\">dotnet new classlib -n User.Common.Contracts<\/pre>\n<p>Bu library i\u00e7erisinde uygulamalar\u0131m\u0131z aras\u0131nda share edece\u011fimiz contract&#8217;lar\u0131 tan\u0131mlayaca\u011f\u0131z. \u015eimdi yeni bir kullan\u0131c\u0131 sisteme kay\u0131t oldu\u011funda publish edece\u011fimiz event&#8217;i, a\u015fa\u011f\u0131daki gibi\u00a0olu\u015ftural\u0131m.<\/p>\n<pre class=\"lang:default decode:true\">using System.Collections.Generic;\r\n\r\nnamespace User.Common.Contracts\r\n{\r\n    public class UserRegisteredEvent\r\n    {\r\n        public string Email { get; set; }\r\n        public Dictionary&lt;string, string&gt; TracingKeys { get; set; }\r\n    }\r\n}<\/pre>\n<p>Olu\u015fturma i\u015fleminin ard\u0131ndan, &#8220;<em>User.Common.Contracts<\/em>&#8221; library&#8217;sini, az \u00f6nce olu\u015fturmu\u015f oldu\u011fumuz &#8220;<em>User.API<\/em>&#8221;\u00a0projesine referans olarak ekleyelim.<\/p>\n<pre class=\"lang:default decode:true\">dotnet add reference ..\/User.Common.Contracts\/User.Common.Contracts.csproj<\/pre>\n<p>\u015eimdi &#8220;<em>User.API<\/em>&#8221; projesi i\u00e7erisinde &#8220;<em>Models<\/em>&#8221; isminde bir klas\u00f6r olu\u015ftural\u0131m ve ard\u0131ndan i\u00e7erisine &#8220;<em>Requests<\/em>&#8221; ve &#8220;<em>Responses<\/em>&#8221; klas\u00f6rlerini de olu\u015ftural\u0131m.<\/p>\n<p>&#8220;<em>Requests<\/em>&#8221; klas\u00f6r\u00fc i\u00e7erisine kullan\u0131c\u0131n\u0131n kay\u0131t olurken kullanaca\u011f\u0131 modeli a\u015fa\u011f\u0131daki gibi tan\u0131mlayal\u0131m.<\/p>\n<pre class=\"lang:default decode:true \">namespace User.API.Models.Requests\r\n{\r\n    public class CreateUserRequest\r\n    {\r\n        public string Username { get; set; }\r\n        public string Password { get; set; }\r\n        public string Email { get; set; }\r\n    }\r\n}<\/pre>\n<p>&#8220;<em>Responses<\/em>&#8221; klas\u00f6r\u00fc i\u00e7erisine ise, internal response wrapper class&#8217;\u0131n\u0131 tan\u0131mlayal\u0131m.<\/p>\n<pre class=\"lang:default decode:true \">using System.Collections.Generic;\r\nusing System.Linq;\r\n\r\nnamespace User.API.Models.Responses\r\n{\r\n    public class BaseResponse\r\n    {\r\n        public BaseResponse()\r\n        {\r\n            Errors = new List();\r\n        }\r\n\r\n        public T Data { get; set; }\r\n        public List Errors { get; set; }\r\n        public bool HasError { get { return Errors.Any();  } }\r\n    }\r\n}<\/pre>\n<p>\u015eimdi ise &#8220;<em>Services<\/em>&#8221; isminde bir klas\u00f6r olu\u015ftural\u0131m &#8220;<em>User.API<\/em>&#8221; projesi i\u00e7erisinde. Ard\u0131ndan i\u00e7erisine a\u015fa\u011f\u0131daki gibi &#8220;<em>IUserService<\/em>&#8221; isminde bir interface tan\u0131mlayal\u0131m.<\/p>\n<pre class=\"lang:default decode:true \">using System.Threading.Tasks;\r\nusing User.API.Models.Requests;\r\nusing User.API.Models.Responses;\r\n\r\nnamespace User.API.Services\r\n{\r\n    public interface IUserService\r\n    {\r\n         Task CreateUserAsync(CreateUserRequest request);\r\n    }\r\n}<\/pre>\n<p>Kullan\u0131c\u0131 ile ilgili business logic&#8217;leri, bu service arac\u0131l\u0131\u011f\u0131 ile ger\u00e7ekle\u015ftirece\u011fiz.<\/p>\n<p>Bu noktada messaging ile ilgili i\u015flemleri reliable bir \u015fekilde ger\u00e7ekle\u015ftirebilmemiz i\u00e7in projemize <em>NuGet<\/em> \u00fczerinden bir service bus ekleyece\u011fiz. Ben <em>MassTransit<\/em>&#8216;in lightweight bir wrapper&#8217;\u0131 olan <a href=\"https:\/\/github.com\/GokGokalp\/MetroBus\" target=\"_blank\" rel=\"noopener noreferrer\"><em>MetroBus<\/em><\/a>\u00a0library&#8217;sini kullanaca\u011f\u0131m.<\/p>\n<p><a href=\"\/wp-content\/uploads\/2019\/04\/metrobus-logo.png\"><img decoding=\"async\" class=\"aligncenter size-full wp-image-2611 lazyload\" data-src=\"\/wp-content\/uploads\/2019\/04\/metrobus-logo.png\" alt=\"\" width=\"442\" height=\"102\" data-srcset=\"https:\/\/gokhan-gokalp.com\/wp-content\/uploads\/2019\/04\/metrobus-logo.png 442w, https:\/\/gokhan-gokalp.com\/wp-content\/uploads\/2019\/04\/metrobus-logo-300x69.png 300w\" data-sizes=\"(max-width: 442px) 100vw, 442px\" src=\"data:image\/svg+xml;base64,PHN2ZyB3aWR0aD0iMSIgaGVpZ2h0PSIxIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjwvc3ZnPg==\" style=\"--smush-placeholder-width: 442px; --smush-placeholder-aspect-ratio: 442\/102;\" \/><\/a><\/p>\n<pre class=\"lang:default decode:true\">dotnet add package MetroBus\r\ndotnet add package MetroBus.Microsoft.Extensions.DependencyInjection<\/pre>\n<p>Ard\u0131ndan distributed tracing i\u015flemlerini ger\u00e7ekle\u015ftirebilmemiz i\u00e7in ise <em>OpenTracing<\/em> ve <em>Jaeger<\/em> package&#8217;lar\u0131n\u0131 eklememiz gerekmektedir.<\/p>\n<pre class=\"lang:default decode:true \">dotnet add package OpenTracing.Contrib.NetCore\r\ndotnet add package Jaeger<\/pre>\n<p>\u015eimdi bir klas\u00f6re daha ihtiyac\u0131m\u0131z var. Ben structured klas\u00f6r yap\u0131lar\u0131n\u0131 seviyorum. Her neyse, &#8220;<em>Services<\/em>&#8221; klas\u00f6r\u00fc alt\u0131nda, &#8220;<em>Implementations<\/em>&#8221; ad\u0131nda bir klas\u00f6r olu\u015ftural\u0131m ve i\u00e7erisinde &#8220;<em>IUserService<\/em>&#8221; interface&#8217;ini a\u015fa\u011f\u0131daki gibi implemente edelim.<\/p>\n<pre class=\"lang:default decode:true\">using System;\r\nusing System.Threading.Tasks;\r\nusing User.API.Models.Requests;\r\nusing User.API.Models.Responses;\r\nusing MassTransit;\r\nusing Microsoft.Extensions.Logging;\r\nusing User.Common.Contracts;\r\nusing OpenTracing;\r\nusing OpenTracing.Tag;\r\nusing OpenTracing.Propagation;\r\nusing System.Collections.Generic;\r\n\r\nnamespace User.API.Services.Implementations\r\n{\r\n    public class UserService : IUserService\r\n    {\r\n        private readonly ILogger _logger;\r\n        private readonly IBusControl _busControl;\r\n        private readonly ITracer _tracer;\r\n\r\n        public UserService(ILogger logger, IBusControl busControl, ITracer tracer)\r\n        {\r\n            _logger = logger;\r\n            _busControl = busControl;\r\n            _tracer = tracer;\r\n        }\r\n\r\n        public async Task CreateUserAsync(CreateUserRequest request)\r\n        {\r\n            BaseResponse createUserResponse = new BaseResponse();\r\n\r\n            try\r\n            {\r\n                using (var scope = _tracer.BuildSpan(\"create-user-async\").StartActive(finishSpanOnDispose: true))\r\n                {\r\n                    var span = scope.Span.SetTag(Tags.SpanKind, Tags.SpanKindClient);\r\n\r\n                    var dictionary = new Dictionary&lt;string, string&gt;();\r\n                    _tracer.Inject(span.Context, BuiltinFormats.TextMap, new TextMapInjectAdapter(dictionary));\r\n\r\n                    \/\/some user create business logics\r\n\r\n                    createUserResponse.Data = 1; \/\/ User id\r\n\r\n                    await _busControl.Publish(new UserRegisteredEvent\r\n                    {\r\n                        Email = request.Email,\r\n                        TracingKeys = dictionary\r\n                    });\r\n                }\r\n            }\r\n            catch (Exception ex)\r\n            {\r\n                createUserResponse.Errors.Add(ex.Message);\r\n                _logger.LogError(ex, ex.Message);\r\n            }\r\n\r\n            return createUserResponse;\r\n        }\r\n    }\r\n}<\/pre>\n<p>Service i\u00e7erisinde k\u0131saca neler yapt\u0131k bir bakal\u0131m.<\/p>\n<p>Trace context&#8217;inin di\u011fer service&#8217;lere otomatik olarak propagate edilmesi <em>Jaeger<\/em> taraf\u0131ndan zaten otomatik olarak sa\u011flan\u0131yor. Yani <strong>api-to-api<\/strong> communication&#8217;\u0131 oldu\u011funda ve gerekli configuration&#8217;\u0131 yapt\u0131\u011f\u0131n\u0131zda, bir request&#8217;i u\u00e7tan uca izleyebiliyorsunuz.<\/p>\n<p>Biz bu noktada ise <strong>api-to-subscriber<\/strong> olarak event-based bir communication ger\u00e7ekle\u015ftirdi\u011fimiz i\u00e7in, trace context&#8217;inin propagation i\u015flemini manuel olarak kendimiz sa\u011flad\u0131k. Trace scope&#8217;u i\u00e7erisine bakarsak, &#8220;<em>create-user-async<\/em>&#8221; isminde bir span olu\u015fturduk. Sonras\u0131nda ise client oldu\u011funa dair bir tag ekledik. Tag&#8217;leri kullanarak span&#8217;a additional metadata&#8217;lar ekleyebilmek m\u00fcmk\u00fcnd\u00fcr. Ard\u0131ndan ise &#8220;<em>TextMapInjectAdapter<\/em>&#8221; arac\u0131l\u0131\u011f\u0131 ile ilgili trace context&#8217;ini dictionary&#8217;e inject ettik.<\/p>\n<p>Kullan\u0131c\u0131n\u0131n sisteme kay\u0131t olabilme i\u015flemlerini de tamamlayarak, tracing key&#8217;leri ile birlikte &#8220;<em>UserRegisteredEvent<\/em>&#8221; ini service bus arac\u0131l\u0131\u011f\u0131 ile queue&#8217;ya publish ettik. Bu noktadan sonra ilgili event&#8217;i her kim consume ederse, tracing key&#8217;lerini kulland\u0131\u011f\u0131 s\u00fcrece t\u00fcm ak\u0131\u015f\u0131 g\u00f6zlemleyebilece\u011fiz.<\/p>\n<p><a href=\"\/wp-content\/uploads\/2019\/04\/sherlock1sw-1.jpg\"><img decoding=\"async\" class=\"aligncenter size-full wp-image-2645 lazyload\" data-src=\"\/wp-content\/uploads\/2019\/04\/sherlock1sw-1.jpg\" alt=\"\" width=\"720\" height=\"380\" data-srcset=\"https:\/\/gokhan-gokalp.com\/wp-content\/uploads\/2019\/04\/sherlock1sw-1.jpg 720w, https:\/\/gokhan-gokalp.com\/wp-content\/uploads\/2019\/04\/sherlock1sw-1-300x158.jpg 300w\" data-sizes=\"(max-width: 720px) 100vw, 720px\" src=\"data:image\/svg+xml;base64,PHN2ZyB3aWR0aD0iMSIgaGVpZ2h0PSIxIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjwvc3ZnPg==\" style=\"--smush-placeholder-width: 720px; --smush-placeholder-aspect-ratio: 720\/380;\" \/><\/a><\/p>\n<p>Art\u0131k controller&#8217;\u0131 olu\u015fturabiliriz. A\u015fa\u011f\u0131daki gibi &#8220;<em>UsersController<\/em>&#8221; isminde bir controller olu\u015ftural\u0131m.<\/p>\n<pre class=\"lang:default decode:true\">using Microsoft.AspNetCore.Mvc;\r\nusing User.API.Services;\r\nusing User.API.Models.Requests;\r\nusing User.API.Models.Responses;\r\nusing System.Threading.Tasks;\r\n\r\nnamespace User.API.Controllers\r\n{\r\n    [Route(\"api\/users\")]\r\n    [ApiController]\r\n    public class UsersController : ControllerBase\r\n    {\r\n        private readonly IUserService _userService;\r\n\r\n        public UsersController(IUserService userService)\r\n        {\r\n            _userService = userService;\r\n        }\r\n\r\n        [HttpPost]\r\n        public async Task Post([FromBody]CreateUserRequest request)\r\n        {\r\n            BaseResponse createUserResponse = await _userService.CreateUserAsync(request);\r\n\r\n            if(!createUserResponse.HasError)\r\n            {\r\n                return Created(\"users\", createUserResponse.Data);\r\n            }\r\n            else\r\n            {\r\n                return BadRequest(createUserResponse.Errors);\r\n            }\r\n        }\r\n    }\r\n}<\/pre>\n<p>Controller i\u00e7erisinde ise, olu\u015fturdu\u011fumuz &#8220;<em>IUserService<\/em>&#8221; interface&#8217;i \u00fczerinden kullan\u0131c\u0131n\u0131n kay\u0131t i\u015flemlerini ger\u00e7ekle\u015ftiriyoruz.<\/p>\n<p>\u015eimdi &#8220;<em>Startup<\/em>&#8221; class&#8217;\u0131n\u0131 a\u00e7al\u0131m ve ilgili service injection i\u015flemlerini a\u015fa\u011f\u0131daki gibi ger\u00e7ekle\u015ftirelim.<\/p>\n<pre class=\"lang:default decode:true\">public void ConfigureServices(IServiceCollection services)\r\n{\r\n    services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);\r\n\r\n    services.AddScoped&lt;IUserService, UserService&gt;();\r\n\r\n    string rabbitMqUri = Configuration.GetValue(\"RabbitMqUri\");\r\n    string rabbitMqUserName = Configuration.GetValue(\"RabbitMqUserName\");\r\n    string rabbitMqPassword = Configuration.GetValue(\"RabbitMqPassword\");\r\n\r\n    services.AddSingleton(MetroBusInitializer.Instance.UseRabbitMq(rabbitMqUri, rabbitMqUserName, rabbitMqPassword).Build());\r\n\r\n    services.AddOpenTracing();\r\n    services.AddSingleton(serviceProvider =&gt;\r\n    {\r\n        Environment.SetEnvironmentVariable(\"JAEGER_SERVICE_NAME\", \"User.API\");\r\n        Environment.SetEnvironmentVariable(\"JAEGER_AGENT_HOST\", \"localhost\");\r\n        Environment.SetEnvironmentVariable(\"JAEGER_AGENT_PORT\", \"6831\");\r\n        Environment.SetEnvironmentVariable(\"JAEGER_SAMPLER_TYPE\", \"const\");\r\n        \r\n        var loggerFactory = new LoggerFactory();\r\n\r\n        var config = Jaeger.Configuration.FromEnv(loggerFactory);\r\n        var tracer = config.GetTracer();\r\n\r\n        GlobalTracer.Register(tracer);\r\n\r\n        return tracer;\r\n    });\r\n\r\n    services.AddHealthChecks();\r\n}\r\n\r\npublic void Configure(IApplicationBuilder app, IHostingEnvironment env)\r\n{\r\n    \/\/...\r\n    \r\n    app.UseHealthChecks(\"\/health\");\r\n}<\/pre>\n<p>Service bus&#8217;\u0131, <em>RabbitMQ<\/em> kullanarak initialize ettik ve ard\u0131ndan injection i\u015flemini ger\u00e7ekle\u015ftirdik. Daha sonra ise tracer&#8217;\u0131 configure ederek inject ettik. Tracer&#8217;\u0131 yap\u0131land\u0131r\u0131rken ben sampling type&#8217;\u0131 olarak, &#8220;<em>const<\/em>&#8221; sampler&#8217;\u0131 kulland\u0131m. Di\u011fer se\u00e7ebilece\u011finiz sampling se\u00e7enekleri ise &#8220;<em>Probabilistic<\/em>&#8220;, &#8220;<em>Rate Limiting<\/em>&#8221; ve &#8220;<em>Remote<\/em>&#8221; \u015feklinde. Daha detayl\u0131 sampling bilgilerine ise <em><a href=\"https:\/\/www.jaegertracing.io\/docs\/1.11\/sampling\/\" target=\"_blank\" rel=\"noopener noreferrer\">buradan<\/a><\/em> eri\u015febilirsiniz. Agent host bilgisini de <em>kubernetes<\/em> ortam\u0131n\u0131zdaki node <em>IP<\/em>&#8216;si ile de\u011fi\u015ftirebilirsiniz. E\u011fer agent kurulumunu <strong>sidecar<\/strong> olarak ger\u00e7ekle\u015ftirirseniz de, herhangi bir bilgi set etmenize gerek olmayacakt\u0131r. Default bilgilerle eri\u015fim sa\u011flayacakt\u0131r.<\/p>\n<p><em>API<\/em> art\u0131k haz\u0131r durumda. Senaryomuza tekrar d\u00f6nelim. Kullan\u0131c\u0131 sisteme kay\u0131t olduktan sonra bir event publish edecektik. Daha sonra ise kullan\u0131c\u0131n\u0131n hesab\u0131n\u0131 aktifle\u015ftirebilmesi i\u00e7in o event&#8217;e subscribe olmu\u015f bir aktivasyon e-posta&#8217;s\u0131 g\u00f6nderen service olu\u015fturacakt\u0131k.<\/p>\n<p>\u015eimdi event&#8217;i publish ettik ve art\u0131k kullan\u0131c\u0131ya aktivasyon e-posta&#8217;s\u0131n\u0131 g\u00f6nderecek olan service&#8217;i kodlamaya ba\u015flayabiliriz.<\/p>\n<h2>Subscriber&#8217;\u0131n Kodlanmas\u0131<\/h2>\n<p>Bunun i\u00e7in yeni bir <em>.NET Core<\/em> console application&#8217;\u0131 olu\u015ftural\u0131m.<\/p>\n<pre class=\"lang:default decode:true\">dotnet new console -n User.Activation.Consumer<\/pre>\n<p>Olu\u015fturman\u0131n ard\u0131ndan shared library olan &#8220;<em>User.Common.Contracts<\/em>&#8221; projesini referans olarak ekleyelim. Daha sonra ise <em>NuGet<\/em> \u00fczerinden <em>MetroBus<\/em>&#8216;\u0131, <em>OpenTracing<\/em>&#8216;i ve <em>Jaeger&#8217;<\/em>i projeye a\u015fa\u011f\u0131daki gibi dahil edelim.<\/p>\n<pre class=\"lang:default decode:true\">dotnet add package MetroBus\r\ndotnet add package MetroBus.Microsoft.Extensions.DependencyInjection\r\ndotnet add package OpenTracing.Contrib.NetCore \r\ndotnet add package Jaeger\r\n<\/pre>\n<p>Console uygulamas\u0131, deamon olarak \u00e7al\u0131\u015facak bir background service olacak. App startup ve lifetime management&#8217;\u0131n\u0131 yapabilmemiz i\u00e7in ise <em>NuGet<\/em> \u00fczerinden &#8220;<em>Microsoft.Extensions.Hosting<\/em>&#8221; ve &#8220;<em>Microsoft.Extensions.DependencyInjection<\/em>&#8221; paketlerini de projeye dahil edelim.<\/p>\n<pre class=\"lang:default decode:true\">dotnet add package Microsoft.Extensions.Hosting\r\ndotnet add package Microsoft.Extensions.DependencyInjection<\/pre>\n<div>\n<div>Ayr\u0131ca configuration y\u00f6netimini ger\u00e7ekle\u015ftirebilmemiz i\u00e7inde &#8220;<em>Microsoft.Extensions.Configuration<\/em>&#8221; ve &#8220;<em>Microsoft.Extensions.Configuration.Json<\/em>&#8221; paketlerini de dahil edelim.<\/div>\n<\/div>\n<div>\n<pre class=\"lang:default decode:true\">dotnet add package Microsoft.Extensions.Configuration\r\ndotnet add package Microsoft.Extensions.Configuration.Json<\/pre>\n<\/div>\n<div>\n<div>\u0130lk olarak &#8220;Common&#8221; ad\u0131nda bir klas\u00f6r ve i\u00e7erisine a\u015fa\u011f\u0131daki gibi &#8220;<em>TracingExtension<\/em>&#8221; ad\u0131nda bir class ekleyelim.<\/div>\n<\/div>\n<div>\n<pre class=\"lang:default decode:true\">using System;\r\nusing System.Collections.Generic;\r\nusing OpenTracing;\r\nusing OpenTracing.Propagation;\r\nusing OpenTracing.Tag;\r\n\r\nnamespace User.Activation.Consumer.Common\r\n{\r\n    public static class TracingExtension\r\n    {\r\n        public static IScope StartServerSpan(ITracer tracer, IDictionary&lt;string, string&gt; headers, string operationName)\r\n        {\r\n            ISpanBuilder spanBuilder;\r\n            try\r\n            {\r\n                ISpanContext parentSpanCtx = tracer.Extract(BuiltinFormats.TextMap, new TextMapExtractAdapter(headers));\r\n\r\n                spanBuilder = tracer.BuildSpan(operationName);\r\n                if (parentSpanCtx != null)\r\n                {\r\n                    spanBuilder = spanBuilder.AsChildOf(parentSpanCtx);\r\n                }\r\n            }\r\n            catch (Exception)\r\n            {\r\n                spanBuilder = tracer.BuildSpan(operationName);\r\n            }\r\n\r\n            return spanBuilder.WithTag(Tags.SpanKind, Tags.SpanKindConsumer).StartActive(true);\r\n        }\r\n    }\r\n}<\/pre>\n<p><em>API<\/em> taraf\u0131nda hat\u0131rlarsak, tracing key&#8217;lerini event i\u00e7erisinde publish ederek trace context&#8217;inin propagation i\u015flemini manuel olarak ger\u00e7ekle\u015ftirmi\u015ftik. \u015eimdi ise consumer i\u00e7erisinde span olu\u015fturmak istedi\u011fimiz bir noktada, ayn\u0131 tracing key&#8217;lerini context&#8217;e extract ederek &#8220;<em>TracingExtension<\/em>&#8221; class&#8217;\u0131 vas\u0131tas\u0131yla ger\u00e7ekle\u015ftirece\u011fiz.<\/p>\n<\/div>\n<div>Peki, \u015fimdi ise root dizinde &#8220;<em>Consumers<\/em>&#8221; ad\u0131nda yeni bir klas\u00f6r daha olu\u015ftural\u0131m ve i\u00e7erisine a\u015fa\u011f\u0131daki gibi &#8220;<em>UserActivationConsumer<\/em>&#8221; ad\u0131nda bir class ekleyelim.<\/div>\n<div><\/div>\n<div>-&gt; User.Activation.Consumer.Common<\/div>\n<div>&#8212;&gt; Common<br \/>\n&#8212;&gt; Consumers<\/div>\n<div>\n<pre class=\"lang:default decode:true\">using System.Threading.Tasks;\r\nusing MassTransit;\r\nusing OpenTracing;\r\nusing User.Activation.Consumer.Common;\r\nusing User.Common.Contracts;\r\n\r\nnamespace User.Activation.Consumer.Consumers\r\n{\r\n    public class UserActivationConsumer : IConsumer&lt;UserRegisteredEvent&gt;\r\n    {\r\n        private readonly ITracer _tracer;\r\n\r\n        public UserActivationConsumer(ITracer tracer)\r\n        {\r\n            _tracer = tracer;\r\n        }\r\n\r\n        public async Task Consume(ConsumeContext&lt;UserRegisteredEvent&gt; context)\r\n        {\r\n            using (var scope = TracingExtension.StartServerSpan(_tracer, context.Message.TracingKeys, \"user-activation-link-sender-consumer\"))\r\n            {\r\n                \/\/some user activation link send business logics\r\n\r\n                await System.Console.Out.WriteLineAsync($\"Activation link sent for {context.Message.Email}\");\r\n            }\r\n        }\r\n    }\r\n}<\/pre>\n<p>Bu noktada, daha \u00f6nce <em>API <\/em>i\u00e7erisinden\u00a0publish etti\u011fimiz &#8220;<em>UserRegisteredEvent<\/em>&#8221; model&#8217;ine subscribe i\u015flemini ger\u00e7ekle\u015ftiriyoruz. Ard\u0131ndan &#8220;<em>ITracer<\/em>&#8221; interface&#8217;ini inject ediyoruz.<\/p>\n<p>&#8220;<em>Consume<\/em>&#8221; method&#8217;u i\u00e7erisinde ise, trace context&#8217;inin propagation i\u015flemini ger\u00e7ekle\u015ftirebilmemiz i\u00e7in olu\u015fturmu\u015f oldu\u011fumuz &#8220;<em>TracingExtension<\/em>&#8221; class&#8217;\u0131n\u0131 kullanarak bir scope olu\u015fturuyoruz. Propagate edilmi\u015f trace context&#8217;li &#8220;<em>user-activation-link-sender-consumer<\/em>&#8221; scope&#8217;u sayesinde, art\u0131k yapt\u0131\u011f\u0131m\u0131z i\u015flemleri <strong>api-to-subscriber\u00a0<\/strong>olarak trace edebilece\u011fiz.<\/p>\n<\/div>\n<div>\n<div>\n<div>Bu service bir background service&#8217;i olarak \u00e7al\u0131\u015faca\u011f\u0131 i\u00e7in, \u015fimdi root dizine d\u00f6nelim ve &#8220;<em>Services\/Implementations<\/em>&#8221; klas\u00f6rlerini olu\u015ftural\u0131m. Ard\u0131ndan &#8220;<em>Implementations<\/em>&#8221; klas\u00f6r\u00fc alt\u0131nda &#8220;<em>BusService<\/em>&#8221; ad\u0131nda bir class olu\u015ftural\u0131m ve a\u015fa\u011f\u0131daki gibi implemente edelim.<\/div>\n<\/div>\n<\/div>\n<div>\n<pre class=\"lang:default decode:true \">using System.Threading;\r\nusing System.Threading.Tasks;\r\nusing MassTransit;\r\nusing Microsoft.Extensions.Hosting;\r\n\r\nnamespace User.Activation.Consumer.Services.Implementations\r\n{\r\n    public class BusService : IHostedService\r\n    {\r\n        private readonly IBusControl _busControl;\r\n\r\n        public BusService(IBusControl busControl)\r\n        {\r\n            _busControl = busControl;\r\n        }\r\n\r\n        public Task StartAsync(CancellationToken cancellationToken)\r\n        {\r\n            return _busControl.StartAsync(cancellationToken);\r\n        }\r\n\r\n        public Task StopAsync(CancellationToken cancellationToken)\r\n        {\r\n            return _busControl.StopAsync(cancellationToken);\r\n        }\r\n    }\r\n}<\/pre>\n<p>Implementation s\u0131ras\u0131nda yapt\u0131\u011f\u0131m\u0131z tek \u015fey, bus&#8217;\u0131 start ve stop etmek.<\/p>\n<p>&#8220;<em>Program<\/em>&#8221; class&#8217;\u0131n\u0131 ise a\u015fa\u011f\u0131daki gibi d\u00fczenleyelim.<\/p>\n<\/div>\n<pre class=\"lang:default decode:true\">using System;\r\nusing System.IO;\r\nusing System.Threading.Tasks;\r\nusing Jaeger;\r\nusing Jaeger.Samplers;\r\nusing MassTransit;\r\nusing MetroBus;\r\nusing MetroBus.Microsoft.Extensions.DependencyInjection;\r\nusing Microsoft.Extensions.Configuration;\r\nusing Microsoft.Extensions.DependencyInjection;\r\nusing Microsoft.Extensions.Hosting;\r\nusing Microsoft.Extensions.Logging;\r\nusing OpenTracing;\r\nusing OpenTracing.Util;\r\nusing User.Activation.Consumer.Consumers;\r\nusing User.Activation.Consumer.Services.Implementations;\r\n\r\nnamespace User.Activation.Consumer\r\n{\r\n    class Program\r\n    {\r\n        static async Task Main(string[] args)\r\n        {\r\n            var host = new HostBuilder()\r\n                .ConfigureAppConfiguration((hostingContext, config) =&gt;\r\n                {\r\n                    config.SetBasePath(basePath: Directory.GetCurrentDirectory());\r\n                    config.AddJsonFile(\"appsettings.json\", optional : true);\r\n                })\r\n                .ConfigureServices((hostContext, services) =&gt;\r\n                {\r\n                    \/\/Init tracer\r\n                    services.AddSingleton&lt;ITracer&gt;(t =&gt; InitTracer());\r\n\r\n                    string rabbitMqUri = hostContext.Configuration.GetValue&lt;string&gt;(\"RabbitMqUri\");\r\n                    string rabbitMqUserName = hostContext.Configuration.GetValue&lt;string&gt;(\"RabbitMqUserName\");\r\n                    string rabbitMqPassword = hostContext.Configuration.GetValue&lt;string&gt;(\"RabbitMqPassword\");\r\n\r\n                    services.AddMetroBus(x =&gt;\r\n                    {\r\n                        x.AddConsumer&lt;UserActivationConsumer&gt;();\r\n                    });\r\n\r\n                    services.AddSingleton&lt;IBusControl&gt;(provider =&gt; MetroBusInitializer.Instance\r\n                        .UseRabbitMq(rabbitMqUri, rabbitMqUserName, rabbitMqPassword)\r\n                        .RegisterConsumer&lt;UserActivationConsumer&gt;(\"user.activation.queue\", provider)\r\n                        .Build());\r\n\r\n                    services.AddHostedService&lt;BusService&gt;();\r\n                });\r\n\r\n            await host.RunConsoleAsync();\r\n        }\r\n\r\n        private static ITracer InitTracer()\r\n        {\r\n            Environment.SetEnvironmentVariable(\"JAEGER_SERVICE_NAME\", \"User.Activation.Consumer\");\r\n            Environment.SetEnvironmentVariable(\"JAEGER_AGENT_HOST\", \"localhost\");\r\n            Environment.SetEnvironmentVariable(\"JAEGER_AGENT_PORT\", \"6831\");\r\n            Environment.SetEnvironmentVariable(\"JAEGER_SAMPLER_TYPE\", \"const\");\r\n\r\n            var loggerFactory = new LoggerFactory();\r\n\r\n            var config = Jaeger.Configuration.FromEnv(loggerFactory);\r\n            var tracer = config.GetTracer();\r\n\r\n            GlobalTracer.Register(tracer);\r\n\r\n            return tracer;\r\n        }\r\n    }\r\n}<\/pre>\n<p>San\u0131r\u0131m yukar\u0131da yapt\u0131klar\u0131m\u0131z yeterince a\u00e7\u0131k ve net. Configuration&#8217;\u0131 ve dependency injection&#8217;\u0131 configure ederek, service&#8217;lerimizi inject ediyoruz. Ayr\u0131ca trace&#8217;i ise, &#8220;<em>const<\/em>&#8221; type&#8217;\u0131 ile ve &#8220;<em>User.Activation.Consumer<\/em>&#8221; ad\u0131yla initialize ediyoruz.<\/p>\n<p>Consumer&#8217;\u0131 ise, &#8220;<em>user.activation.queue<\/em>&#8221; ad\u0131nda bir queue ile register ediyoruz. Bu queue ile &#8220;<em>UserRegisteredEvent<\/em>&#8221; model&#8217;ine subscribe olacakt\u0131r.<\/p>\n<h2>Deployment<\/h2>\n<p>Art\u0131k uygulamalar\u0131 deploy etmeye haz\u0131r\u0131z. Ben deployment i\u015flemlerini ger\u00e7ekle\u015ftirebilmek i\u00e7in basit bir <em>Docker<\/em> file ve <em>Helm<\/em> chart haz\u0131rlad\u0131m. Bu chart ile, uygulamalar\u0131 <em>Azure Kubernetes Service<\/em> \u00fczerine deploy edece\u011fim. Siz kendi environment&#8217;\u0131n\u0131za g\u00f6re chart&#8217;\u0131 de\u011fi\u015ftirebilirsiniz.<\/p>\n<p>Haz\u0131rlam\u0131\u015f oldu\u011fum chart ve docker file&#8217;a, <em><a href=\"https:\/\/github.com\/GokGokalp\/OpenTracing-Jaeger-Sample\/tree\/master\/helm-charts\" target=\"_blank\" rel=\"noopener noreferrer\">buradan<\/a><\/em> eri\u015febilirsiniz.<\/p>\n<h2>Test<\/h2>\n<p>\u015eimdi test a\u015famas\u0131na ge\u00e7ebiliriz. \u00d6ncelikle sistemde yeni bir kullan\u0131c\u0131 olu\u015fturabilmek i\u00e7in a\u015fa\u011f\u0131da oldu\u011fu gibi &#8220;<em>api\/users<\/em>&#8221; endpoint&#8217;ine bir <em>POST<\/em> request&#8217;i g\u00f6nderelim.<\/p>\n<p><a href=\"\/wp-content\/uploads\/2019\/04\/api_post.jpg\"><img decoding=\"async\" class=\"aligncenter size-full wp-image-2667 lazyload\" data-src=\"\/wp-content\/uploads\/2019\/04\/api_post.jpg\" alt=\"\" width=\"813\" height=\"422\" data-srcset=\"https:\/\/gokhan-gokalp.com\/wp-content\/uploads\/2019\/04\/api_post.jpg 813w, https:\/\/gokhan-gokalp.com\/wp-content\/uploads\/2019\/04\/api_post-300x156.jpg 300w, https:\/\/gokhan-gokalp.com\/wp-content\/uploads\/2019\/04\/api_post-768x399.jpg 768w\" data-sizes=\"(max-width: 813px) 100vw, 813px\" src=\"data:image\/svg+xml;base64,PHN2ZyB3aWR0aD0iMSIgaGVpZ2h0PSIxIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjwvc3ZnPg==\" style=\"--smush-placeholder-width: 813px; --smush-placeholder-aspect-ratio: 813\/422;\" \/><\/a><\/p>\n<p>Bu request ile kullan\u0131c\u0131 kay\u0131t journey&#8217;ini ba\u015flatm\u0131\u015f olduk. Senaryomuzda oldu\u011fu gibi, kullan\u0131c\u0131 kay\u0131t i\u015flemi ger\u00e7ekle\u015ftikten sonra bir event publish edildi.<\/p>\n<p><a href=\"\/wp-content\/uploads\/2019\/04\/kubernetes_service.jpg\"><img decoding=\"async\" class=\"aligncenter size-full wp-image-2668 lazyload\" data-src=\"\/wp-content\/uploads\/2019\/04\/kubernetes_service.jpg\" alt=\"\" width=\"977\" height=\"361\" data-srcset=\"https:\/\/gokhan-gokalp.com\/wp-content\/uploads\/2019\/04\/kubernetes_service.jpg 977w, https:\/\/gokhan-gokalp.com\/wp-content\/uploads\/2019\/04\/kubernetes_service-300x111.jpg 300w, https:\/\/gokhan-gokalp.com\/wp-content\/uploads\/2019\/04\/kubernetes_service-768x284.jpg 768w\" data-sizes=\"(max-width: 977px) 100vw, 977px\" src=\"data:image\/svg+xml;base64,PHN2ZyB3aWR0aD0iMSIgaGVpZ2h0PSIxIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjwvc3ZnPg==\" style=\"--smush-placeholder-width: 977px; --smush-placeholder-aspect-ratio: 977\/361;\" \/><\/a><\/p>\n<p>Event&#8217;in publish edilmenin ard\u0131ndan event&#8217;e subscribe etmi\u015f oldu\u011fumuz aktivasyon e-posta&#8217;s\u0131n\u0131 g\u00f6nderecek olan service (user-activation-consumer) ilgili i\u015flemini ger\u00e7ekle\u015ftirmi\u015ftir.<\/p>\n<p>Peki bu s\u00fcre\u00e7te neler oldu, haydi <em>Jaeger<\/em> \u00fczerinden bir bakal\u0131m.<\/p>\n<p><a href=\"\/wp-content\/uploads\/2019\/04\/jaeger_ui.jpg\"><img decoding=\"async\" class=\"aligncenter size-full wp-image-2669 lazyload\" data-src=\"\/wp-content\/uploads\/2019\/04\/jaeger_ui.jpg\" alt=\"\" width=\"1226\" height=\"991\" data-srcset=\"https:\/\/gokhan-gokalp.com\/wp-content\/uploads\/2019\/04\/jaeger_ui.jpg 1226w, https:\/\/gokhan-gokalp.com\/wp-content\/uploads\/2019\/04\/jaeger_ui-300x242.jpg 300w, https:\/\/gokhan-gokalp.com\/wp-content\/uploads\/2019\/04\/jaeger_ui-768x621.jpg 768w, https:\/\/gokhan-gokalp.com\/wp-content\/uploads\/2019\/04\/jaeger_ui-1024x828.jpg 1024w\" data-sizes=\"(max-width: 1226px) 100vw, 1226px\" src=\"data:image\/svg+xml;base64,PHN2ZyB3aWR0aD0iMSIgaGVpZ2h0PSIxIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjwvc3ZnPg==\" style=\"--smush-placeholder-width: 1226px; --smush-placeholder-aspect-ratio: 1226\/991;\" \/><\/a><\/p>\n<p>Jaeger \u00fczerindeki ak\u0131\u015fa bakarsak, bu i\u015flem <strong>4<\/strong> derinli\u011fe ve <strong>5<\/strong> adet span&#8217;a sahip. Toplam s\u00fcre\u00e7 ise <strong>29.54ms <\/strong>s\u00fcrm\u00fc\u015f. Post i\u015fleminin k\u0131r\u0131l\u0131mlara bakarsak ise, request ilgili action&#8217;dan ge\u00e7tikten sonra &#8220;<em>create-user-async<\/em>&#8221; method&#8217;una geliyor ve i\u00e7erisinde kullan\u0131c\u0131 kay\u0131t i\u015flemleri ger\u00e7ekle\u015ftiriliyor. Ard\u0131ndan ise asenkron olarak aktivasyon link&#8217;i g\u00f6nderme i\u015flemleri &#8220;<em>User.Activation.Consumer<\/em>&#8221; service&#8217;i i\u00e7erisinde ger\u00e7ekle\u015ftiriliyor.<\/p>\n<p>G\u00f6rd\u00fc\u011f\u00fcm\u00fcz gibi bu journey asenkron olarak ger\u00e7ekle\u015fiyor olmas\u0131na ra\u011fmen, bu request nerede, s\u00fcre\u00e7 nerede ne kadar zaman harc\u0131yor gibi sorulara cevap alabiliyoruz.<\/p>\n<h2>Sonu\u00e7<\/h2>\n<p>Distributed tracing ile bir developer olarak microservice mimarisi i\u00e7erisinde ko\u015fturan kodumuzu, request&#8217;in life cycle&#8217;\u0131n\u0131, debug edebilir ve optimize edebiliriz. <em>OpenTracing<\/em> API&#8217;\u0131 ile de, vendor lock-in durumuna d\u00fc\u015fmeden sistemimizin farkl\u0131 tracer&#8217;lar ile esnek bir \u015fekilde trace edilebilmesini sa\u011flayabiliyoruz. Ayr\u0131ca ben bu makalede, distributed bir yap\u0131daki sistem i\u00e7erisinde trace bilgilerinin propagation i\u015flemlerini de g\u00f6stermeye \u00e7al\u0131\u015ft\u0131m.<\/p>\n<p>Projects: <em><a href=\"https:\/\/github.com\/GokGokalp\/OpenTracing-Jaeger-Sample\" target=\"_blank\" rel=\"noopener noreferrer\">https:\/\/github.com\/GokGokalp\/OpenTracing-Jaeger-Sample<\/a><\/em><\/p>\n<h2>Referanslar<\/h2>\n<blockquote><p><em>https:\/\/github.com\/yurishkuro\/opentracing-tutorial<br \/>\nhttps:\/\/www.jaegertracing.io\/docs\/1.11\/architecture\/<br \/>\n<\/em><\/p><\/blockquote>\n","protected":false},"excerpt":{"rendered":"<p>Distributed tracing, microservice architecture&#8217;\u0131 olarak tasarlad\u0131\u011f\u0131m\u0131z sistem i\u00e7erisindeki uygulamalar\u0131m\u0131z\u0131n, nerede performans problemi ya\u015fad\u0131\u011f\u0131n\u0131 belirleyebilmemiz ve monitor edebilmemiz i\u00e7in harika bir method. Bir ba\u015fka de\u011fi\u015fle, hangi request nereye gidiyor, u\u00e7tan uca bir request ne kadar zaman harc\u0131yor gibi sorulara cevap alabilmemiz i\u00e7in implemente etmemiz gereken bir&#8230;<\/p>\n<div class=\"more-link-wrapper\"><a class=\"more-link\" href=\"https:\/\/gokhan-gokalp.com\/tr\/distributed-tracing-with-opentracing-api-of-net-core-applications-on-kubernetes\/\">Devam\u0131n\u0131 okuyun<span class=\"screen-reader-text\">Kubernetes \u00dczerindeki .NET Core Uygulamalar\u0131n\u0131n OpenTracing API ile Distributed Tracing \u0130\u015flemleri<\/span><\/a><\/div>\n","protected":false},"author":1,"featured_media":2604,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[505,68,417,434,285,368,152],"tags":[526,419,524,525,523,259,522,153],"class_list":["post-2602","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-net-core","category-architectural","category-asp-net-core","category-containerizing","category-messaging","category-microservices","category-rabbitmq","tag-net-core","tag-asp-net-core","tag-distributed-tracing","tag-event-based","tag-jaeger","tag-microservice","tag-opentracing","tag-rabbitmq","entry"],"translation":{"provider":"WPGlobus","version":"3.0.2","language":"tr","enabled_languages":["en","tr"],"languages":{"en":{"title":true,"content":true,"excerpt":false},"tr":{"title":true,"content":true,"excerpt":false}}},"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v27.3 - https:\/\/yoast.com\/product\/yoast-seo-wordpress\/ -->\n<title>Kubernetes \u00dczerindeki .NET Core Uygulamalar\u0131n\u0131n OpenTracing API ile Distributed Tracing \u0130\u015flemleri - G\u00f6khan G\u00f6kalp<\/title>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/gokhan-gokalp.com\/distributed-tracing-with-opentracing-api-of-net-core-applications-on-kubernetes\/\" \/>\n<meta property=\"og:locale\" content=\"tr_TR\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Kubernetes \u00dczerindeki .NET Core Uygulamalar\u0131n\u0131n OpenTracing API ile Distributed Tracing \u0130\u015flemleri - G\u00f6khan G\u00f6kalp\" \/>\n<meta property=\"og:url\" content=\"https:\/\/gokhan-gokalp.com\/distributed-tracing-with-opentracing-api-of-net-core-applications-on-kubernetes\/\" \/>\n<meta property=\"og:site_name\" content=\"G\u00f6khan G\u00f6kalp\" \/>\n<meta property=\"article:published_time\" content=\"2019-04-29T12:30:18+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/gokhan-gokalp.com\/wp-content\/uploads\/2019\/04\/distributed-tracing-open-tracing-dotnet-core.jpg\" \/>\n\t<meta property=\"og:image:width\" content=\"1695\" \/>\n\t<meta property=\"og:image:height\" content=\"843\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/jpeg\" \/>\n<meta name=\"author\" content=\"G\u00f6khan G\u00f6kalp\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:label1\" content=\"Yazan:\" \/>\n\t<meta name=\"twitter:data1\" content=\"G\u00f6khan G\u00f6kalp\" \/>\n\t<meta name=\"twitter:label2\" content=\"Tahmini okuma s\u00fcresi\" \/>\n\t<meta name=\"twitter:data2\" content=\"28 dakika\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\\\/\\\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\\\/\\\/gokhan-gokalp.com\\\/distributed-tracing-with-opentracing-api-of-net-core-applications-on-kubernetes\\\/#article\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/gokhan-gokalp.com\\\/distributed-tracing-with-opentracing-api-of-net-core-applications-on-kubernetes\\\/\"},\"author\":{\"name\":\"G\u00f6khan G\u00f6kalp\",\"@id\":\"https:\\\/\\\/gokhan-gokalp.com\\\/#\\\/schema\\\/person\\\/7e2a7fa98babd22a5fdae563c4b8cdbe\"},\"headline\":\"Kubernetes \u00dczerindeki .NET Core Uygulamalar\u0131n\u0131n OpenTracing API ile Distributed Tracing \u0130\u015flemleri\",\"datePublished\":\"2019-04-29T12:30:18+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\\\/\\\/gokhan-gokalp.com\\\/distributed-tracing-with-opentracing-api-of-net-core-applications-on-kubernetes\\\/\"},\"wordCount\":3758,\"commentCount\":4,\"publisher\":{\"@id\":\"https:\\\/\\\/gokhan-gokalp.com\\\/#\\\/schema\\\/person\\\/7e2a7fa98babd22a5fdae563c4b8cdbe\"},\"image\":{\"@id\":\"https:\\\/\\\/gokhan-gokalp.com\\\/distributed-tracing-with-opentracing-api-of-net-core-applications-on-kubernetes\\\/#primaryimage\"},\"thumbnailUrl\":\"https:\\\/\\\/gokhan-gokalp.com\\\/wp-content\\\/uploads\\\/2019\\\/04\\\/distributed-tracing-open-tracing-dotnet-core.jpg\",\"keywords\":[\".net core\",\"asp.net core\",\"distributed tracing\",\"event-based\",\"jaeger\",\"MicroService\",\"opentracing\",\"RabbitMQ\"],\"articleSection\":[\".NET Core\",\"Architectural\",\"ASP.NET Core\",\"Containerizing\",\"Messaging\",\"Microservices\",\"RabbitMQ\"],\"inLanguage\":\"tr\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\\\/\\\/gokhan-gokalp.com\\\/distributed-tracing-with-opentracing-api-of-net-core-applications-on-kubernetes\\\/#respond\"]}]},{\"@type\":\"WebPage\",\"@id\":\"https:\\\/\\\/gokhan-gokalp.com\\\/distributed-tracing-with-opentracing-api-of-net-core-applications-on-kubernetes\\\/\",\"url\":\"https:\\\/\\\/gokhan-gokalp.com\\\/distributed-tracing-with-opentracing-api-of-net-core-applications-on-kubernetes\\\/\",\"name\":\"Kubernetes \u00dczerindeki .NET Core Uygulamalar\u0131n\u0131n OpenTracing API ile Distributed Tracing \u0130\u015flemleri - G\u00f6khan G\u00f6kalp\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/gokhan-gokalp.com\\\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\\\/\\\/gokhan-gokalp.com\\\/distributed-tracing-with-opentracing-api-of-net-core-applications-on-kubernetes\\\/#primaryimage\"},\"image\":{\"@id\":\"https:\\\/\\\/gokhan-gokalp.com\\\/distributed-tracing-with-opentracing-api-of-net-core-applications-on-kubernetes\\\/#primaryimage\"},\"thumbnailUrl\":\"https:\\\/\\\/gokhan-gokalp.com\\\/wp-content\\\/uploads\\\/2019\\\/04\\\/distributed-tracing-open-tracing-dotnet-core.jpg\",\"datePublished\":\"2019-04-29T12:30:18+00:00\",\"breadcrumb\":{\"@id\":\"https:\\\/\\\/gokhan-gokalp.com\\\/distributed-tracing-with-opentracing-api-of-net-core-applications-on-kubernetes\\\/#breadcrumb\"},\"inLanguage\":\"tr\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\\\/\\\/gokhan-gokalp.com\\\/distributed-tracing-with-opentracing-api-of-net-core-applications-on-kubernetes\\\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"tr\",\"@id\":\"https:\\\/\\\/gokhan-gokalp.com\\\/distributed-tracing-with-opentracing-api-of-net-core-applications-on-kubernetes\\\/#primaryimage\",\"url\":\"https:\\\/\\\/gokhan-gokalp.com\\\/wp-content\\\/uploads\\\/2019\\\/04\\\/distributed-tracing-open-tracing-dotnet-core.jpg\",\"contentUrl\":\"https:\\\/\\\/gokhan-gokalp.com\\\/wp-content\\\/uploads\\\/2019\\\/04\\\/distributed-tracing-open-tracing-dotnet-core.jpg\",\"width\":1695,\"height\":843},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\\\/\\\/gokhan-gokalp.com\\\/distributed-tracing-with-opentracing-api-of-net-core-applications-on-kubernetes\\\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\\\/\\\/gokhan-gokalp.com\\\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Distributed Tracing with OpenTracing API of .NET Core Applications on Kubernetes\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\\\/\\\/gokhan-gokalp.com\\\/#website\",\"url\":\"https:\\\/\\\/gokhan-gokalp.com\\\/\",\"name\":\"G\u00f6khan G\u00f6kalp\",\"description\":\"C# &amp; Python lover\",\"publisher\":{\"@id\":\"https:\\\/\\\/gokhan-gokalp.com\\\/#\\\/schema\\\/person\\\/7e2a7fa98babd22a5fdae563c4b8cdbe\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\\\/\\\/gokhan-gokalp.com\\\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"tr\"},{\"@type\":[\"Person\",\"Organization\"],\"@id\":\"https:\\\/\\\/gokhan-gokalp.com\\\/#\\\/schema\\\/person\\\/7e2a7fa98babd22a5fdae563c4b8cdbe\",\"name\":\"G\u00f6khan G\u00f6kalp\",\"pronouns\":\"he\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"tr\",\"@id\":\"https:\\\/\\\/gokhan-gokalp.com\\\/wp-content\\\/litespeed\\\/avatar\\\/e645f66b6264ced10d7b6d8b1f85509b.jpg?ver=1776775506\",\"url\":\"https:\\\/\\\/gokhan-gokalp.com\\\/wp-content\\\/litespeed\\\/avatar\\\/e645f66b6264ced10d7b6d8b1f85509b.jpg?ver=1776775506\",\"contentUrl\":\"https:\\\/\\\/gokhan-gokalp.com\\\/wp-content\\\/litespeed\\\/avatar\\\/e645f66b6264ced10d7b6d8b1f85509b.jpg?ver=1776775506\",\"caption\":\"G\u00f6khan G\u00f6kalp\"},\"logo\":{\"@id\":\"https:\\\/\\\/gokhan-gokalp.com\\\/wp-content\\\/litespeed\\\/avatar\\\/e645f66b6264ced10d7b6d8b1f85509b.jpg?ver=1776775506\"},\"sameAs\":[\"https:\\\/\\\/gokhan-gokalp.com\"],\"url\":\"https:\\\/\\\/gokhan-gokalp.com\\\/tr\\\/author\\\/gok-gokalp\\\/\"}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"Kubernetes \u00dczerindeki .NET Core Uygulamalar\u0131n\u0131n OpenTracing API ile Distributed Tracing \u0130\u015flemleri - G\u00f6khan G\u00f6kalp","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/gokhan-gokalp.com\/distributed-tracing-with-opentracing-api-of-net-core-applications-on-kubernetes\/","og_locale":"tr_TR","og_type":"article","og_title":"Kubernetes \u00dczerindeki .NET Core Uygulamalar\u0131n\u0131n OpenTracing API ile Distributed Tracing \u0130\u015flemleri - G\u00f6khan G\u00f6kalp","og_url":"https:\/\/gokhan-gokalp.com\/distributed-tracing-with-opentracing-api-of-net-core-applications-on-kubernetes\/","og_site_name":"G\u00f6khan G\u00f6kalp","article_published_time":"2019-04-29T12:30:18+00:00","og_image":[{"width":1695,"height":843,"url":"https:\/\/gokhan-gokalp.com\/wp-content\/uploads\/2019\/04\/distributed-tracing-open-tracing-dotnet-core.jpg","type":"image\/jpeg"}],"author":"G\u00f6khan G\u00f6kalp","twitter_card":"summary_large_image","twitter_misc":{"Yazan:":"G\u00f6khan G\u00f6kalp","Tahmini okuma s\u00fcresi":"28 dakika"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/gokhan-gokalp.com\/distributed-tracing-with-opentracing-api-of-net-core-applications-on-kubernetes\/#article","isPartOf":{"@id":"https:\/\/gokhan-gokalp.com\/distributed-tracing-with-opentracing-api-of-net-core-applications-on-kubernetes\/"},"author":{"name":"G\u00f6khan G\u00f6kalp","@id":"https:\/\/gokhan-gokalp.com\/#\/schema\/person\/7e2a7fa98babd22a5fdae563c4b8cdbe"},"headline":"Kubernetes \u00dczerindeki .NET Core Uygulamalar\u0131n\u0131n OpenTracing API ile Distributed Tracing \u0130\u015flemleri","datePublished":"2019-04-29T12:30:18+00:00","mainEntityOfPage":{"@id":"https:\/\/gokhan-gokalp.com\/distributed-tracing-with-opentracing-api-of-net-core-applications-on-kubernetes\/"},"wordCount":3758,"commentCount":4,"publisher":{"@id":"https:\/\/gokhan-gokalp.com\/#\/schema\/person\/7e2a7fa98babd22a5fdae563c4b8cdbe"},"image":{"@id":"https:\/\/gokhan-gokalp.com\/distributed-tracing-with-opentracing-api-of-net-core-applications-on-kubernetes\/#primaryimage"},"thumbnailUrl":"https:\/\/gokhan-gokalp.com\/wp-content\/uploads\/2019\/04\/distributed-tracing-open-tracing-dotnet-core.jpg","keywords":[".net core","asp.net core","distributed tracing","event-based","jaeger","MicroService","opentracing","RabbitMQ"],"articleSection":[".NET Core","Architectural","ASP.NET Core","Containerizing","Messaging","Microservices","RabbitMQ"],"inLanguage":"tr","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/gokhan-gokalp.com\/distributed-tracing-with-opentracing-api-of-net-core-applications-on-kubernetes\/#respond"]}]},{"@type":"WebPage","@id":"https:\/\/gokhan-gokalp.com\/distributed-tracing-with-opentracing-api-of-net-core-applications-on-kubernetes\/","url":"https:\/\/gokhan-gokalp.com\/distributed-tracing-with-opentracing-api-of-net-core-applications-on-kubernetes\/","name":"Kubernetes \u00dczerindeki .NET Core Uygulamalar\u0131n\u0131n OpenTracing API ile Distributed Tracing \u0130\u015flemleri - G\u00f6khan G\u00f6kalp","isPartOf":{"@id":"https:\/\/gokhan-gokalp.com\/#website"},"primaryImageOfPage":{"@id":"https:\/\/gokhan-gokalp.com\/distributed-tracing-with-opentracing-api-of-net-core-applications-on-kubernetes\/#primaryimage"},"image":{"@id":"https:\/\/gokhan-gokalp.com\/distributed-tracing-with-opentracing-api-of-net-core-applications-on-kubernetes\/#primaryimage"},"thumbnailUrl":"https:\/\/gokhan-gokalp.com\/wp-content\/uploads\/2019\/04\/distributed-tracing-open-tracing-dotnet-core.jpg","datePublished":"2019-04-29T12:30:18+00:00","breadcrumb":{"@id":"https:\/\/gokhan-gokalp.com\/distributed-tracing-with-opentracing-api-of-net-core-applications-on-kubernetes\/#breadcrumb"},"inLanguage":"tr","potentialAction":[{"@type":"ReadAction","target":["https:\/\/gokhan-gokalp.com\/distributed-tracing-with-opentracing-api-of-net-core-applications-on-kubernetes\/"]}]},{"@type":"ImageObject","inLanguage":"tr","@id":"https:\/\/gokhan-gokalp.com\/distributed-tracing-with-opentracing-api-of-net-core-applications-on-kubernetes\/#primaryimage","url":"https:\/\/gokhan-gokalp.com\/wp-content\/uploads\/2019\/04\/distributed-tracing-open-tracing-dotnet-core.jpg","contentUrl":"https:\/\/gokhan-gokalp.com\/wp-content\/uploads\/2019\/04\/distributed-tracing-open-tracing-dotnet-core.jpg","width":1695,"height":843},{"@type":"BreadcrumbList","@id":"https:\/\/gokhan-gokalp.com\/distributed-tracing-with-opentracing-api-of-net-core-applications-on-kubernetes\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/gokhan-gokalp.com\/"},{"@type":"ListItem","position":2,"name":"Distributed Tracing with OpenTracing API of .NET Core Applications on Kubernetes"}]},{"@type":"WebSite","@id":"https:\/\/gokhan-gokalp.com\/#website","url":"https:\/\/gokhan-gokalp.com\/","name":"G\u00f6khan G\u00f6kalp","description":"C# &amp; Python lover","publisher":{"@id":"https:\/\/gokhan-gokalp.com\/#\/schema\/person\/7e2a7fa98babd22a5fdae563c4b8cdbe"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/gokhan-gokalp.com\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"tr"},{"@type":["Person","Organization"],"@id":"https:\/\/gokhan-gokalp.com\/#\/schema\/person\/7e2a7fa98babd22a5fdae563c4b8cdbe","name":"G\u00f6khan G\u00f6kalp","pronouns":"he","image":{"@type":"ImageObject","inLanguage":"tr","@id":"https:\/\/gokhan-gokalp.com\/wp-content\/litespeed\/avatar\/e645f66b6264ced10d7b6d8b1f85509b.jpg?ver=1776775506","url":"https:\/\/gokhan-gokalp.com\/wp-content\/litespeed\/avatar\/e645f66b6264ced10d7b6d8b1f85509b.jpg?ver=1776775506","contentUrl":"https:\/\/gokhan-gokalp.com\/wp-content\/litespeed\/avatar\/e645f66b6264ced10d7b6d8b1f85509b.jpg?ver=1776775506","caption":"G\u00f6khan G\u00f6kalp"},"logo":{"@id":"https:\/\/gokhan-gokalp.com\/wp-content\/litespeed\/avatar\/e645f66b6264ced10d7b6d8b1f85509b.jpg?ver=1776775506"},"sameAs":["https:\/\/gokhan-gokalp.com"],"url":"https:\/\/gokhan-gokalp.com\/tr\/author\/gok-gokalp\/"}]}},"_links":{"self":[{"href":"https:\/\/gokhan-gokalp.com\/tr\/wp-json\/wp\/v2\/posts\/2602","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/gokhan-gokalp.com\/tr\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/gokhan-gokalp.com\/tr\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/gokhan-gokalp.com\/tr\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/gokhan-gokalp.com\/tr\/wp-json\/wp\/v2\/comments?post=2602"}],"version-history":[{"count":38,"href":"https:\/\/gokhan-gokalp.com\/tr\/wp-json\/wp\/v2\/posts\/2602\/revisions"}],"predecessor-version":[{"id":2689,"href":"https:\/\/gokhan-gokalp.com\/tr\/wp-json\/wp\/v2\/posts\/2602\/revisions\/2689"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/gokhan-gokalp.com\/tr\/wp-json\/wp\/v2\/media\/2604"}],"wp:attachment":[{"href":"https:\/\/gokhan-gokalp.com\/tr\/wp-json\/wp\/v2\/media?parent=2602"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/gokhan-gokalp.com\/tr\/wp-json\/wp\/v2\/categories?post=2602"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/gokhan-gokalp.com\/tr\/wp-json\/wp\/v2\/tags?post=2602"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}