<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
    <channel>
        <title>نوشته های Mohammad Mahdi Tilab</title>
        <link>https://virgool.io/feed/@mohammadmahditilab</link>
        <description></description>
        <language>fa</language>
        <pubDate>2026-06-17 03:14:06</pubDate>
        <image>
            <url>https://files.virgool.io/upload/users/2088723/avatar/avatar.png?height=120&amp;width=120</url>
            <title>Mohammad Mahdi Tilab</title>
            <link>https://virgool.io/@mohammadmahditilab</link>
        </image>

                    <item>
                <title>در جاوا 8+ چرا وقتی ExecutorService هست، کلاس CompletableFuture هم وجود دارد؟</title>
                <link>https://virgool.io/@mohammadmahditilab/excecutorservice-vs-compatiblefuture-ieqwfupyfvmd</link>
                <description>یکی از سوالات چالشی همیشه در مصاحبه ها، سوالاتی مربوط به Concurrency بوده. خصوصا مربوط به ویژگی های جاوا 8 به بالا. گاها ممکنه سوال بپرسن که ExecutorService یا CompletableFuture؟ چرا؟مثال زیر رو در نظر بگیرید که دو عملیات دارید یکی برای اضافه کردن دو عدد و دیگری برای ضرب در 15:public static Integer add(int a, int b) {
	return a + b;
}
public static Integer multiply(int result) {
    return result * 15;
}در نظر بگیرید این دو عملیات رو تمایل دارید پشت سر هم انجام بدید.در مرحله اول بریم دو عملیات جمع و ضرب رو با استفاده از ExecutorService و Future ببینیم:public static void main(String[] args) throws ExecutionException, InterruptedException {	 
	ExecutorService executor = Executors.newFixedThreadPool(3); //1
	List&lt;String&gt; finalResultList = new ArrayList&lt;&gt;();
	for (int i = 0; i &lt; 10; i++) {
		Future&lt;Integer&gt; futureResult = executor.submit(new Add(10, 20)); // 2
		Integer intermediateResult = futureResult.get(); // blocking // 3
		Future&lt;Integer&gt; finalResult = executor.submit(new Multiply(intermediateResult)); // 4
		finalResultList.add(i + &amp;quot &amp;quot + finalResult.get()); // blocking //5
	}
	executor.shutdown();
}

static class Add implements Callable&lt;Integer&gt; {
	int a;
	int b;

	Add(int a, int b) {
		this.a = a;
		this.b = b;
	}

	@Override
	public Integer call() throws Exception {
		return a + b;
	}
}

static class Multiply implements Callable&lt;Integer&gt; {
	int result;
	Multiply(int result) {
		this.result = result;
	}

	@Override
	public Integer call() throws Exception {
		result = result * 10;
		System.out.println(Thread.currentThread().getName() + &amp;quot &amp;quot + result);
		return result;
	}
}بریم سراغ بررسی کد:در خط 1، یک شی از ExcutorService با تعداد Thread حداکثر 3 ایجاد کردیم.  در خط 3 برای اینکه بتونیم نتایج عملیات ضرب رو داشته باشیم مجبوریم پاسخ عملیات رو در thread main داشته باشیم که این کار رو با متد get() انجام میدیم یا اصطلاحا blocking انجام میدیم و در خط 4 نتیجه رو در thread جدیدی برای عملیات ضرب ارسال میکنیم.خروجی به صورت زیر شد:&gt; Task :ExecutorServiceSample.main()
pool-1-thread-2 300
pool-1-thread-1 300
pool-1-thread-3 300
pool-1-thread-2 300
pool-1-thread-1 300
pool-1-thread-3 300
pool-1-thread-2 300
pool-1-thread-1 300
pool-1-thread-3 300
pool-1-thread-2 300همینطور که میبینید عملیات به این صورت پیش رفته که هر thread بعد از اتمام thread بعدی کارش رو شروع و thread ها به صورت همزمان نتونستن کارشون رو شروع کنن. چون ترتیب استفاده از threadها حفط شده. در نتیجه عملیات هایی که صف ExecutorService هستند به صورت Synchronize و Blocking Queue عمل میکنند و نمیتوانند همزمان عملیات ها را انجام دهند.نحوه عملکرد  ExecutorService  که عملیات ها به ترتیب صف اجرا میشوند همانطور که در تصویر بالا میبینید ExecutorService ابتدا کار جدیدی را از صف برمیدارد و به اتمام میرساند سپس به سراغ کار بعدی می رود.در مرحله بعدی میخواهیم همین کار را با استفاده از CompletableFuture انجام دهیم:public static void main(String[] args) throws ExecutionException, InterruptedException {
	ExecutorService executor = Executors.newFixedThreadPool(3);
	List&lt;CompletableFuture&lt;Integer&gt;&gt; finalResultList = new ArrayList&lt;&gt;();

	for (int i = 0; i &lt; 10; i++)  {
        	CompletableFuture&lt;Integer&gt; voidCompletableFuture = CompletableFuture 
                .supplyAsync(() -&gt; add(10, 20), executor) //1
                .thenApplyAsync(CompletableFutureSample::multiply, executor); //2

		finalResultList.add(voidCompletableFuture);
	}

	for (CompletableFuture&lt;Integer&gt; future : finalResultList) {
        	future.get(); //3
	}
	executor.shutdown();
}

public static Integer add(int a, int b) {
	return a + b;
}

public static Integer multiply(int a) {
	a = a * 10;
	System.out.println(Thread.currentThread().getName() + &amp;quot &amp;quot + a);
	return a;
}بریم و کد رو بررسی کنیم:در خط 1 با استفاده از supplyAsync عملیات جمع به صورت async معرفی شده و با توجه به انکه ترتیب عملیات جمع و ضرب برای ما مهم هست، بعد از دریافت نتیجه، جواب رو بدون اینکه به Thread main منتقل کنیم، با استفاده از متد thenApplyAsync مستقیم به thread جدید که از ExecutorService درخواست کردیم ارسال میکنیم. نکته مهم این هستش که میتونیم به صورت همزمان چندین task رو شروع کنیم. حال ممکنه هر Task شامل چند عملیات باشه مثل ضرب و جمع.خروجی رو بینیم:pool-1-thread-1 300
pool-1-thread-1 300
pool-1-thread-3 300
pool-1-thread-1 300
pool-1-thread-3 300
pool-1-thread-1 300
pool-1-thread-3 300
pool-1-thread-1 300
pool-1-thread-3 300
pool-1-thread-2 300همانطور که میبینید ترتیب Thread ها دیگه رعایت نشده و هر Threadی که کارش تموم شده Task بعدی رو شروع کرده به انجام.تصویر زیر ببینید:نحوه اجرای Taskها در CompletableFuture که به صورت همزمان قابل اجرا است همانطور که در تصویر بالا میبینید، Task ها به صورت همزمان انجام شده اند نه به صورت Blockingبرای محتواهای بیشتر من رو در linkedIn و github دنبال کنید!شاد باشید! :-)</description>
                <category>Mohammad Mahdi Tilab</category>
                <author>Mohammad Mahdi Tilab</author>
                <pubDate>Fri, 26 May 2023 11:51:17 +0330</pubDate>
            </item>
                    <item>
                <title>ابزاری به نام Jaeger برای بهتر دیدن خطاها در معماری ماکروسرویس و استفاده از آن در Spring boot 3.0</title>
                <link>https://virgool.io/@mohammadmahditilab/%D8%A7%D8%A8%D8%B2%D8%A7%D8%B1%DB%8C-%D8%A8%D9%87-%D9%86%D8%A7%D9%85-jaeger-%D8%A8%D8%B1%D8%A7%DB%8C-%D8%A8%D9%87%D8%AA%D8%B1-%D8%AF%DB%8C%D8%AF%D9%86-%D8%AE%D8%B7%D8%A7%D9%87%D8%A7-%D8%AF%D8%B1-%D9%85%D8%B9%D9%85%D8%A7%D8%B1%DB%8C-%D9%85%D8%A7%DA%A9%D8%B1%D9%88%D8%B3%D8%B1%D9%88%DB%8C%D8%B3-%D9%88-%D8%A7%D8%B3%D8%AA%D9%81%D8%A7%D8%AF%D9%87-%D8%A7%D8%B2-%D8%A2%D9%86-%D8%AF%D8%B1-spring-boot-30-c6nugawkzlyh</link>
                <description>مقدمهتوی خیلی از پروژه های مایکروسرویس، برای هر عملیاتی که در پشت صحنه هر درخواست وبی وجود داره، ممکنه بخوایم بدونیم اون درخواست چطور، با چه پارامترهایی، در چه مدت زمانی و غیره داره انجام میشه یا زنجیره ای از عملیات های مختلف رو داریم و میخوایم بدونیم به تفکیک هر کدوم چه جزییاتی دارن. مثلا در نظر بگیرید یه معماری دارید به شکل زیر:نمونه معماری microservice به صورت چند لایههمونطور که توی تصویر بالا میبینید، ما 3 تا مایکروسرویس داریم که مایکروسرویس name-generator-service به دو سرویس دیگه درخواست ارسال میکنه. حالا برای اینکه بتونیم بهتر و سریعتر اشکال زدایی کنیم، نیاز به این داریم که یه سری لاگ بگذاریم. ممکنه مشکلات عدم دسترسی در شبکه و ارتباط بین مایکروسرویس ها یا کندی اونها باشه. حالا اگه هر سیستمی لاگ های خودش رو داشته باشه، پیدا کردن و مدیریت اونها سخت میشه. ابزارهای Distributed tracing به ما کمک میکنند تا بتونیم این مشکل رو رفع کنیم. یکی از این ابزارها Zipkin هستش و دیگری Jaeger که ما میخوایم در این مقاله به اون بپردازیم.معرفی Jaegerحالا که متوجه شدیم distributed tracing ها در معماری ماکروسرویس چه نقشی رو بازی میکنن، بریم سراغ معرفی Jaeger tracing.کلمه Jaeger در زبان آلمانی به معنای مبارز و معادل Hunter در زبان انگلیسی هستش و به عنوان یک ابزار متن باز توسط شرکت Uber معرفی شده.مدل داده ای که در Jaeger وجود داره سازگار با الگوی داده ای OpenTracing هستش. یک استاندارد برای Tracingها که ابزارهای مختلف بتونن با هم سازگاری بیشتری داشته باشن و مهاجرت از یه ابزار به ابزار دیگه راحت تر باشه. برای اطلاعات بیشتر به سایت The OpenTracing project مراجعه کنید.نکته: امروز که دارم این مقاله رو می نویسم پروژه OpenTracing داره منقضی میشه و جاش رو به OpenTelementry میده. در آینده از OpenTelementry براتون مینویسم. این موضوع ربطی به Jaeger نداره و این ابزار با هر دو پروتکل یا الگوی داده ای، سازگار هستش.پیاده سازی Jaeger Tracing در Spring boot 3.0ما فرض رو میگذاریم روی اینکه شما از ابزار Intellij Idea Ultimate استفاده نمی کنی و فقط دسترسی https://start.spring.io برای ایجاد پروژه Spring Boot داری. dependency یا ماژول وابسته &quot;Spring Web&quot; رو اضافه کنید کافیه. روی دکمه Generate کلیک کنید و کد رو دانلود کنید.اگر از Spring boot 2 به بالا استفاده می کنید وابسته زیر برای Maven کافیه: https://gist.github.com/m-tilab/f35e92525f700c323b6ff982b06c1ad0 اما اگه از Spring boot 3 به بالا استفاده میکنید، در نظر داشته باشید که با توجه به اینکه این وابسته بروز نشده نیاز دارید تا مجبور کنید ابزار build رو تا از نسخه قدیمی تر reactor-core استفاده کنه.پس برای Spring boot 3 در Maven به صورت زیر میشه: https://gist.github.com/m-tilab/798c1e96cf692c656d81e51e559ac6f8 یا معادلش در 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 شامل نام عملیات، زمان شروع و مدت هستش. span ها میتونن زیر مجموعه داشته باشن. مثلا خرید از یک فروشگاه اینترنتی رو در نظر بگیرید، API که خرید رو نهایی میکنه بایستی دو عملیات انجام بده یکی نهایی سازی وضعیت خرید و دوم کم کردن موجودی کالا از انبار، پس ما برای کل متد یه span داریم که شامل دو زیر span که هر کدوم عملیات خودشون رو دارند.گام بعدی میریم سراغ راه اندازی خود Jaeger با استفاده از docker-compose: https://gist.github.com/m-tilab/c4ba0592f685ba78c724dcd4c8e2d816 ما میتونیم از 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 رو فراخوانی کنیم: curl-ihttp://localhost:8080/service/path1 اگر به کنسول در instance1 نگاه کنیم چیزی شبیه این خواهیم داشت: https://virgool.io/p/c6nugawkzlyh/INFO69938---[nio-8080-exec-1]i.j.internal.reporters.LoggingReporter:Spanreported:ed70bbaa2bd5b42f:c7c94163fc95fc1e:ed70bbaa2bd5b42f:1-GET که شامل سه بخش 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 UIجمع بندیدر این مقاله سعی شد تا به معرفی ابزار jaeger یکپارچه سازیش با  Spring boot اشاره بشه. این ابزار میتونه بهتون برای رفع مشکلات و بررسی کارایی اپلیکیشن شما بهتون کمک کنه.در ضمن میتونید به این صفحه سر بزنید و نمونه پروژه تمامی کدها رو یکجا داشته باشید:m-tilab/springboot3-jaeger-integration (github.com)موفق باشید</description>
                <category>Mohammad Mahdi Tilab</category>
                <author>Mohammad Mahdi Tilab</author>
                <pubDate>Mon, 20 Feb 2023 16:31:53 +0330</pubDate>
            </item>
                    <item>
                <title>الگوی طراحی Parameter Object در جاوا</title>
                <link>https://virgool.io/@mohammadmahditilab/%D8%A7%D9%84%DA%AF%D9%88%DB%8C-%D8%B7%D8%B1%D8%A7%D8%AD%DB%8C-parameter-object-%D8%AF%D8%B1-%D8%AC%D8%A7%D9%88%D8%A7-ylavtztcxnju</link>
                <description>مشکلشاید براتون پیش اومده باشه که یه متدی داشته باشید که پارامترها یا argument های زیادی بگیره و این موضوع کد شما رو کثیف کنه. همونطور که میدونید متدها در جاوا میتونن حداکثر 255 پارامتر رو پوشش بدن یا بهتره بگیرم، 255 یونیت. برای درک بهتر یونیت به این لینک مراجعه کنید. شاید بگید 255 که خیلی زیاده! درسته زیاده اما من پروژه ای رو داشتم که نیاز شده بیش از بود 255 تا پارامتر رو داشته باشه. در ضمن توی خیلی از الگوهای کد تمیز نبایستی تعداد پارامترهای یه متد از 3 تا تجاوز کنه. توی کتاب #کد_تمیز از #رابرت_مارتین یا همون Uncle_Bob# هم اشاره شده این موضوع. اینم سندشراهکار: استفاده از الگوی طراحی Parameter Objectگام اول: یه کلاس داخلی (inner class) یا کلاس عمومی درست کنید که تمامی پارامترهای متد رو به عنوان ویژگی یا attribute داشته باشه. کلاس رو به صورت immutable طراحی کنید.گام دوم:برای راحتی کار میتونید یه کلاس builder هم براش بسازید تا راحت تر بتونید از این کلاس شیء جدید ایجاد کنید. حالا یا از @builder در lombok یا خودتون بسازید.گام آخر یه constructor از کلاس پارامترهای خودتون برای کلاس اصلی ایجاد کنید.در زیر یه نمونه از کد و طراحی کلاس رو آوردم:نمونه کلاس قبل از تغییرات: تو مثال زیر کلاس SearchService دو متد search داره که میخوایم با استفاده از الگوی طراحی MethodParameter اصلاحش کنیم. https://gist.github.com/m-tilab/241b72646cc83e3dda20b2f08f4e769c بعد از تغییراتتو کلاس زیر یه زیر کلاس به نام ParameterObject ایجاد شده که یه سازنده از نوع builder داره https://gist.github.com/m-tilab/ca5e5554ce096a090ca6d9c169e70e3b#file-parameter-object-design-pattern-after-java-L30-L32 حالا ما برای استفاده ازش میتونیم بنویسیم: https://gist.github.com/m-tilab/b770ce1c079fe9d0b93d58ab00cc3980 دیاگرامش رو هم میتونید اینجا ببینید:نحوه استفاده از الگوی طراحی parameter Object در جاوا شاد باشید!</description>
                <category>Mohammad Mahdi Tilab</category>
                <author>Mohammad Mahdi Tilab</author>
                <pubDate>Wed, 25 Jan 2023 20:49:35 +0330</pubDate>
            </item>
                    <item>
                <title>آموزش کار با Google Jib در جاوا</title>
                <link>https://virgool.io/@mohammadmahditilab/%D8%A2%D9%85%D9%88%D8%B2%D8%B4-%DA%A9%D8%A7%D8%B1-%D8%A8%D8%A7-google-jib-yzfywjl9kkmh</link>
                <description>آموزش کار با Google Jib در جاواسلام بچه ها،امروز میخوام در مورد یه ابزار براتون بنویسم به نام #گوگل_جیباگه با داکر کار کرده باشید میدونید که برای آماده سازی یه image نیاز هستش که از #داکر یا #داکر_کامپوز استفاده بشه. با استفاده از گوگل جیب میتونید این کار انجام بدید بدون نوشتن dockerFile  یا Docker-compose.ymlکاری که با dockerfile  یا docker-compose انجام میدادیم:کاری که با jib  انجام میدیم:برای استفاده از jib  میتونید از پلاگین مربوطه توی gradle  یا maven  استفاده کنید:نمونه کد پلاگین maven:&lt;plugin&gt;
    &lt;groupId&gt;com.google.cloud.tools&lt;/groupId&gt;
    &lt;artifactId&gt;jib-maven-plugin&lt;/artifactId&gt;
    &lt;version&gt;2.8.0&lt;/version&gt;
    &lt;configuration&gt;
      &lt;from&gt;
        &lt;image&gt;gcr.io/PROJECT/BASE_IMAGE&lt;/image&gt;
      &lt;/from&gt;
      &lt;to&gt;
        &lt;image&gt;gcr.io/PROJECT/IMAGE_NAME&lt;/image&gt;
      &lt;/to&gt;
    &lt;/configuration&gt;
  &lt;/plugin&gt;نمونه کد پلاگین gradle:jib {
	from.image = &#039;gcr.io/PROJECT/BASE_IMAGE&#039;
	to.image = &#039;gcr.io/PROJECT/IMAGE&#039;
}تو گام بعدی با اجرای کامند build مربوطه container خودتون رو می سازید:Maven:./mvnw compile jib:build -Dimage=&lt;your image, eg. gcr.io/my-project/spring-boot-jib&gt;Gradle:./gradlew jib --image=&lt;your image, eg. gcr.io/my-project/spring-boot-jib&gt;در نظر داشته باشید میتونید از هر docker registry استفاده کنید و وابسته به gcr یا docker hub نیستیدشاد باشید! :-)</description>
                <category>Mohammad Mahdi Tilab</category>
                <author>Mohammad Mahdi Tilab</author>
                <pubDate>Thu, 19 Jan 2023 21:08:39 +0330</pubDate>
            </item>
            </channel>
</rss>