Go uygulamalarını New Relic ile takip etmek

Onur Destanoğlu
Trendyol Tech
Published in
5 min readMay 27, 2020

Trendyol teknoloji ekibi bünyesindeki takımlar, mikroservis mimarisini kullanıyor olmanın da getirmiş olduğu bir serbestlik sayesinde her bir servisi, işin gerektirdiği teknolojik gereksinimleri sağlayacak doğru araçları kullanarak geliştirmektedir. Bu nedenle çok çeşitli programlama dilleri ve veritabanı sistemlerinin kullanıldığı Trendyol teknoloji ekibinde, siparişlerin takibinden sorumlu olan sipariş yönetim sistemi (OMS) takımı olarak biz de Go, Java, Kotlin gibi programlama dilleri ile Couchbase, Redis, Elasticsearch, Cassandra ve PostgreSQL gibi depolama çözümlerini kullanmaktayız.

Her mikroservis yapısının temel sorunu olan izlenebilirlik probleminin çözümünde ise, OMS takımı olarak EFK (Elasticsearch, Fluentd ve Kibana) yapısı yardımı ile log analizi yaparken, New Relic uygulaması üzerinden de servislerimizin anlık durumlarının takibini gerçekleştirmekteyiz.

Servislerimizin çoğunluğunun yazıldığı dil olan Java ve JVM tabanlı dillerin tam uyumlu olarak çalıştığı New Relic, ne yazık ki Go dilini tercih ettiğimiz servislerde ek ayarlara ihtiyaç duymaktadır. Bir Java uygulamasının herhangi bir endpointini ve ihtiyaç duyduğu dış servis/veritabanı erişimlerini zahmetsiz bir şekilde takip edebilirken, Go uygulamalarında detaylı takip etmek istediğimiz her bir endpoint için kodlama yapmak gerekmektedir.

Bu kısa yazımda Go uygulamalarımızı New Relic üzerinden izleyebilmek için yaptığımız geliştirmeleri ve eksiklerimizi nasıl giderdiğimizi paylaşmak istedim.

Go uygulamalarının New Relic üzerinden izlenebilmesi için temel olarak şu kütüphanelere ihtiyaç duyduk:

github.com/newrelic/go-agent/v3 v3.4.0
github.com/newrelic/go-agent/v3/integrations/nrgin v1.0.0

Bu kütüphanenin Http sunucusuna (bizim tercihimiz Gin) eklenmesi ile birlikte her bir http endpointin izleme kaydının oluşturulması otomatik olarak gerçekleştirilmektedir. Ancak Java uygulamalarında olduğu gibi endpointin işlevini yerine getirebilmek için gerekli olan alt işlemlerin izlenmesi ne yazık ki çeşitli kod parçalarının eklenmesi ile gerçekleşmektedir. Bunu sağlamak amacıyla ilk olarak New Relic kütüphanesini oluşturduğumuz tüm servislere aktararak, izleme kaydının oluşturulmasını istediğimiz her bir işlem metoduna şu satırları ekledik.

txn := repo.agent.StartTransaction("Inner operation...")
defer txn.End()

Bu işlemin sonucunda gerçekleştirdiğimiz bir yük testinin New Relic üzerinden izlerken genel görünüm ekranı şu şekildeydi.

Overview Ekranı

Java uygulamalarından alışık olduğumuz veritabanı, dış servis gibi faktörleri ne yazık ki bu ekranda görünmüyor ve tüm işlem “Go” etiketi altında gösterilmekteydi. Bu hali ile servisin doğru bir şekilde izlenmesi ve olası bir darboğazın kaynağının kolaylıkla tespiti mümkün değildi.

Ayrıca işlem takibi ekranında ise sadece endpointlerimizin değil tüm dış sistem erişimlerimizin de işlem kayıtları bulunmaktaydı. Bu durum, endpointlerimizin oluşturduğu işlem kayıtlarının hiyerarşik bir yapıda oluşturulmadığını göstermekteydi. Herhangi bir endpoint izinin detayına baktığımızda da bunu görebilmekteydik.

İzlenen bir işlemin detayı

Bu durumda daha iyi bir takip gerçekleştirebilmek için dış sistem erişimlerinin ayrı bir işlem olarak değil, zaten Gin context’inde her bir endpoint çağrısı esnasında otomatik olarak oluşturulan ana işlemin alt kırılımı olarak oluşturmamız gerektiğini fark ettik.

Bu nedenle harici http çağrısı yapılan yerlerde şu kod bloğu ile harici bir kırılım oluşturduk. Her ne kadar NewRelic Go kütüphanesinde dış servis işlemlerinin izlenmesi için hazır bir yapı (StartExternalSegment) varsa da, kullandığımız REST kütüphanesinin HttpRequest nesnesine erişim gerektirdiğinden bu yapıyı kullanmayı tercih etmedik.

transaction := ctx.Value(utils.TracerKey)
if transaction != nil {
externalSegment := newrelic.ExternalSegment{
URL: externalUri,
Host: a.address,
}
externalSegment.StartTime = transaction
(*newrelic.Transaction).StartSegmentNow()
defer externalSegment.End()
}

Veritabanı erişimlerinde ise yine NewRelic kütüphanesinin hazır bir yapısı olan DatastoreSegment’i kullandık. Bu yapı ile yaptığımız her bir veritabanı sorgusunun detayı ile izlenebilmesini ve böylelikle olası sorunlu yapıların tespit edilebilmesini amaçladık.

transaction := ctx.Value(utils.TracerKey)
if transaction != nil {
segment := newrelic.DatastoreSegment{
Product: DatastoreName,
Collection: BucketName,
Operation: "CustomDBOperation",
ParameterizedQuery: statement,
QueryParameters: map[string]interface{}{
"parameter_1": parameter_1,
"parameter_2": parameter_2,
"parameter_3": parameter_3,
},
}
segment.StartTime = transaction
(*newrelic.Transaction).StartSegmentNow()
defer segment.End()
}

Bu geliştirmelerden sonra elde ettiğimiz takip ekranları, Java uygulamalarında görmeye alıştığımız haline kavuşmuş oldu. Giriş niteliğindeki genel durum ekranında “Go” etiketinin yanında, dış servis çağrıları (“Web external”) ve farklı veritabanı erişimleri de (“Redis”, “Couchbase”) görünmeye başladı. Bu durum anlık darboğazların kaynağını basit bir şekilde işaret etmek adına son derece faydalı bir kazanım sağladı.

Overview Ekranı

Her bir işlem için oluşturulan detay ekranında ise, işleme bağlı olarak gerçekleştirilen alt işlemler hiyerarşik bir şekilde gösterilmeye başladı. Bu yapı işlem özelinde sorunlu noktaların takibinin yapılmasını ve genel performans iyileştirme olasılıklarının tespit edilebilmesini sağladı.

İşlem Detay Ekranı

İlk izleme talebimizde erişemediğimiz NewRelic veritabanı analiz sekmesi ise bu kez tüm veritabanı erişimlerimiz için ayrı analizler yapılabilecek bir hal aldı. Beklenenden yavaş koşturulan her bir veritabanı sorgusunun parametreleri ile birlikte gösterimi ile sorun analizi yapma konusunda ciddi kazanımlar elde etmiş olduk.

Veritabanı erişim performans gösterimi
Veritabanı sorgu detayı

NewRelic kullanarak Go uygulamarını izlemenin getirdiği bir diğer kazanım ise CPU ve hafıza kaynaklarının anlık kullanımlarını ve aktif GoRoutine kullanım durumunu gösteren Go sekmesi oldu. Bu sekme aracılığı ile hafıza ve GoRoutine leaklerinin tespiti kolaylıkla yapılarak, Go gibi çok az hafıza kullanan bir dilde ancak uzun süre çalışma sonunda fark edilebilecek problemlerin tespiti kolaylıkla ve hızlı bir şekilde yapabilme imkanı bulduk. ( ve evet bir GoRoutine leak tespit ettik :) )

Go runtime performans analiz ekranı

Sizler de problemleri, çözüm için gerekli olan araçlar ile çözebilme serbestliğinde ve en önemlisi yüksek performans ve ölçekleme gerektiren sistemler ile çalışmak istiyorsanız Trendyol insan kaynakları departmanı ile ya da doğrudan bizlerle iletişime geçebilirsiniz.

--

--

Onur Destanoğlu
Trendyol Tech

Working as Head of Engineering at Trendyol Order Management Team