توی خیلی از پروژه های مایکروسرویس، برای هر عملیاتی که در پشت صحنه هر درخواست وبی وجود داره، ممکنه بخوایم بدونیم اون درخواست چطور، با چه پارامترهایی، در چه مدت زمانی و غیره داره انجام میشه یا زنجیره ای از عملیات های مختلف رو داریم و میخوایم بدونیم به تفکیک هر کدوم چه جزییاتی دارن. مثلا در نظر بگیرید یه معماری دارید به شکل زیر:
همونطور که توی تصویر بالا میبینید، ما 3 تا مایکروسرویس داریم که مایکروسرویس name-generator-service به دو سرویس دیگه درخواست ارسال میکنه. حالا برای اینکه بتونیم بهتر و سریعتر اشکال زدایی کنیم، نیاز به این داریم که یه سری لاگ بگذاریم. ممکنه مشکلات عدم دسترسی در شبکه و ارتباط بین مایکروسرویس ها یا کندی اونها باشه. حالا اگه هر سیستمی لاگ های خودش رو داشته باشه، پیدا کردن و مدیریت اونها سخت میشه.
ابزارهای Distributed tracing به ما کمک میکنند تا بتونیم این مشکل رو رفع کنیم. یکی از این ابزارها Zipkin هستش و دیگری Jaeger که ما میخوایم در این مقاله به اون بپردازیم.
حالا که متوجه شدیم distributed tracing ها در معماری ماکروسرویس چه نقشی رو بازی میکنن، بریم سراغ معرفی Jaeger tracing.
کلمه Jaeger در زبان آلمانی به معنای مبارز و معادل Hunter در زبان انگلیسی هستش و به عنوان یک ابزار متن باز توسط شرکت Uber معرفی شده.
مدل داده ای که در Jaeger وجود داره سازگار با الگوی داده ای OpenTracing هستش. یک استاندارد برای Tracingها که ابزارهای مختلف بتونن با هم سازگاری بیشتری داشته باشن و مهاجرت از یه ابزار به ابزار دیگه راحت تر باشه. برای اطلاعات بیشتر به سایت The OpenTracing project مراجعه کنید.
نکته: امروز که دارم این مقاله رو می نویسم پروژه OpenTracing داره منقضی میشه و جاش رو به OpenTelementry میده. در آینده از OpenTelementry براتون مینویسم. این موضوع ربطی به Jaeger نداره و این ابزار با هر دو پروتکل یا الگوی داده ای، سازگار هستش.
ما فرض رو میگذاریم روی اینکه شما از ابزار Intellij Idea Ultimate استفاده نمی کنی و فقط دسترسی https://start.spring.io برای ایجاد پروژه Spring Boot داری. dependency یا ماژول وابسته "Spring Web" رو اضافه کنید کافیه. روی دکمه Generate کلیک کنید و کد رو دانلود کنید.
اگر از Spring boot 2 به بالا استفاده می کنید وابسته زیر برای Maven کافیه:
اما اگه از Spring boot 3 به بالا استفاده میکنید، در نظر داشته باشید که با توجه به اینکه این وابسته بروز نشده نیاز دارید تا مجبور کنید ابزار build رو تا از نسخه قدیمی تر reactor-core استفاده کنه.
پس برای Spring boot 3 در Maven به صورت زیر میشه:
یا معادلش در Gradle به صورت زیر هستش:
https://gist.github.com/m-tilab/01643098626121544ebd6c7969194b5f
در کد بالا یاد گرفتیم که چطور از یک نسخه خاصی از یک ماژول زیر وابسته (sub-dependency) استفاده کنیم.
در مرحله بعد یک کنترلر ایجاد میکنیم:
https://gist.github.com/m-tilab/d769719c10b2f1ae4b03a6ec341e8061
همونطور که میبینید دو مسیر به آدرس های /path1 و /path2 داریم که میتونیم این اپلیکیشن رو با دو پورت مختلف یکی با 8080 و دیگری با 8090 استارت کنیم و دو تا instance ازش داشته باشیم که /path1 در instance1 با استفاده از RestTemplate مسیر /path2 رو در instance2 فراخوانی کرده.
هر span شامل نام عملیات، زمان شروع و مدت هستش. span ها میتونن زیر مجموعه داشته باشن. مثلا خرید از یک فروشگاه اینترنتی رو در نظر بگیرید، API که خرید رو نهایی میکنه بایستی دو عملیات انجام بده یکی نهایی سازی وضعیت خرید و دوم کم کردن موجودی کالا از انبار، پس ما برای کل متد یه span داریم که شامل دو زیر span که هر کدوم عملیات خودشون رو دارند.
گام بعدی میریم سراغ راه اندازی خود Jaeger با استفاده از docker-compose:
ما میتونیم از Jaeger روی پروتکل های UDP و TCP استفاده کنیم. به صورت پیش فرض پنل ادمین Jaeger روی پورت 16686 هستش پس میتونید با آدرس http://localhost:16686/ بهش دسترسی داشته باشید.
گام بعدی اتصال اپلیکیشن Spring boot به Jaeger هستش که به صورت زیر در application.yml بایستی تنظیم بشه:
https://gist.github.com/m-tilab/5636f0619cbc2a46e74887c096896fc5
حالا بریم برای تست دو اپلیکیشن رو بالا میاریم:
https://gist.github.com/m-tilab/4d0ba6e2bd185f6a8675802c58246eca
حالا میتونیم با استفاده از CURL آدرس instance1 رو فراخوانی کنیم:
اگر به کنسول در instance1 نگاه کنیم چیزی شبیه این خواهیم داشت:
که شامل سه بخش Root Span Id, Current Span Id, Parent Span Id هستش. حالا اگر به کنسول instance2 نگاه کنیم لاگ زیر رو خواهیم داشت:
INFO 69885 --- [nio-8090-exec-1] i.j.internal.reporters.LoggingReporter : Span reported: ed70bbaa2bd5b42f:e9060cb1d5336c55:c7c94163fc95fc1e:1 - path2
همونطور که میبینید دو مقدار مشترک بین instance2 و instance1 هستش.
در ضمن میتونید موارد بالا رو هم در UI Jaeger هم مشاهده کنید:
در این مقاله سعی شد تا به معرفی ابزار jaeger یکپارچه سازیش با Spring boot اشاره بشه. این ابزار میتونه بهتون برای رفع مشکلات و بررسی کارایی اپلیکیشن شما بهتون کمک کنه.
در ضمن میتونید به این صفحه سر بزنید و نمونه پروژه تمامی کدها رو یکجا داشته باشید:
m-tilab/springboot3-jaeger-integration (github.com)
موفق باشید