<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
    <channel>
        <title>نوشته های Amir Shokri</title>
        <link>https://virgool.io/feed/@amirsh71</link>
        <description>توسعه دهنده ی جاوا</description>
        <language>fa</language>
        <pubDate>2026-06-16 20:24:17</pubDate>
        <image>
            <url>https://files.virgool.io/upload/users/83457/avatar/7BhjCS.jpeg?height=120&amp;width=120</url>
            <title>Amir Shokri</title>
            <link>https://virgool.io/@amirsh71</link>
        </image>

                    <item>
                <title>بخش دوم (فرانت اند)- Continuous Deployment ساده برای خود راه اندازی کنید!!</title>
                <link>https://virgool.io/@amirsh71/%D8%A8%D8%AE%D8%B4-%D8%AF%D9%88%D9%85-%D9%81%D8%B1%D8%A7%D9%86%D8%AA-%D8%A7%D9%86%D8%AF-continuous-deployment-%D8%B3%D8%A7%D8%AF%D9%87-%D8%A8%D8%B1%D8%A7%DB%8C-%D8%AE%D9%88%D8%AF-%D8%B1%D8%A7%D9%87-%D8%A7%D9%86%D8%AF%D8%A7%D8%B2%DB%8C-%DA%A9%D9%86%DB%8C%D8%AF-fitgdjf9laiz</link>
                <description>قبلا، توی پست (Continuous Deployment ساده برای خود راه اندازی کنید!!) در مورد چگونگی راه اندازی یک CD سبک و جمع و جور و شخصی مطلبی منتشر کردم، که خلاصش این میشه که میشه با استفاده از قابلیت هوک گیت (Git hooks) و Build Tool هایی مثل Apache Maven کاری کرد، که وقتی توی مرحله دولوپ توی IDE خواستید نسخه جدید روی سرور قرار بدید، لازم نباشه، هربار پروژه رو کامپایل کنید و بصورت دستی فایل کامپایل شده رو به سرور منتقل کنید و اپلیکیشن قبلی رو قطع کنید و اپلیکشن جدید رو ران کنید...توی پست قبلی فقط چگونگی اتوماتیک کردن این ماجرا برای بک اند رو گفتم، هرچند اگه Front-end هم با پروژه ی Back-end یکی باشه و بصورت static فایل داخل jar بک اند قرار بگیره، میتونیم با تنظیمات و پلاگین های خاصی موقع بیلد کردن پروژه روی سرور (بعد از push) کاری کنیم که قسمت front هم بیلد شه و نیازی مطالب این پست نباشه.هرچند علت اصلی نوشتن این پست ها و راه اندازی این CD ساده و سبک، این هستش که ما سرورمون ریسورس های کمی داره و نمیتونیم از ابزار های قدرتمند CI/CD استفاده کنیم و به همین دلیل معمولا امکان build پروژه های جاوااسکریپتی روی سرور با ریسورس (RAM) پایین وجود نداره.به همین خاطر، ترجیح بر اینه که بیلد کردن Front پروژه توی سیستم خود دولوپر انجام بشه و با کمترین عملیات و زمان و ترافیک مصرفی نسخه Front روی سرور آپدیت بشه.خب بریم سر اصل ماجرا...در قدم اول بهتره که deployment  فرانت و بک از هم جدا باشه، من از nginx برای deploy کردن فرانت استفاده کردم.پس بروی سرور nginx رو نصب و راه اندازی می کنیم:sudo apt updatesudo apt install nginxبعد از نصب nginx باید تنظیمات site در صورت نیاز انجام بشه و اگه نیازه reverse-proxy ها ست بشن که رکوئست های مربوط به Back-end به ip/port بک اندمون منتقل بشن.ما فرضمون بر اینکه که محل قرار گیری فایل های سرور nginx توی مسیر /var/www/html/ هستشفرض دوم این هستش که از ویندوز و power-shell استفاده می کنیم. اگه از لینوکس یا سیستم دیگه ای استفاده می کنید باید تغییرات لازم رو انجام بدید.فرض سوم این هستش که نرم افزار PUTTY رو روی ویندوز نصب کردید. که همراهش tools های دیگه هم مثل plink و pscp و puttygen نصب شده.برای اتصال ssh به سرور با private-key و public-key و اینکه مجبور نباشیم هرسری password وارد کنیم لازمه که با نرم افزار puttygen که همراه PUTTY نصب شده، جفت کلید مخصوص خودمون رو تولید کنیم. توضیحات انجام این کار اینجا گفته شده:Use SSH Keys With PuTTY On Windowsبصورت خلاصه اینطور میشه که اول باید جفت کلید رو با این نرم افزار تولید کنید، و بعد private-key رو توی سیستم خودتون ذخیره کنید و public-key رو روی سرور ست کنید. و بعد توی درخواست هایی که بعدا با putty به سرور ارسال می کنیم لازمه مسیر private-key رو به دستورات اضافه کنیم تا درخواست هامون sign بشن و توسط سرور شناسایی بشن. (اینجا هم فرض کردیم که کلید خصوصی رو با اسم privateKey.ppk  ذخیره کردید)خب حالا باید اسکرپیتی بنویسیم تا مراحل زیر رو واسمون به ترتیب و بصورت اتوماتیک انجام بده.1- دستور بیلد فرانت با webpack یا هرچیز مشابه دیگه انجام بشه و کد فرانت بیلد بشه2- فایل های بیلد شده رو zip کنیم تا حجمش کمتر بشه و زمان انتقالش هم کاهش پیدا کنه3- فایل zip رو با ssh به سرور منتقل کنیم و بروی سرور extract کنیم.cd [PATH_TO_PROEJCT]
npm run webpack:prod
cd [PATH_TO_PROEJCT]\target\classes\static
jar -cMf static.zip ./
Start-Process &#039;C:\PUTTY_PATH\pscp.exe&#039; -ArgumentList (&amp;quot-scp -i [PATH_TO_KEY]\privateKey.ppk [PATH_TO_PROEJCT]\target\classes\static\static.zip [USER_NAME]@[SERVER_IP]:/var/www/html/&amp;quot)
plink -batch -ssh -i &amp;quot[PATH_TO_KEY]\privateKey.ppk&amp;quot [USER_NAME]@[SERVER_IP] &amp;quotcd /var/www/html; jar xvf static.zip&amp;quotبا اجرای این اسکرپیت بصورت اتوماتیک پروژه فرانت بیلد میشه، زیپ میشه، به سرور انتقال پیدا میکنه، و توی مسیر مورد نظر توی سرور nginx قرار داده می شه و nginx هم بصورت اتوماتیک سرور رو آپدیت میکنه.</description>
                <category>Amir Shokri</category>
                <author>Amir Shokri</author>
                <pubDate>Thu, 11 Feb 2021 12:43:55 +0330</pubDate>
            </item>
                    <item>
                <title>Handle Unhandled Exceptions مدیریت خطاهای مدیریت نشده</title>
                <link>https://virgool.io/javacup/handle-unhandled-exceptions-in-apis-xl56zudmolfb</link>
                <description>خطاهای 500 یا سرور نرم افزار شما چگونه به کاربر می رسند؟ آیا مدیریت می شوند؟ کاربر چگونه این خطاها را به مسئول سیستم شما گزارش می کند؟ آیا مسئول سیستم و توسعه دهندگان به راحتی می توانند مشکل تشخیص دهند و آن را برطرف کنند؟پیاده سازی و ساخت Back-end یک نرم افزار و ایجاد API هایی برای سرویس دهی به سایر برنامه ها شامل در نظر گرفتن جنبه های مختلفی می باشد، از جمله:معماری و طرح کلی نرم افزار Architecture and Design Patternsارتباط با دیتابیس Persistence Layerبیزینس لاجیک Business Logicامنیت نرم افزار، احراز هویت و احراز دسترسی Authentication &amp; Authorizationارتباط با سایر سرویس هاارتباط با فایلایجاد API هامانیتورینگ و بررسی سلامت نرم افزار Monitoring &amp; Health Checksتست های اتوماتیکبیلد و نسخه گذاری اتوماتیک CI/CDکارایی نرم افزار Performanceمدیریت تراکنش ها Transaction Managementجاب ها Job Schedulingوب سوکت WebSocketو اما مورد بحث این پست:مدیریت خطاهامدیریت خطاهافارغ از زبان ها، تکنولوژی ها و کتابخانه های استفاده شده، نهایتا کاربر استفاده کننده از API های ما به خطاهای مختلفی برخورد خواهد کرد، می توان این خطاها رو به چند دسته ی کلی زیر تقسیم بندی کرد:خطاهای بیزینسی؛ به عنوان مثال: موجودی حساب شما کافی نیست.خطاهای استفاده ی نادرست؛ به عنوان مثال: ورودی های API به شکل درست ارسال نمی شوند.خطاهای امنیتی؛ به عنوان مثال: شما به این سرویس دسترسی ندارید.و اما مورد بحث این پست:خطاهای مدیریت نشده؛ یا خطاهای سرور 5XXاین دسته از خطاها ممکن است به علت ایراد در برنامه، کدنویسی، منطق و یا ارتباط با سایر سرویس ها ایجاد شوند. با اینکه به عنوان یک دولوپر باید سعی شود که هیچ خطایی از این دسته به کاربر نهایی نرسد، اما جلوگیری کامل از این اتفاق به نظر گریزناپذیر می آید.معمولا هنگام رخداد این گونه خطاها پیامی از سرور ارسال می شود تا کاربر متوجه شود که خطایی رخ داده است و باید با مسئول یا پشیتبان سیستم تماس بگیرد.نمونه خطای پیش فرض مرورگر هنگام مواجه با خطای 500 فرض کنید نرم افزار در محیط پروداکشن اجرا شده است و هزاران کاربر همزمان در حال استفاده از آن هستند. کاربری با خطای 500 مواجه می شود و با مسئول سیستم تماس می گیرد و مشکل را گزارش می کند.ایراد این روش آن است که مسئول سیستم چگونه ایراد را پیدا کند!؟ آن هم بین هزاران خط لاگ برنامه که هر لحظه بر تعداد آن ها افزوده می شود. حتی اگر سطح لاگ برنامه نیز مدیریت شود تا لاگهای با سطح حساسیت بیشتری ثبت شوند، باز هم ممکن است چندین کاربر همزمان به خطاهای 500 مختلف بربخورند و تشخیص و تعیین اینکه چه لاگ خطایی مربوط به چه کاربری بوده امری دشوار می باشد.پیدا کردن لاگ خطای مورد نظر بین هزاران خط لاگ برنامه یا تعیین لاگ مربوط به خطای گزارش شده بین هزاران لاگ خطا امری بسیار دشوار است.معرفی روش پیشنهادیبصورت خلاصه روش پیشنهادی بدین صورت است که در لایه ای از برنامه که خطای مورد نظر به HTTP Response تبدیل می شود، علاوه بر ثبت لاگ خطا، کدی کوتاه و تاحدودی یکتا تولید شود که علاوه بر اینکه همراه خطا لاگ می شود، به عنوان کد رهگیری به کاربر نیز ارسال شود.در این حالت کاربر کدرهگیری تولید شده را به مسئول سیستم گزارش می کند، و مسئول سیستم نیز با داشتن این کد به راحتی لاگ خطای مورد نظر را بین هزاران خط لاگ خطای برنامه پیدا خواهد کرد و تیم توسعه بسیار سریع تر می تواند مشکل را برطرف کند.نمونه پیاده سازی در Spring Bootدر اینجا نمونه ای از چگونگی پیاده سازی این روش پیشنهادی در Spring Boot آورده می شود:در کلاسی با نام فرضی ApplicationExceptionAdvice که با @ControllerAdvice مشخص شده است تا خطاهای ایجاد شده در برنامه، مدیریت Handle شوند، handler مربوط به خطای کلی Exception ایجاد می شود:12345678910@ControllerAdvice
public abstract class ApplicationExceptionAdvice  {
    protected final Logger LOGGER = LoggerFactory.getLogger(this.getClass());
    @ExceptionHandler(Exception.class)
    public ResponseEntity&lt;?&gt; handle(Exception ex) {
        int randomTrackingNo = RandomUtils.nextInt(1000, 9999);
        LOGGER.error(&amp;quottrackingNo:{}, {} exception occur: {}&amp;quot, randomTrackingNo, ex.getClass().getSimpleName(), ex);
        return new ResponseEntity(&amp;quotsystem.unHandled.exception: &amp;quot+randomTrackingNo, HttpStatus.INTERNAL_SERVER_ERROR);
    }
}در این کلاس handler خطاهای دیگر نیز می توانند وجود داشته باشند، و هرخطای دیگری که handler آن وجود نداشته باشد، توسط این handler مدیریت خواهند شد. ابتدا کدی 4 رقمی بصورت رندوم تولید می شود تا علاوه براینکه احتمال تکرار آن در یک روز کم شود، به یاد سپاری آن برای کاربر و مسئول سیستم نیز آسان باشد. در صورت نیاز می توانید از sequence جهت ایجاد کد رهگیری یکتا استفاده کنید.پس از تولید این کد لاگ خطا همراه کد رهگیری ثبت می گردد و کاربر نیز از وقوع خطای پیش بینی نشده به همراه کد رهگیری ای که می بایست به مسئول سیستم گزارش کند، آگاه می شود.لطفا نظر خودتون رو در مورد این روش، یا روش های پیشنهادی خودتون همینجا بیان کنید.</description>
                <category>Amir Shokri</category>
                <author>Amir Shokri</author>
                <pubDate>Sat, 01 Aug 2020 07:49:54 +0430</pubDate>
            </item>
                    <item>
                <title>Continuous Deployment ساده برای خود راه اندازی کنید!!</title>
                <link>https://virgool.io/javacup/simple-git-continuous-deployment-uhs0rhneyle6</link>
                <description>فرایند انتشار نسخه و نصب و راه اندازی آن در سرور مخصوصا در مرحله ی توسعه ی نرم افزار، کاری تکراری و زمان بر است که توسعه دهنده با هر تغییر در سورس کد می بایست تمام مراحل را تکرار کند، بدین صورت که بعد از تغییر مورد نظر کد را کامپایل کرده، به سرور منتقل کند، نسخه ی اجرایی قبلی را متوقف کند و نسخه جدید را اجرا کند.در این مطلب آموزشی برای یک برنامه نوشته شده با فریمورک اسپرینگ ( Spring Framework)؛ به عنوان نمونه؛ Continuous Deployment ساده یا به عبارت بهتر روشی برای نسخه گذاری اتوماتیک بر روی سرور شرح داده می شود تا توسعه دهنده برای نسخه گذاری فقط لازم باشد کد خود را بروی سرور push کند و تمام مراحل تکراری بعدی بصورت اتوماتیک انجام شود و نسخه ی برنامه ی در حال اجرا بروی سرور اتوماتیک بروز گردد.به چه ابزاری نیاز داریم؟فریمورک اسپرینگنصب git روی سیستم لوکالنصب git روی سروردقت داشته باشید که برای استفاده از این روش محدود به Spring framework نیستید و با کمی تغییر می توانید  این روش را برای هر سورس کد دیگری استفاده کنید.اولین قدم راه اندازی git روی سیستم خود به عنوان توسعه دهنده است که احتمالا این کار را از قبل انجام داده اید، در غیر اینصورت به کمک دستورات زیر:$ git init
$ git addبه پروژه ی خود git را اضافه می کنیم.سپس به سرور متصل شده و یک git repository در سرور راه اندازی می کنیم، جهت خلاصه کردن آموزش صرفا به صورت خیلی ساده یک repository در سرور ایجاد می کنیم:$ mkdir /home/amir/myrepo
$ cd /home/amir/myrepo
$ git initسپس این repository را به عنوان remote repository به پروژه ی لوکال خود معرفی می کنیم:$ git remote add origin ssh://[serverUser]@[serverIp]:/home/amir/myrepoدر صورتیکه در روند push کردن تغییرات با خطای زیر مواجه شدید: remote: error: refusing to update checked out branch: refs/heads/masterاحتمالا دستور زیر مشکل را حل خواهد کرد:git config receive.denyCurrentBranch ignoreتا اینجای کار یک repository در سرور خود ایجاد کرده ایم تا با push کردن بتوانید نسخه ی سورس کد موجود در سرور را بروز کنیم.در قدم بعدی به کمک قابلیت git hooks اتوماتیک کردن روند نسخه گذاری را کامل تر می کنیم:قلاب ها (hooks) در git اسکریپت هایی هستند که در مراحل یا رویدادهای مختلف git اجرا می شوند. در اینجا hookی تعریف می کنیم تا بعد از ایجاد تغییرات در repository سرور این اسکریپت اجرا شود.قبل از انجام اینکار اجازه دهید یک فولدر جهت قراردادن نسخه ی کامپایل شده ی کد ایجاد می کنیم:$ mkdir /home/amir/deployو تغییر کوچکی در برنامه ی spring ی خود دهیم:SpringApplication app = new SpringApplication(Jhip4App.class);
app.addListeners(new ApplicationPidFileWriter());با اینجام اینکار به برنامه یک Listener از جنس ApplicationPidFileWriter اضافه می کنیم تا هنگام اجرای برنامه PID یا شناسه پروسه برنامه را در یک فایل (application.pid) ذخیره کند.و با دستور زیر فایل hook را جهت ویرایش باز می کنیم:$ nano /home/amir/myrepo/.git/hooks/post-receive
$ chmod ug+x /home/amir/myrepo/.git/hooks/*و اسکریپت زیر را داخل آن قرار می دهیم:#!/bin/bash
DEPLOY=&amp;quot/home/amir/deploy&amp;quot
GIT_DIR=&amp;quot/home/amir/myrepo&amp;quot
BRANCH=&amp;quotmaster&amp;quot
while read oldrev newrev ref
do
	# only checking out the master (or whatever branch you would like to deploy)
	if [ &amp;quot$ref&amp;quot = &amp;quotrefs/heads/$BRANCH&amp;quot ];
	then
		echo &amp;quotRef $ref received. Deploying ${BRANCH} branch to production...&amp;quot
		git --work-tree=$GIT_DIR --git-dir=$GIT_DIR/.git checkout -f $BRANCH
		echo &amp;quottry to package...&amp;quot
		$GIT_DIR/mvnw -f $GIT_DIR/pom.xml package -Dmaven.test.skip=true		
		echo &amp;quottry to move jar file to depoly folder&amp;quot
		\cp -fR $GIT_DIR/target/jhip-4-0.0.1-SNAPSHOT.jar $DEPLOY/app.jar
                echo &amp;quottry to shutdown app&amp;quot
                kill $(cat $DEPLOY/application.pid)		
                echo &amp;quottry to run application...&amp;quot
                nohup java -jar $DEPLOY/app.jar &amp;
	else
		echo &amp;quotRef $ref received. Doing nothing: only the ${BRANCH} branch may be 
                 deployed on this server.&amp;quot
	fi
doneاسکریپ بالا چه می کند؟اگر تغییر اعمال شده روی شاخه یا Branch مورد نظر (در اینجا master) باشد:ابتدا تغییرات جدید روی سرور در مسیر مورد نظر checkout میشودسپس با دستور maven package فایل jar برنامه تولید می شوددر مرحله ی بعد این فایل jar به مسیر deploy منتقل می گرددسپس با کمک فایل application.pid که هنگام اجرای برنامه ساخته می شود برنامه ی قبلی shutdown می شودو در آخر برنامه جدید با nohup در بک گراند اجرا می شودبه همین سادگی و بدون نیاز به راه اندازی CI/CD پیچیده، مراحل نسخه گذاری مخصوصا در فاز توسعه نرم افزار را می توان اتوماتیک کرد.</description>
                <category>Amir Shokri</category>
                <author>Amir Shokri</author>
                <pubDate>Fri, 24 Jul 2020 01:56:09 +0430</pubDate>
            </item>
            </channel>
</rss>