Distributed Tracing di Blibli.com

Eko Kurniawan Khannedy
Blibli.com Tech Blog
5 min readJun 14, 2018

Hal yang paling sulit dilakukan saat kita membangun sistem Microservices adalah, bagaimana cara men-track sebuah request dari satu service ke service yang lain.

Bayangkan jika ada sebuah request yang datang dari user ke service A, selanjutnya service A melakukan request ke service B dan C, dan service B melakukan request ke service D, E , F dan begitu seterusnya, maka akan sangat sulit untuk di-track request tersebut.

Bagaimana di Blibli.com menyelesaikan permasalahan ini?

Cara untuk mengatasi masalah ini sebenernya cukup sederhana, namun butuh kedisiplinan untuk meng-implementasikan nya di semua service secara bersama-sama.

Tracing via API

Seperti yang pernah saya jelaskan di artikel Evolusi Backend di Blibli.com, awal Blibli.com menggunakan arsitektur microservices, kita menggunakan API First Development, dimana semua komunikasi antar service terjadi menggunakan API call.

Non Spring Boot Service

Di Blibli.com, kita sudah menggunakan arsitektur microservice, bahkan sebelum spring boot booming. Di Blibli.com, kita membuat framework yang mirip dengan spring boot yang kita beri nama X-Framework. Untuk mengatasi permasalahan diatas, di X-Framework kita memaksa agar setiap Controller wajib menerima parameter requestId dan setiap akan melakukan API Call ke service lain, requestId tersebut akan di sisipkan di query param atau http header.

Tracing via API

Dengan seperti ini, maka kita mampu men-track tiap request dari depan (API Gateway) sampai ke ujung service yang menerima request tersebut.

Lantas bagaimana cara passing requestId tersebut antar method atau function? Apakah berarti di tiap method dan function yang terdapat di dalam service butuh parameter requestId?

Yup, hal yang paling ribet adalah, kita harus memastikan requestId yang diterima Controller benar-benar diteruskan ke API Call selanjutnya. Karena X-Framework yang kita buat masih menggunakan Servlet API yang blocking. Jadi caranya cukup sederhana, kita hanya menggunakan ThreadLocal untuk menyimpan requestId tersebut, dengan demikian dimanapun kita butuh requestId, di service, di repository atau di sdk client, kita bisa dengan mudah mendapatkannya dari ThreadLocal.

Namun hal ini tidak bisa dilakukan dengan mudah jika aplikasi kita sudah Reactive atau Non-Blocking. Lantas bagaimana cara menyelesaikan masalah ini?

Spring Boot Service

Sejak tahun 2016, ketika Spring Boot sudah mulai stabil dan booming, kita mulai migrasi dari X-Framework ke Spring Boot, bukan rewrite existing service, melainkan jika terdapat service baru maka kita akan buat menggunakan Spring Boot. Beberapa service pun ada yang dimigrasi dari X-Framework ke Spring Boot, tergantung dengan collab team nya masing-masing yang memegang service tersebut.

Service yang dibuat menggunakan Spring Boot, di Blibli.com rata-rata sudah menggunakan Reactive Programming dan Non Blocking, menggunakan Servlet API Async dan RxJava. Dengan demikian, solusi requestId memanfaatkan ThreadLocal sudah tidak relevan lagi, karena reactive code tidak bisa menggunakan ThreadLocal untuk menyimpan data requestId-nya, karena tiap method mungkin saja di run di thread yang berbeda.

Di Spring Boot service, kita tidak membuat library baru untuk distributed tracing, kita menggunakan Spring Cloud Sleuth. Karena Spring Sleuth telah mendukung integrasi dengan reactive library seperti RxJava dan Reactor, maka kita putuskan untuk menggunakan Spring Sleuth. Cara kerjanya hampir mirip dengan requestId yang kita buat, bedanya yang dikirim antar service berupa span, yang tidak hanya berisikan requestId, tapi banyak lagi, termasuk terdapat baggage yang bisa kita isi secara custom. Hal ini sanggat bermanfaat jika kita ingin mengirim value antar service diluar requestId.

Tracing via API menggunakan Spring Sleuth

Bagaimana jika Spring Boot service tersebut butuh melakukan API Call ke X-Framework service? Mudah saja, kita hanya menggambil data span id nya, lalu mengirim ke parameter request id untuk X-Framework service tersebut. Sehingga tidak ada perubahan yang harus dilakukan di X-Framework service yang sudah ada.

Tracing via Event

Saat ini, di Blibli.com, kita mulai melakukan migrasi dari API First Development ke Event First Development. Dimana semua integrasi dilakukan melalu event. Permasalahan terjadi lagi disini, bagaimana cara men-track request dari satu service ke service yang lain, jika menggunakan event?

Sayangnya, Spring Sleuth tidak mendukung integrasi menggunakan Message Broker, seperti Kafka atau RabbitMQ. Lantas bagaimana kita menyelesaikan masalah ini?

Kita tidak menggunakan framework baru atau membuat framework baru untuk menyelesaikan masalah ini, kita tetap menggunakan Spring Sleuth, hanya saja kita meng-improve kemampuan Spring Sleuth agar bisa terintegrasi melalui Message Broker, dimana di Blibli.com, utamanya kita menggunakan Apache Kafka.

Cara kerja Spring Sleuth sebenarnya sederhana, tiap ada API Call ke service lain, dia akan menyimpan semua data span nya pada HTTP header yang akan dikirimkan ke service lain. Di service lain, Spring Sleuth akan menangkap isi span yang dikirim melalui HTTP header, dan meneruskan span tersebut ke service selanjutnya jika memang terdapat API Call ke service selanjutnya.

Dari situ, berarti yang hanya kita perlukan adalah; sebelum kita mengirim event ke Message Broker, kita perlu menyisipkan data span tersebut ke event-nya, lalu di service yang menerima event tersebut, kita hanya perlu mengambil nilai span tersebut yang terdapat pada event. Sesederhana itu.

Tracing via Event menggunakan Spring Cloud Sleuth

Di Blibli.com, kita menggunakan Spring Kafka untuk integrasi ke Apache Kafka. Untuk mengimplementasikan hal diatas di semua Spring Boot Service, bukanlah hal yang bijak, karena akan banyak sekali yang harus kita ubah, dan perubahannya pun sama saja di tiap service. Oleh karena itu, kita buat plugin untuk Spring Kafka, dengan begitu, Spring Boot Project hanya butuh menggunakan plugin tersebut, dan secara otomatis urusan untuk meng-inject span ke event dan meng-extract span dari event akan diurus oleh kafka plugin tersebut.

Caranya pun sederhana, karena kita menggunakan format JSON sebagai payload event yang dikirim ke Kafka, jadi kita hanya butuh menyisipkan sebuah field untuk span-nya. Bisa Anda lihat di code disini :

Isi Event Object yang dikirim menggunakan Kafka Plugin

Semua isi span, dari id dan baggages disisipkan di event nya, dengan demikian, tak hanya requestId yang bisa kita teruskan ke service selanjutnya, tapi data yang lain pun bisa kita masukkan ke baggages untuk diteruskan ke service-service selanjutnya.

Kesimpulan

Dengan menggunakan Spring Sleuth dan Kafka Plugin, kita sudah dapat melakukan distributed tracing dengan mudah di Blibli.com, baik itu untuk distributed tracing melalui API dan juga melalui Event.

Tertantang untuk berkolaborasi menjadi Backend Engineer di Blibli.com Jakarta atau Bandung? Anda bisa hubungi saya Eko Kurniawan Khannedy via email eko.k.khannedy@gdn-commerce.com.

Referensi

--

--