این مطالب عمدتاً برای برنامه نویسان وب مبتدی در نظر گرفته شده است.

مقدمه.

اغلب مشتریانی با من تماس می گیرند که CMS خودنویسی نصب کرده اند یا ماژول های نوشته شده توسط برنامه نویسان وب مبتدی که نمی دانند برای محافظت از داده ها چه چیزی لازم است و اغلب عملکردهای فیلترینگ را بدون فکر کردن به نحوه کار آنها و دقیقاً چه کاری باید با آنها انجام داد کپی می کنند. .

در اینجا سعی خواهم کرد تا جایی که ممکن است جزئیات را شرح دهم. اشتباهات رایجهنگام فیلتر کردن داده ها در اسکریپت PHPو بده نکات سادهنحوه صحیح فیلتر کردن داده ها

مقالات زیادی در نت در مورد فیلتر کردن داده ها وجود دارد، اما همانطور که باید باشد، کامل و بدون مثال دقیق نیستند.

تشریح.

فیلتراسیون. اشتباه شماره 1
برای متغیرهای عددی از چک زیر استفاده می شود:
$number = $_GET["input_number"]; if (intval($number)) ( ... پرس و جوی SQL را اجرا کنید... )
چرا منجر به تزریق SQL? نکته این است که کاربر می تواند در یک متغیر مشخص کند ورودی_شمارهمعنی:
1"+UNION+SELECT
در چنین مواردی، چک با موفقیت پاس می شود، زیرا تابع intval مقدار صحیح متغیر را می گیرد، یعنی. 1، اما در خود متغیر $شمارههیچ چیز تغییر نکرده است، بنابراین کد مخرببه پرس و جوی SQL ارسال می شود.
فیلترینگ صحیح:
$number = intval($_GET["input_number"]); if ($number) ( ... پرس و جوی SQL را اجرا کنید... )
البته، شرایط می تواند تغییر کند، به عنوان مثال اگر شما نیاز به دریافت یک محدوده خاص دارید:
اگر ($number >= 32 AND $number<= 65)

اگر از چک باکس ها یا چند انتخاب با مقادیر عددی استفاده می کنید، این را علامت بزنید:
$checkbox_arr = array_map("intval", $_POST["checkbox"]);
آرایه_نقشه
من همچنین با فیلتر کردن در این شکل روبرو هستم:
$number = htmlspecialchars(intval($_GET["input_number"]));
کاراکترهای ویژه html
یا:
$number = mysql_escape_string(intval($_GET["input_number"]));
mysql_escape_string

هیچ چیز جز لبخند نمی تواند باعث آن شود :)

فیلتراسیون. اشتباه شماره 2
برای متغیرهای رشته ای از فیلتر زیر استفاده می شود:
$input_text = اسلش ($_GET["input_text"]);
تابع addslashes از مشخصات فرار می کند. کاراکترها، اما رمزگذاری پایگاه داده را در نظر نمی گیرد و امکان دور زدن فیلتر وجود دارد. من متن نویسنده ای که این آسیب پذیری را توصیف کرده است کپی نمی کنم و به سادگی پیوندی به کریس شیفلت می دهم (می توانید ترجمه را در Runet جستجو کنید).

برای مثال از تابع mysql_escape_string یا mysql_real_escape_string استفاده کنید:
$input_text = mysql_escape_string($_GET["input_text"]);
اگر قصد ورود ندارید تگ های html، بهتر است فیلتر زیر را انجام دهید:
$input_text = strip_tags($_GET["input_text"]); $input_text = htmlspecialchars($input_text); $input_text = mysql_escape_string($input_text);
strip_tags - برچسب های html را بریده می کند.
htmlspecialchars - خاص را تبدیل می کند. کاراکترها در موجودیت html
این روشی است که علاوه بر تزریق SQL از خود در برابر حملات XSS محافظت می کنید.
اگر به تگ های html نیاز دارید، اما فقط برای نمایش کد منبع، کافی است از:
$input_text = htmlspecialchars($_GET["input_text"]); $input_text = mysql_escape_string($input_text);

اگر برای شما مهم است که مقدار متغیر خالی نباشد، از تابع trim برای مثال استفاده کنید:
$input_text = trim($_GET["input_text"]); $input_text = htmlspecialchars($input_text); $input_text = mysql_escape_string($input_text);

فیلتراسیون. اشتباه شماره 3
این در مورد جستجو در پایگاه داده است.
برای جستجو بر اساس اعداد، از فیلتری که در خطای اول توضیح داده شده است استفاده کنید.
برای جستجو بر اساس متن، از فیلتری که در خطای دوم توضیح داده شده است، استفاده کنید، اما با رزرو.
به منظور جلوگیری از انجام خطای منطقی توسط کاربر، باید موارد خاص را حذف یا فرار کنید. کاراکترهای SQL
مثال بدون افزودن پردازش خط:
$input_text = htmlspecialchars($_GET["input_text"]); // جستجو: "%" $input_text = mysql_escape_string($input_text);
در نتیجه، یک پرس و جو دریافت می کنیم:
... WHERE text_row LIKE "%".$input_text."%" ... // WHERE text_row LIKE "%%%"
این به میزان قابل توجهی بار روی پایه را افزایش می دهد.
در اسکریپت من از تابعی استفاده می کنم که کاراکترهایی را که نمی خواهم از جستجو حذف می کند:
تابع strip_data($text) ($quotes = آرایه ("\x27"، "\x22"، "\x60"، "\t"، "\n"، "\r"، "*"، "%"، "<", ">"، "?"، "!")؛ $goodquotes = آرایه ("-"، "+"، "#")؛ $repquotes = آرایه ("\-"، "\+"، "\#"); $text = trim(strip_tags($text))؛ $text = str_replace($quotes، ""، $text)؛ $text = str_replace($goodquotes، $repquotes، $text)؛ $text = ereg_replace(" +" , " ", $text؛ $text را برگردانید؛ )
البته همه نمادهای بالا خطرناک نیستند، اما در مورد من نیازی به آنها نیست، بنابراین جستجو و جایگزینی انجام می دهم.
نمونه ای از استفاده از فیلترینگ:
$input_text = strip_data($_GET["input_text"]); $input_text = htmlspecialchars($input_text); $input_text = mysql_escape_string($input_text);
من همچنین به شما توصیه می کنم برای تعداد کاراکترها در جستجو محدودیت ایجاد کنید، حداقل کمتر از 3، زیرا. اگر تعداد رکوردهای زیادی در پایگاه داده دارید، جستجوی 1-2 کاراکتر به میزان قابل توجهی بار روی پایگاه داده را افزایش می دهد.
فیلتراسیون. اشتباه شماره 4
مقادیر متغیر فیلتر نمی شوند $_COOKIE. برخی فکر می کنند که از آنجایی که این متغیر را نمی توان از طریق فرم عبور داد، پس این یک ضمانت امنیتی است.
جعل این متغیر توسط هر مرورگری با ویرایش کوکی های سایت بسیار آسان است.
به عنوان مثال، در یکی از CMS های معروف بررسی الگوی سایت مورد استفاده وجود داشت:
if (@is_dir (MAIN_DIR. "/template/" . $_COOKIE["skin"]))($config["skin"] = $_COOKIE["skin"]; ) $tpl->dir = MAIN_DIR . "/قالب/" . $config["skin"];
در این حالت می توانید مقدار متغیر را تغییر دهید $_COOKIE["پوست"]و یک خطا را مطرح کنید که در نتیجه مسیر مطلق پوشه سایت را مشاهده خواهید کرد.
اگر از مقدار کوکی ها برای ذخیره در پایگاه داده استفاده می کنید، سپس از یکی از فیلترهای توضیح داده شده در بالا استفاده کنید، همین امر در مورد متغیر نیز صدق می کند. $_SERVER.
فیلتراسیون. اشتباه شماره 5
بخشنامه گنجانده شده است register_globals. اگر روشن است حتما آن را خاموش کنید.
در برخی مواقع می توانید مقدار متغیری را که نباید پاس داده می شد، ارسال کنید، مثلاً اگر سایت دارای گروه است، برای گروه 2 متغیر $group باید خالی یا برابر با 0 باشد، اما کافی است که جعلی باشد. فرم را با افزودن کد:

متغیر در اسکریپت PHP گروه $اگر با مقدار پیش فرض در اسکریپت اعلام نشده باشد برابر با 5 خواهد بود.
فیلتراسیون. اشتباه شماره 6
فایل های دانلود شده را بررسی کنید.
موارد زیر را بررسی کنید:
  1. فرمت فایل. توصیه می شود بارگیری فایل ها را با پسوندهای: php، php3، php4، php5 و غیره غیرفعال کنید.
  2. آیا فایل در سرور move_uploaded_file آپلود شده است؟
  3. حجم فایل
معاینه. اشتباه شماره 1
من به مواردی برخورد کردم که برای درخواست AJAX (مثلا: افزایش شهرت) یک نام کاربری یا شناسه ارسال شد (که شهرت به آنها افزایش می یابد) اما خود پی اچ پی وجود چنین کاربری را بررسی نکرد.
مثلا:
$user_id = intval($_REQUEST["user_id"]); ... INSERT INTO REPLOG SET uid = "($user_id)"، plus = "1" ... ... UPDATE Users SET reputation = Reputation+1 WHERE user_id = "($user_id)" ...
به نظر می رسد که ما یک رکورد در پایگاه داده ایجاد می کنیم که برای ما کاملاً بی فایده است.
معاینه. اشتباه شماره 2
هنگام انجام اقدامات مختلف (افزودن، ویرایش، حذف) با داده ها، فراموش نکنید که حقوق کاربر برای دسترسی به این عملکرد و بررسی شود. ویژگی های اضافی (استفاده از htmlبرچسب‌ها یا امکان انتشار مطالب بدون تأیید).

برای مدت طولانی یک خطای مشابه را در یک ماژول تالار گفتمان رفع کردم، زمانی که هر کاربری می‌توانست پیام مدیریت را ویرایش کند.

معاینه. اشتباه شماره 3
هنگام استفاده از چندگانه فایل های phpیک بررسی ساده انجام دهید
در پرونده index.php(یا در هر فایل اصلی دیگری) این خط را قبل از اضافه کردن سایر فایل های php بنویسید:
define("READFILE"، true);
در ابتدای فایل های php دیگر بنویسید:
if (! تعریف شده ("READFILE")) ( خروج ("خطا، روش اشتباه برای فایل".
برو به اصلی."); }
این کار دسترسی به فایل ها را محدود می کند.
معاینه. اشتباه شماره 4
از هش برای کاربران استفاده کنید. این به جلوگیری از فراخوانی یک تابع خاص توسط XSS کمک می کند.
نمونه ای از کامپایل هش برای کاربران:
$secret_key = md5(strtolower("http://site.ru/" . $member["name"] . sha1($password) . date("Ymd"))); // $secret_key هش ما است
سپس، در تمام اشکال مهم، ورودی را با مقدار هش فعلی کاربر جایگزین کنید:

در حین اجرای اسکریپت، بررسی کنید:
if ($_POST["secret_key"] !== $secret_key) ( خروج ("خطا: secret_key!")؛ )
معاینه. اشتباه شماره 5
هنگام خروجی خطاهای SQL، یک محدودیت ساده برای دسترسی به اطلاعات ایجاد کنید. به عنوان مثال، یک رمز عبور برای متغیر GET:
if ($_GET["passsql"] == "رمز عبور") ( ... خروجی خطای SQL... ) else ( ... فقط اطلاعات خطا، بدون جزئیات... )
این اطلاعات از هکرها پنهان می شود که می تواند به او در هک سایت کمک کند.
معاینه. اشتباه شماره 5
سعی کنید با دریافت نام فایل ها از خارج، فایل ها را وارد نکنید.
مثلا:
if (isset($_GET["file_name"])) (شامل $_GET["file_name"] .".php"؛ )
از سوئیچ استفاده کنید

من در حال ایجاد یک لیست ساده در PHP هستم که در آن کاربر می تواند نام، سن، ایمیل ها و غیره را اضافه کند. من یک گزینه حذف نیز اضافه کرده ام، اما می خواهم زمانی که کاربر روی دکمه حذف کلیک می کند یک پیام تأیید اضافه کنم.

من گوگل را امتحان کردم اما فقط راه حل های جی کوئری و جاوا اسکریپت را پیدا کردم. آیا راهی برای انجام این کار فقط با PHP وجود دارد؟

نامسن"; while($query2=mysql_fetch_array($query1)) (echo" ".$query2["name"].""؛ پژواک" ".$query2["سن"].""؛ پژواک" ویرایش کنید"؛ پژواک" ایکس"; } ?>

حذف.php

که در

اگر می‌خواهید این کار را فقط با PHP انجام دهید، باید «گام‌ها» را به اسکریپت خود اضافه کنید، مانند:

مرحله 1 (نمایش فرم) -> مرحله 2 (از تأیید اعتبار) -> مرحله 3 (تأیید اعتبار)

برای انجام این کار، می توانید از جلسات برای ذخیره محتوای فرم و پارامتر GET برای پیگیری این مرحله استفاده کنید. در غیر این صورت، بیشترین راه حل سادهاستفاده از جاوا اسکریپت است:

پژواک" ایکس"; //از دو نقل قول برای js در داخل php استفاده کنید!

این چیزیست که شما نیاز دارید

while($query2=mysql_fetch_array($query1)) (echo " ".$query2["name"].""؛ پژواک" ".$query2["سن"].""؛ پژواک" ویرایش کنید"؛ پژواک" ایکس"; ) در while($query2=mysql_fetch_array($query1)) (echo" ".$query2["name"].""؛ پژواک" ".$query2["سن"].""؛ پژواک" ویرایش کنید"؛ پژواک" ایکس"; }

و یک تابع جاوا اسکریپت ایجاد کنید

تابع confirmationDelete(anchor) ( var conf = تایید ("آیا مطمئن هستید که می خواهید این رکورد را حذف کنید؟"؛ if(conf) window.location=anchor.attr("href")؛ )

به من اعتماد کن، کار است 🙂

اضافه کردن یک رویداد onClick برای فعال کردن گفتگو و javascript:return تایید ("آیا مطمئن هستید که می خواهید این را حذف کنید؟");

پژواک" ایکس";

//add onclick event onclick="return deleteconfig()"

برای من کار کنید اما این را تغییر دهید:

onclick="javascript:confirmationDelete($(this));return false;"

onclick="confirmationDelete(this);return false;"

در زیر یک نوع از موارد بالا وجود دارد که یک فیلد تأیید می دهد و متغیر را از PHP به جاوا اسکریپت و برگرداندن به PHP می دهد.
من از این برای انتخاب یک دکمه رادیویی برای حذف یک فایل از لیست فایل استفاده کردم.
تابع ماشه OnClick را با نام php $fileName در جاوا اسکریپت ببینید، با نام فایل تأیید کنید و اگر چنین است در href با متغیرهایی برای $_GET ارسال کنید.

کد PHP/HTML:

$fileName $extn اندازه دلار $modtime "; ?>

مقاله در مورد خوشه ها، نه در مورد تقسیم با تکثیر، و نه حتی در مورد ابرها است. این مقاله در مورد ساخت یک معماری محاسباتی بسیار قابل اعتماد است که در آن تعداد کاربران و درخواست های آنها می تواند مانند یک بهمن رشد کند. و برای یک کسب و کار بسیار مهم است که وب سرویس هر درخواست را بپذیرد، آن را به درستی و تا انتها پردازش کند (بدون توجه به خرابی و سقوط برخی از اجزا)، و تضمین شود که پاسخی را به مشتری ارائه دهد. و البته بدون هزینه های «فضایی» برای تجهیزات و حقوق مدیران سیستم.

به عبارت دیگر، اول از همه، فکر کنید - "آیا به آن نیاز دارم". اگر کسی فروشگاه آنلاینی دارد که همسترهای سخنگو را با گردش مالی 100 سفارش در ماه می فروشد، احتمالاً خیر. و اگر قصد دارید کسب‌وکاری را اداره کنید که می‌تواند صدها هزار و میلیون‌ها کاربر را در خود جای دهد، به محاسبات زیادی نیاز دارد، با داده‌های با ارزش کار می‌کند، تراکنشی هر فرآیند تجاری را تضمین می‌کند و به پردازش موازی داده‌ها نیاز دارد، آیا آن است.

بخش مالی، فروشگاه‌های آنلاین بزرگ با طیف وسیعی از صدها هزار واحد، حراج‌های آنلاین، سیستم‌های رزرو هتل و خطوط هوایی، سرویس‌های ابری یا اجتماعی جدید با جاه‌طلبی برای به دست آوردن یک میلیون کاربر در روز بعد از شروع کمپین تبلیغاتی هستند. به طور بالقوه به یک سیستم بسیار قابل اعتماد برای خدمات وب شما علاقه مند است.

مخاطب این مطالب است

1. توسعه دهندگان پروژه های وب بزرگ که علاقه مند به ایجاد خدمات محاسباتی با بارگذاری بالا و تحمل خطا هستند.

2. صاحبان مشاغل جدید یا در حال گسترش که انتظار رشد "انفجاری" پایگاه کاربران را دارند و تقاضاهای زیادی را برای بخش محاسباتی دارند.

3. رهبران فنی و مدیران پروژه های بزرگ وب که از وضعیت فعلی راضی نبوده و به تجدید سازماندهی اساسی می اندیشند.

چرا این همه کلمه در مورد "محاسبات"؟

زیرا آینده نزدیک پروژه های بزرگ وب در زمینه " اطلاعات بزرگروندی است که در سال 2011 در کنار مجازی سازی، صرفه جویی در مصرف انرژی و نظارت به عنوان یکی از برترین ها شناخته شد و از سال 2013 جایگاه خود را محکم در صنعت باز کرد و حتی به یکی از آکادمیک ها تبدیل شد. موضوعات در دانشگاه های بزرگ خارجی

حجم داده‌هایی که برای حل مشکلات کسب‌وکار پردازش می‌شوند، امروزه به طور مداوم در حال افزایش است و در آینده این روند فقط تسریع خواهد شد. اگر ده سال پیش برای نشان دادن یک "ویترین فروشگاه آنلاین" با یک محصول به کاربر کافی بود، یک سال پیش برای تجزیه و تحلیل مسیر کاربر از طریق سایت و نشان دادن یک محصول کاملا مرتبط (به اصطلاح "فناوری های رفتاری") کافی بود. )، امروزه دانستن همه چیز در مورد کاربر، از جمله قد، وزن، سن و نام سگ مورد علاقه خود، امری عادی در نظر گرفته می شود.

قوانین رقابت طبیعی حکم می کند که اگر می خواهید به مشتریان گزینه های بیشتری نسبت به رقبای خود ارائه دهید، به داده های بیشتر و محاسبات بیشتری نیاز دارید. و دیر یا زود، پروژه شما، بدون رویکردهای مناسب، می تواند در آنها غرق شود - درست مانند پروژه های مختلف در حال حاضر در همه جا غرق می شوند: کسی به دلیل پیچیدگی پشتیبانی، کسی به دلیل ساده کد بد، کسی به دلیل اتصال بالای ماژول ها، شخصی به دلیل استفاده از اجزای غیرقابل اعتماد.

بنابراین، هرکسی که امروز یک پروژه وب را با جاه طلبی های بزرگ شروع می کند، اگر در ابتدا پروژه خود را نه تنها به عنوان یک کد برنامه، بلکه به عنوان یک محیط محاسباتی - سیستمی با قوانین وجود و توسعه خاص خود - در نظر بگیرند، مزایای بیشتری نسبت به رقبا خواهند داشت. هر چه بیشتر توجه کنید مدیریت محاسباتدر ابتدا، احتمال اینکه در سال های آینده از رقبا پیشی بگیرید بیشتر خواهد بود.

نویسنده این سطور متقاعد شده است که رویکرد مدرن "کلاسیک" برای ایجاد خدمات وب با بارگذاری بالا دارای تعدادی اشکالات جدی است. بیایید ببینیم چرا. ابتدا یک طرح مدرن معمولی را در نظر بگیرید:

رویکرد کلاسیک برای ایجاد یک سرویس وب با بارگذاری بالا

1. بسیاری از سرورها به نقش ها تقسیم می شوند.

2. بخشی از سرورها (نقش Frontend) برای برگرداندن منابع استاتیک (تصاویر، CSS، فایل‌های JS) و "توزیع" جلوی ترافیک ورودی به گره‌های پایین‌دست طراحی شده‌اند. نرم افزار اصلی معمولا Nginx است.

3. گره های پایین دست (نقش Backend) در محاسبات پویا مشغول هستند. به عبارت ساده، می تواند یک بسته نرم افزاری Apache + PHP معمولی باشد.

4. گروه دیگری از سرورها برای ذخیره سازی داده ها طراحی شده اند. اینها MySQL، Memcache، Redis و غیره هستند.

5. خود کد سرویس وب (در این مثال– کد PHP) به طور مساوی در تمام گره هایی که Apache + PHP وجود دارد کپی می شود و به همان اندازه درخواست هایی را که بر روی یک یا آن گره "افتاده" می شوند پردازش می کند.

6. پایگاه‌های داده با استفاده از نوعی تقسیم‌بندی بر روی گروه سرورهای خود «آلوده می‌شوند» و بار روی آن‌ها به روشی مشابه متعادل می‌شود.

یک خواننده با دقت متوجه خواهد شد که تعادل DNS و CDN در طرح ذکر نشده است، اما نویسنده به عمد آنها را حذف کرده تا طرح را بیش از حد پیچیده نکند. اصل عملکرد این قطعات بسیار شبیه به موارد فوق است.

معایب رویکرد کلاسیک

1. روند اصلی عارضه است. در طول عمر پروژه، طرح "کلاسیک" پیچیده تر و پیچیده تر می شود. با اضافه شدن هر سرور جدید، باید آن را وارد طرح توزیع ترافیک کنید. البته در پروژه های بزرگ، معرفی یک سرور به یک نقش یک اقدام «یک دکمه» است، اما با این وجود، این افزایش زیرساختی است که نیاز به پشتیبانی دارد. و حتی بیشتر از آن - در شرایطی که ظرفیت های فعلی پایگاه داده دیگر کافی نیست و باید "زندگی" (بدون توقف سرویس) پایگاه داده را به سرورهای جدید منتقل یا توزیع کنید.

اما مشکل اصلی این است که افزایش خوشه توزیع ترافیک منجر به کاهش پیچیدگی کد برنامه نمی شود. شما می توانید یک کلاستر را بدون نقص بسازید، اما کد یکسان باقی می ماند.

2. استقرار اتمی نیست. به عبارت ساده، چیدمان نسخه جدیدپروژه روی سرورهای رزمی مدتی طول می کشد. لازم است فایل ها را به صورت فیزیکی در N-20 ماشین بارگیری کنید، تغییراتی را در پایگاه داده ایجاد کنید، کش ها را بازنشانی کنید (بسیاری از کش های مختلف) و همزمان در تمام سرورها پردازش درخواست ها با "کد قدیمی" تمام شود و پردازش با "کد قدیمی" شروع شود. کد جدید». در غیر این صورت، زمانی که بخشی از درخواست کاربر به روش قدیمی پردازش می شود، بخشی به روش جدید، بخشی از آن طبق "طرح قدیمی"، بخشی "بر اساس طرح جدید" به پایگاه داده می رود، بسیاری از درگیری های کوچک ممکن است ایجاد شود. به زودی.

بنابراین، در حالت ایده‌آل، همه می‌خواهند سرویس را برای مدت زمان به‌روزرسانی (چند ثانیه یا ده‌ها ثانیه) متوقف کنند و سپس دوباره آن را روشن کنند. در واقع، با جریان حداقل 1000 درخواست در ثانیه، هیچ‌کس این کار را انجام نمی‌دهد، ترجیح می‌دهد مرتباً برخوردهای جزئی را «با دست» برطرف کند، یا آنها را با برنامه‌نویسی دفاعی پوشش دهد که از سازگاری با عقب «تا نسل هفتم» پشتیبانی می‌کند. در مورد چگونگی پشتیبانی منظم سازگاری به عقبزندگی برنامه نویسان را پیچیده می کند (و هزینه پروژه را به طور کلی افزایش می دهد) - یک خواننده هوشمند می تواند برای خودش فکر کند.

3. استفاده شده توسط HTTP. پروتکل HTTP بدیهی است که از نظر اخلاقی و فنی منسوخ شده است و اگر از آن پیروی کنید (به عنوان مثال) توسعه موبایل- می دانید که در همه جا با پروتکل های سبک تر جایگزین شده است. اما اشکال اصلی متفاوت است: پروتکل HTTP "در مرورگر" به تکمیل حلقه نیاز دارد - نیاز به پاسخ در مدت زمان محدودی دارد. این سرویس را ملزم می کند که پاسخ را دقیقاً در بازه زمانی اندکی که مرورگر به آن اجازه می دهد محاسبه و آماده کند. اگر سرویس است این لحظهاضافه بار - درخواست برای همیشه از بین خواهد رفت.

بنابراین، در «پروژه‌های وب معمولی» به ترفندهای مختلفی مانند Long Polling یا هر شکل دیگری از درخواست‌های دوره‌ای متوسل می‌شوند که نه تنها معماری را پیچیده می‌کند، بلکه سرویس را با «کشش بیهوده» بارگذاری می‌کند.

4. مقداردهی اولیه اسکریپت در هر درخواست. این نتیجه استفاده از HTTP و زبان های اسکریپت نویسی مانند PHP است که طبق یک سنت قدیمی، در پاسخ به هر درخواست مجدداً راه اندازی می شوند. بله، بله، در پاسخ به هر یک از 1000 درخواست در ثانیه، اسکریپت PHP دوباره شروع می شود، دوباره همه متغیرها را مقداردهی اولیه می کند و دوباره به پایگاه داده ارتباط برقرار می کند. در عمل، این اتفاق می افتد که پردازش یک درخواست 0.005 ثانیه طول می کشد و اسکریپت به ترتیب 0.05 ثانیه - ده برابر بیشتر - مقداردهی اولیه می شود!

به عبارت دیگر، 90 درصد از مواقع سرورهای شما مشغول پردازش درخواست‌های مشتری نیستند، بلکه شروع به کار بی‌فایده اسکریپت‌ها می‌کنند. سعی کنید آن را به پول تبدیل کنید. بنابراین، راه‌حل‌های زیادی ابداع شده‌اند، مانند ذخیره‌سازی OPcode، اتصالات پایدار پایگاه داده، حافظه پنهان محلی مانند Memcache یا Redis، که برای جبران این اثر ناخوشایند طراحی شده‌اند.

5. کاربرد یکپارچه. مهم نیست که چگونه برنامه را به ماژول ها تقسیم می کنید، مهم نیست که چقدر سعی می کنید کد را روی یک ساختار دایرکتوری پیچیده پخش کنید، مهم نیست که از چه بارگذاری خودکار تنبلی استفاده می کنید، تنها یک معیار وجود دارد: اگر برای آپلود نیاز به آپلود کل برنامه دارید. حداقل یک تغییر، شما یک برنامه یکپارچه دارید. چیز دیگری داده نمی شود.

معایب کاربردهای یکپارچه به طور گسترده در ادبیات توضیح داده شده است. یکی از اصلی ترین آنها را می توان به اختصار ذکر کرد: اگر می خواهید حتی کوچکترین ویژگی در یک ساعت در حال تولید باشد، باید کل زنجیره تولید را در یک ساعت جا دهید. تعریف مشکل، پیاده‌سازی، بررسی سازگاری به عقب، نوشتن تست، نوشتن مستندات، اجرای بخش آزمایش دستی، رفع اشکال - همه در عرض یک ساعت.

زیرا اگر کل برنامه را دقیقاً در 00 دقیقه هر ساعت آپلود کنید، در پایان هر ساعت باید بیاورید کل برنامهبه یک وضعیت باثبات

6. رابط وب توسط باطن ارائه می شود. در یک مورد معمولی، ظاهر(و، بر این اساس، کد HTML) صفحات پروژه، به عنوان یک قاعده، در پاسخ به هر درخواست، در سمت Backend ارائه می شود. این یک مصرف بیش از حد و غیرقابل توجیه منابع و پول است.

7. تقسیم بندی سیاسی ادارات. بخش مدیران سیستممسئول اطمینان از این است که جلوی ترافیک ورودی روی دسته ای از سرورهایی که کد PHP روی آنها اجرا می شود، "آلوده" است. بخش برنامه نویسی مسئول کدهای PHP است. اگر کد PHP برای پردازش یک درخواست خاص وقت نداشته باشد، مشخص نیست که چه کسی مسئول این کار است - یا مدیر، که ترافیک زیادی را به سرور "اجازه" داده و آن را بیش از حد بارگذاری کرده است، یا برنامه نویسی که غیر از آن نوشته است. -اسکریپت بهینه اگر پایگاه داده شروع به کند شدن کند، مشخص نیست چه کسی افراطی باقی می‌ماند: مدیری که به موقع متوجه «تکه‌کردن» آن نشده است یا برنامه‌نویسی که می‌تواند آن را بفهمد.

اگر مثال‌ها اغراق‌آمیز به نظر می‌رسند و «از زندگی واقعی نیستند»، به یاد داشته باشید که نویسنده در پروژه‌ای کار می‌کرد که روی 200 سرور میزبانی شده بود و 60 مورد از آنها توسط پایگاه داده اشغال شده بود. به یاد آوردن چند نفر برای خدمات رسانی به این پروژه ترسناک است.

عیب اصلی

ما فکر فوق را تکرار می کنیم: اشکال اصلی طرح کلاسیک، به گفته نویسنده، این است که متخصصان فنی چیز اشتباه را بهینه می کنند. آنها بخش ورودی درخواست‌ها را بهینه می‌کنند، در واقع آن را روی گروه بزرگی از ماشین‌ها «لکه‌دار» می‌کنند، به‌جای بهینه‌سازی اصل - بخش محاسباتی. و بسیار ساده تر از آن چیزی است که به نظر می رسد.

ایده آل نظری

آه، چقدر دوست داشتیم بتوانیم:

1. از شر یکسری سرورهای گران قیمت خلاص شوید و از یک یا دو گروه کوچک استفاده کنید.

2. طرح Nginx->Apache->PHP را با "سربار" وحشی آن از نظر منابع مصرف شده که هزینه دارد، رها کنید.

3. به همین دلیل هزینه هولناک اولیه سازی اسکریپت های PHP را حذف کنید.

4. از نیاز به "رندر کردن" صفحات در باطن خودداری کنید. اگر یک وب سرویس بتواند با اتصال اینترنت ناپایدار یا بدون اتصال به اینترنت (مثلاً در هنگام استفاده) کار کند، کاملاً رویایی خواهد بود شبکه موبایلدر جاده).

5. خلاص شدن از شر «حلقه زمان‌بندی HTTP»، پاسخ را تنها زمانی که این پاسخ آماده است و با ضمانت تحویل به مشتری تحویل دهید.

6. پروژه را در قطعات کوچک، بدون توقف و بدون از دست دادن یک درخواست مشتری، به روز کنید.

7. اگر بخشی از پروژه (برخی جزء) "افتاد" یا به طور موقت برای اهداف اشکال زدایی خاموش شد، نگران درخواست های از دست رفته نباشید.

غیر واقعی؟ به آسانی!

اولین قدم ها به سوی تعالی

اول بیایید یاد بگیریم چیباید انجام شود، بنابراین ما بحث خواهیم کرد - چگونه.

1. کل سیستم را به صورت SOA طراحی کنید(معماری سرویس گرا) با ESB (اتوبوس پیام سازمانی)، کنار گذاشتن رویکرد یکپارچه، به طوری که هر بخش مستقل از منطق تجاری توسط یک "سرویس" جداگانه پردازش می شود و آنها از طریق یک گذرگاه تبادل مستقل با یکدیگر ارتباط برقرار می کنند.

2. همزمانی را کنار بگذارید. به عنوان مثال، در یک طرح همزمان "درخواست - پردازش - پاسخ"، این یک حلقه HTTP است که کنترل خاتمه دقیقی ندارد و به راحتی می تواند قطع شود. در ناهمزمان، سه فرآیند جداگانه وجود دارد: درخواست (ارسال و تایید)، پردازش (تلاش مجدد در صورت شکست)، تحویل پاسخ (با ضمانت).

3. پروژه را به دو برنامه تقسیم کنید- Frontend و Backend. در مورد یک وب سرویس، فرانت اند (معمولا) یک برنامه جاوا اسکریپت است. نکته اصلی این است که برنامه ها به صورت ناهمزمان و جدا از یکدیگر کار می کنند و پیام ها را از طریق یک پروتکل ارتباطی دو طرفه رد و بدل می کنند.

4. انصراف از HTTPبه نفع WebSocket پروتکل WebSocket در مقایسه با HTTP عملکرد فوق‌العاده‌ای دارد، «حلقه‌هایی با زمان‌بندی» ندارد و به شما امکان می‌دهد هر داده (از جمله داده‌های باینری) را در هر دو جهت انتقال دهید.

5. یک "ذخیره" برای اجرای پرس و جوها فراهم کنید. هنگامی که درخواست از مشتری دریافت شد، به مشتری بگویید "Confirm" و این درخواست را ذخیره کنید. به محض اینکه backend از چرخه پردازش قبلی آزاد شد، درخواست را به آن ارسال کنید. تا زمانی که درخواست بین گره‌های بک‌اند «راه می‌رود»، آن را از لحظه‌ای که «وارد» گره شد، نگه دارید و به محض «ترک» از گره، آن را انجام دهید. بنابراین، اگر برخی از گره ها سقوط کنند، سیستم درخواست را از دست نمی دهد و بلافاصله آن را به پردازش ارسال می کند. در پایان پردازش، نتیجه را برای مشتری ارسال کنید و آن را ذخیره کنید تا زمانی که مشتری بگوید "تأیید".

6. از محاسبات موازی اطمینان حاصل کنیدبرای آن دسته از عملیاتی که می توانند به صورت موازی از نظر منطق تجاری انجام شوند، و توالی برای آنهایی که باید کاملاً به صورت متوالی انجام شوند. این پاراگراف نه با ادعای "Captain Obviousness" نوشته شده است، بلکه برای نشان دادن این است که هیچ فرآیند تجاری را نمی توان "کوکورانه" روی کدهای چند رشته ای قرار داد.

7. اسکریپت نویسی را به نفع «شیطان» کنار بگذارید. دیمون فرآیندی است که یک بار شروع می شود و سپس دائماً در حافظه "آویزان" می شود. از آنجایی که همیشه اجرا می شود، نیازی به صرف زمان برای شروع مجدد برای هر درخواست ندارد. با نگاهی به آینده، می گویم که دیمون PHP (هنگام استفاده از نسخه های مدرن PHP) هیچ تفاوت اساسی با یک اسکریپت معمولی PHP ندارد.

رویکرد طراحی SOA

SOA - معماری سرویس گرا - جدید نیست. این رویکرد ماژولار برای توسعه نرم افزار در قرن گذشته توسط IBM آغاز شد و در حال حاضر توسط رهبران صنعت، عمدتاً در محصولات "سطح سازمانی" در دات نت و جاوا پشتیبانی و ترویج می شود.

در رویکرد کلاسیک برنامه نویسی وب سرویس ها در زبان های پی اچ پیو مشابه - طراحی از مدل ها، ویژگی ها و عملیات روی آنها شروع می شود. مدل ها اشیاء دنیای واقعی را نشان می دهند و عملیات نشان دهنده اعمال روی اشیا هستند. با این حال، همانطور که تمرین نشان می‌دهد، دنیای واقعی بسیار چندوجهی‌تر و پیچیده‌تر است و با زبان رویدادها و واکنش‌ها به آنها بسیار مؤثرتر توصیف می‌شود (برای جزئیات بیشتر، پست شماره 1593 را با توضیحات و مثال‌ها ببینید).

دنیای واقعی شامل رویدادهایی است که به طور همزمان (از نظر برنامه نویسی - "موازی") و عمدتاً بدون مشارکت ما رخ می دهد و واکنش های مختلفی به آنها رخ می دهد یا رخ نمی دهد. واکنش ها به نوبه خود می توانند رویدادهای زیر را ایجاد کنند. SOA برای "برنامه نویسی در دنیای واقعی" ایده آل است، زیرا کار با رویدادها، روابط بین آنها و واکنش به آنها راحت تر است. علاوه بر این، در رویکرد درستبه سازماندهی معماری، و رویدادها و واکنش ها به آنها به طور موازی رخ می دهد، حتی اگر از یک زبان برنامه نویسی "تک رشته ای" مانند PHP استفاده کنید.

شما باید یک محصول SOA را بر اساس اینکه چه رویدادهایی در منطق کسب و کار شما رخ می دهند، چگونه به یکدیگر مرتبط هستند یا باید به دنبال یکدیگر باشند، چه واکنش هایی باید در پاسخ به رویدادهای خاص رخ دهد و دقیقاً چه کسی این یا آن رویدادها را پردازش می کند، طراحی کنید. رویدادهای دیگر پیاده‌سازی مدل‌های داده و اقدامات روی آنها در پس‌زمینه محو می‌شود (در یک «سرویس» کپسوله شده است)، و فهرست «سرویس‌ها» و طرح تعامل بین آنها (API بین‌سرویس) در پیش‌زمینه قرار می‌گیرد.

پیاده سازی: تقریب اول

1. Frontend به عنوان یک برنامه مستقل. هر چارچوب محبوب JavaScript-MVC برای پیاده سازی مناسب است. با این حال، من از روی تمرین توجه می کنم، اگر با ترکیب کلمات "JavaScript، MVC و Framework" در یک جمله شروع به درد کنید، برای شما دشوار خواهد بود. برنامه باید بتواند همه «صفحه‌های» خود را بدون ارجاع به پشتیبان ترسیم کند، به کاربر ناوبری (انتقال بین «صفحه‌ها») را نیز بدون ارجاع به باطن بدهد، و از یک کانال ارتباطی دو طرفه با باطن پشتیبانی کند.

2. نقطه ورود برای اتصال WebSocket به باطن. تعدادی راه حل آماده در NodeJS وجود دارد که از درخواست های بازگشتی برای طولانی نظرسنجی و ajax نیز پشتیبانی می کند. از نظر ایدئولوژیک منسوخ شده استمرورگرها برای آینده متذکر می شوم که این گره در HTTP خالص نیز قابل دسترسی است، اگر نیاز به نوشتن برخی دروازه ها با خدمات دیگران دارید، اما برای ساده تر کردن، می توانید یک گره "HTTP خالص" جداگانه بنویسید.

نقطه ورودی یک کانال ارتباطی دو طرفه با برنامه جلویی (به عبارت دیگر با مرورگر) ارائه می‌کند که درخواست‌های آن را می‌پذیرد و پاسخ‌ها را به آن برمی‌گرداند.

3. ذخیره درخواست های در حال اجرا در سیستم. برای این کار، سرور محبوب AMQP بهترین تناسب است که صف های پیام و مسیریابی بین آنها را فراهم می کند. به محض اینکه درخواست بعدی از مشتری رسید، آن را در صف "ورودی" قرار دهید. علاوه بر این، توسط دیمون از این صف استخراج می شود، که محتویات درخواست را تجزیه و تحلیل می کند و آن را به مسیریابی در سراسر سیستم می فرستد (که در واقع به معنای انتقال آن به یک یا چند صف مطابق با الگوریتم های خاص است). هر دیمون که با بخش خاص خود از منطق تجاری سروکار دارد، این یا آن پیام را از صف ورودی "خود" دریافت می کند، آن را پردازش می کند و پاسخ را در صف "خروجی" قرار می دهد.

توجه دارم که در اصطلاح کارگزار محبوب RabbitMQ هیچ مفهومی از صف های خروجی وجود ندارد. پیام ها در صرافی (مبادله) منتشر می شوند که از آنجا خود کارگزار طبق قوانین مسیریابی به صف های خاص منتقل می شود. در اینجا آنقدر برای درک مشروط نوشته شده است که پاسخ مستقیماً برای درخواست کننده ارسال نمی شود.

4. کنترل شیطان(سرپرست). برای اجرای یک اسکریپت ساده PHP به عنوان دیمون، کافیست کد اجرایی را در while(true) (...) بپیچید و تایپ کنید خط فرمانچیزی شبیه "php your-script.php". اما بهتر است از هر ناظر مناسبی برای این کار استفاده کنید که اساساً همین کار را انجام می دهد، اما وضعیت لازم محیط را نیز فراهم می کند، وضعیت فرآیند را نظارت می کند و کارهای مفیدتری را انجام می دهد.

در زندگی واقعی، دیمون PHP کمی پیچیده‌تر است: باید سیگنال‌های کنترلی و پیام‌های پیکربندی مجدد را دریافت کند، نباید حافظه را درز کند، باید اتصالات قطع شده را به پایگاه داده حفظ کند (یا بازیابی کند) - اما به طور کلی پیچیده‌تر از این نیست. اسکریپت های پی اچ پی که به آن ها عادت دارید.

یک قدم به واقعیت نزدیکتر: رویکرد رویداد محور در SOA

برخی از رویکردها (به نظر نویسنده، منسوخ شده) برای ساخت برنامه های ماژولار بر اساس اصل RPC (Remote Procedure Calling) است، که مستلزم فراخوانی مستقیم به روش ها یا رویه های خاص در یک جزء پروژه از راه دور است. این رویکرد به طور کامل تمام مزایای SOA را از بین می برد، زیرا معمولاً به معنای اتصال مستقیم و سخت بین گره های ارسال کننده و اجرا کننده است. در طراحی و اجرای یک محصول پیچیده، باید تا حد امکان از اصل اجزای با جفت سست رعایت شود، زیرا این پیچیدگی معماری و کد است که در نهایت هزینه مالکیت (اصلاحات و تغییرات محصول) را تعیین می کند. پس از راه اندازی آن).

رویکرد رویداد محور در SOA فرض می‌کند که مؤلفه‌ها (سرویس‌ها) با ارسال رویدادهای ناهمزمان («رویدادها» از کلمه Event) با یکدیگر ارتباط برقرار می‌کنند. رویداد پیامی است (مثلاً در اصطلاحات AMQP) که دارای نام (نام) و مجموعه ای از پارامترها است. یک رویداد در نظر گرفته شده است تا به سیستم بگوید که چیزی اتفاق افتاده است، یا "یک سوال" از سیستم بپرسد. در حالت کلی، رویدادها بدون آدرس "به سیستم" (به طور دقیق تر، به گذرگاه مشترک ESB) ارسال می شوند - یعنی بدون قصد خاصی برای تحویل به گره ها یا اجراکنندگان خاص.

برعکس، گره‌های خاص (کامپوننت‌ها، سرویس‌ها) به گذرگاه مشترک برای رویدادهای خاصی گوش می‌دهند که آماده پاسخگویی به آن هستند. این ممکن است به این معنی باشد که سرویس آماده شنیدن برخی رویدادها و انجام عمل مناسب است. یا این سرویس دانشی دارد (مثلاً دارای یک پایگاه داده با اطلاعات کاربران است) و آماده ارائه آنها در پاسخ به "درخواست" است. در هر دو مورد، نتیجه پاسخگویی به یک رویداد، تولید یک رویداد جدید (با نام متفاوت و پارامترهای مختلف) است که توسط سایر خدمات علاقمند نیز قابل شنیدن است.

با سازماندهی مناسب ژنرال لاستیک های ESB، سرویس ها رویدادها را به صورت ناهمزمان و بدون انتظار برای یکدیگر ارسال و دریافت می کنند. این بدان معنی است که سرویس ارسال می تواند هر تعداد رویداد را بدون تاخیر زمانی به ESB ارسال کند و به حل وظایف زیر بپردازد. این را با HTTP کلاسیک مقایسه کنید، که به معنای انتظار برای پاسخ در چرخه پردازش فعلی است، و مزایای آن را خواهید دید. و سرویس های دریافت کننده نیز بلافاصله پس از اتمام پردازش رویداد قبلی، رویدادهای جدید را به صورت ناهمزمان، مستقل از یکدیگر دریافت خواهند کرد.

مدل رویداد SOA در کد

به طور خلاصه، شما باید به کد خود نه به عنوان کلاس هایی با توابع (روش ها)، بلکه به عنوان رویدادها و اقداماتی که در پاسخ به این رویدادها رخ می دهند نگاه کنید. علاوه بر این، نتایج اقدامات نیز رویدادها هستند. در رابطه با معماری مورد بحث، می توان گفت که رویدادهای محلی رویدادهایی هستند که در داخل یک اسکریپت PHP خاص رخ می دهند و رویدادهای راه دور رویدادهایی هستند که از صف AMQP به این اسکریپت آمده اند (یا در نتیجه به آنجا ارسال می شوند). اگر با تمام کدهای خود به این شکل رفتار کنید، بلافاصله به یک اثر شگفت انگیز و بسیار مهم منجر می شود:

اگر رویدادهای محلی و راه دور یکسان هستند، مدیریت رویدادهای محلی و راه دور یکسان هستند!

چرا اینقدر مهم است؟ از آنجا که برنامه نویسان تیم شما همچنان به نوشتن کدهای PHP معمولی ادامه می دهند، بدون اینکه فکر کنند این یا آن رویداد در کجا پردازش می شود - همان جا در این یا یک اسکریپت PHP همسایه، یا جایی در انتهای دیگر سیستم، در دیمون دیگری، در حداقل در مورد زبان برنامه نویسی دیگر. اگر در حال ساخت پروژه ای با یک API عمومی هستید، هر شرکت کننده شخص ثالث می تواند کد خود را در رویدادهای شما "امضا" کند (و آنها را پردازش کند)، یا برعکس - کد خود را برای شما ارسال کند تا رویدادهای آن را به عنوان پردازش کنید. درخواست‌ها (و اگر از مدل تجاری SAAS مانند آمازون استفاده می‌کنید، برای آن پول دریافت کنید).

به یاد بیاورید که ما آن را نقص اصلی پروژه های وب بزرگ کلاسیک نامیدیم - به طور مداوم در حال رشد استپیچیدگی، و در نتیجه هزینه مالکیت، هزینه پشتیبانی و تغییرات. در مورد یک معماری SOA مبتنی بر رویداد - پیچیدگی به طور مداوم در حال کاهش است، از آنجا که "گره های پیچیده" را می توان به راحتی به خدمات مستقل (در این مورد شیاطین) تقسیم کرد، در حالی که اصول سیستم بدون تغییر باقی می ماند و عملکرد آن فقط افزایش می یابد.

یک نسخه جدید را بدون از دست دادن فرآیندهای فعلی مستقر کنید

از آنجایی که دیگر یک سیستم یکپارچه ندارید، نیازی به استقرار کل سیستم ندارید. علاوه بر این، استقرار یک کامپوننت (سرویس، دیمون) ممکن است هر زمانی طول بکشد، البته در محدوده معقول. نکته مهم این است که در طول زمان استقرار (آن چند ثانیه یا چند ده ثانیه) کامپوننت، کل پروژه یک لحظه سرویس را قطع نمی کند. چگونه انجام می شود؟

شما به سادگی سرویسی را که باید به روز شود خاموش کنید. کد و ساختار پایگاه داده آن را به روز کنید (در صورت لزوم)، سپس آن را دوباره اجرا کنید. تمام درخواست‌های فعلی به این سرویس در صف AMQP منتظر می‌مانند تا سرویس بالا بیاید. توجه می کنم که از آنجایی که خدمات کوچک هستند (مقدار کمی از کد مورد نیاز برای حل بخش کوچکی از منطق کسب و کار) - این بسیار سریعتر از استقرار یک برنامه یکپارچه کامل است. اما در هر صورت ضرری نخواهد داشت.

مشکل در رابط وب

یک رابط وب سریع و پاسخگو پیش نیاز یک پروژه پر بار است. بیایید ببینیم که چرا رابط وب می‌تواند به طور کلی با رویکرد کلاسیک پیاده‌سازی «آهسته‌تر» شود:

1. اینترفیس روی باطن ترسیم شده است که بیش از حد بارگذاری شده و به آرامی این کار را انجام می دهد. انتقال بین صفحات کند است. حتی با AJAX، بلوک ها خیلی آهسته دوباره ترسیم می شوند.

2. کد منبع رابط (HTML، CSS، JS) اضافی است و به آرامی از طریق کانال های ارتباطی منتقل می شود، به خصوص اگر این کار در بارگذاری هر صفحه در طول ناوبری کاربر از طریق رابط انجام شود.

3. رابط حاوی مقدار زیادی منطق جاوا اسکریپت بهینه نشده است که در دستگاه های ضعیف (عمدتاً در دستگاه های تلفن همراه) کند است.

بیایید سعی کنیم این مشکلات را حل کنیم:

چگونه یک رابط وب سریع و واکنش گرا بسازیم

1. اول و مهمتر از همه، منبعرابط باید بیش از یک بار به مشتری منتقل نمی شود. تنها راه متمدن برای رسیدن به این هدف، ساختن یک برنامه کاربردی جاوا اسکریپت کامل است. یک بار برای مشتری بارگیری می شود (در این حالت می توانید یک پیش بارگذاری متحرک زیبا را نشان دهید) و سپس در تمام مدت کار با سرویس، مشتری دیگر نیازی به منتظر ماندن برای دانلود نخواهد داشت.

2. تمام انتقالات بین "صفحه نمایش" رابط وب باید انجام شود داخل یک برنامه جاوا اسکریپت، و در هیچ موردی به عنوان درخواست جداگانه به باطن. اصطلاحی برای آن وجود دارد، "برنامه وب یک صفحه"، که در آن پیمایش اساسا با تغییر "صفحه نمایش" انجام می شود، در حالی که محتوا نوار آدرسبه صورت پویا تغییر می کند و احساس کاملی از "انتقال صفحه" کلاسیک ایجاد می کند.

3. ارسال پیام (رویدادها) به باطن، و دریافت پاسخ، باید باشد جدا از یکدیگر و از ناوبری کاربر(نامتقارن). WebSocket فوق الذکر ذاتاً چنین پیاده سازی را "توصیه می کند". تمام عملیات طولانی همچنین نباید رابط را مسدود کنند، مگر اینکه به طور خاص برای این کار انجام شود.

بنابراین، کاربر فقط برای دانلود اولیه برنامه (چند ثانیه) نیاز به اتصال به اینترنت دارد. علاوه بر این، حتی در صورت عدم اتصال موقت (به عنوان مثال، از یک دستگاه تلفن همراه در مترو، خارج از شهر، در یک هتل شلوغ خارج از کشور و غیره) می تواند با این سرویس کار کند - برنامه درخواست ها را ضبط می کند و سعی می کند به محض ظاهر شدن اینترنت آنها را ارسال کنید و به همین ترتیب پاسخ دریافت خواهید کرد.

طبیعتاً این امر توسعه دهنده را از نیاز به بهینه سازی و به حداقل رساندن کد رها نمی کند. با این حال، همانطور که تمرین نشان می دهد (به عنوان مثال، سرویس Trello)، این کار دشوارتر از دیگران نیست.

نکته برای شک در توسعه دهندگان خدمات وب برای دستگاه های تلفن همراه: طبق رویه نویسنده، در سال 2013 برنامه های جاوا اسکریپت تک صفحه ای در حمل و نقل وب سوکت با موفقیت در iPad کار می کنند.

کار کاربر از چندین دستگاه

از سر کار، از خدمت شما استفاده می کند کامپیوتر رومیزی، در راه خانه یک آیفون بیرون می آورد و در خانه تبلت را روشن می کند. اگر کاربر دستوری را از رابط به سرویس ارسال کرده باشد، در این صورت منتظر پاسخ در مورد نتایج پردازش است. درک این موضوع آسان است که اگر (زمانی که) پردازش زمان ملموسی طول کشید، پاسخ باید به دستگاهی که کاربر از آن استفاده می‌کند ارسال شود (با عرض پوزش برای جناس) تحویل پاسخ، نه در زمان درخواست.

مشکل این است که نمی توان به صراحت گفت که آیا کاربر استفاده از این یا آن را متوقف کرده است (با عرض پوزش دوباره). دستگاه خاص. شاید مرورگر را بسته است. شاید باتریش تموم شده شاید او به سمت تونل مترو رفت که هیچ ارتباطی وجود ندارد و نیم دقیقه دیگر دوباره ظاهر می شود. گزینه های زیادی وجود دارد و نویسنده ناشناخته است بهترین راهتعاریف با این حال، در اینجا چیزی است که ممکن است مفید باشد:

1. تمام دستگاه های کاربر و زمان آخرین فعالیت را از هر یک از آنها ضبط کنید (در پشتیبان).

2. رویدادهای سیستمی را که باید به کاربر گزارش شود به مواردی که باید فقط به دستگاه‌های فعال تحویل داده شوند و رویدادهایی که باید «پخش شده» (به همه دستگاه‌ها) تحویل داده شوند، طبقه‌بندی کنید.

3. وارد کنید لایه اضافیانتزاعات - سرویسی که رویدادهای خاصی را که برای کاربر جالب است رهگیری می کند و پیام هایی از آنها تشکیل می دهد. بنابراین، می توانید به راحتی همان پیام را در مورد موفقیت عملیات پخش کنید - در چندین نوع: یک اعلان کوتاه در دستگاه موبایل، کمی معتبرتر - برای مرورگر، یک پیام دقیق - توسط پست الکترونیک.

4. در هر کانال ارتباطی جداگانه (رابط وب، دستگاه تلفن همراه، پست) صف هایی برای ارسال به هر کاربر ارائه دهید. عملکرد استاندارد AMQP همچنین به شما در مهلت‌های انقضای پیام کمک می‌کند تا بیش از یک زمان معین در آنجا نباشند و سیستم را مسدود نکنند. هنگامی که کاربر از طریق یک کانال خاص آنلاین می شود، پیام های تازه و معلق از آن نوع خاص به او تحویل داده می شود.

نویسنده می تواند اضافه کند که بر اساس همان سیستم، امکان ایجاد یک ارسال تاخیری اعلان ها (که زودتر از یک تاریخ خاص ارسال نمی شود) و حتی ارسال مکاتبات دوره ای کاغذی واقعی (اقدامات، پرداخت ها و غیره) وجود دارد. ، اما این موضوع برای یک مقاله جداگانه است.

من روی نکته اصلی تأکید می کنم: پیام های ارسال شده را به عنوان نوعی اعلان هایی که در فیس بوک یا Vkontakte به آن عادت دارید در نظر نگیرید. پیام های تحویلی هستند نتایج پرس و جوکاربر! تمام اقدامات کاربر در رابط، که مستلزم نوعی درخواست به باطن است، پاسخ ها را به شکل یکنواخت "پیام های تحویل شده" از طریق یک کانال ارتباطی واحد دریافت می کند. و سپس الگوریتم برنامه وب درک می کند که با این یا آن پیام چه کاری باید انجام شود - یک اعلان با متن بکشید، یک خط در جدول اضافه کنید، چیزی را در رابط تغییر دهید و غیره.

محاسبات موازی و ترتیبی

اگر بک اند کندی دارید، طراحی یک رابط وب پیشانی سریع بی فایده خواهد بود. نه، این نه در مورد نهرها است، نه در مورد چنگال ها و نه در مورد ارلنگ. ما روی PHP معمولی باقی می‌مانیم که در دسترس هر برنامه‌نویس مبتدی/متوسط ​​است.

اصلاً چرا به موازی سازی نیاز داریم؟ حتی اگر به طور کلی در مورد کاستی های زبان های تک رشته ای صحبت نکنیم، موازی سازی به طور قابل توجهی محاسبه کار را سرعت می بخشد، به این معنی که به میزان قابل توجهی نیاز به منابع سخت افزاری (سخت افزار) را کاهش می دهد و رضایت کاربر را از کار در آن افزایش می دهد. رابط (آنها سریعتر نتیجه می گیرند).

هر فرآیند تجاری نسبتاً پیچیده ای را در پروژه وب خود در نظر بگیرید و آن را به صورت زنجیره ای از مراحل ترسیم کنید. شما دنباله ای از اقدامات را در سیستم از درخواست تا پاسخ دریافت خواهید کرد. به احتمال زیاد ابتدا تعدادی بررسی، سپس اجرای وظیفه اصلی، سپس وظایف فرعی و در نهایت خروجی نتیجه وجود خواهد داشت. با دقت نگاه کنید: آیا می توان هر یک از اقدامات را به صورت موازی انجام داد؟

بگذارید مثالی برای شما بزنم: فرض کنید کاربر می خواهد خدماتی را خریداری کند که به عنوان یک گزینه پولی اضافی در طرح تعرفه او گنجانده شده است. تعداد گزینه ها محدود است. اگر این گزینه با موفقیت روشن شد، باید یک اعلان برای کاربر در مرورگر ارسال کنید، یک ایمیل تکراری ارسال کنید، از حساب صورت‌حساب او پول کم کنید و به بخش مشتری اطلاع دهید. یک زنجیر بکشید:

1. سیستم درخواستی برای فعال کردن گزینه دریافت کرد.
2. ما به کاربر مجوز می دهیم و از طرح تعرفه او مطلع می شویم.
3. بررسی می کنیم که آیا اصلاً امکان فعال کردن این گزینه وجود دارد یا خیر طرح تعرفهکاربر.
4. بررسی کنید که آیا کاربر پول کافی در حساب دارد یا خیر.
5. بررسی کنید که آیا این گزینه با سایر تنظیمات مغایرت دارد یا خیر.
6. اگر همه چیز درست است، گزینه را فعال کنید.
7. ما یک اعلان به مرورگر ارسال می کنیم.
8. ما یک اعلان از طریق پست ارسال می کنیم.
9. حذف پول در صورتحساب.
10. ما به بخش مشتری اطلاع می دهیم.

یک خواننده با دقت ممکن است با توالی اقدامات ایراد بگیرد، اما نویسنده به شما یادآوری می کند که این یک نمونه تقریبی است.

ما چه می بینیم؟ توجه داشته باشید که دلیلی برای انجام تمام مراحل به صورت متوالی وجود ندارد. "موازی کردن" 3،4،5 به سه رشته، و در پایان - 7،8،9،10 به چهار موضوع بسیار صحیح تر است.

به جریان ها و چنگال ها فکر می کنید؟ بیهوده، شما SOA دارید!

نحوه انجام محاسبات موازی در SOA

برای خوانندگانی که به تازگی مقاله را به این نقطه پیمایش کرده اند، توضیح خواهم داد که این مربوط به موازی کردن همان کار در SOA نیست - برای این، در حالت کلی، کافی است دیمون را در N نمونه اجرا کنید و مراقب باشید. بحث دسترسی به پایگاه داده

بنابراین، در این مثال، ما سه-چهار-چند وظیفه مختلف داریم که توسط سرویس های مختلف انجام می شود و می خواهیم آنها را به صورت موازی اجرا کنیم. ارسال آنها به پردازش موازی دشوار نیست: کافی است یک رویداد ارسال کنید "آیا کاربر نام کاربری گزینه X را فعال می کند؟"، و همه سرویس های مشترک در این رویداد آن را می گیرند، بررسی های خود را انجام می دهند و رویدادهای حاصل را ارسال می کنند.

مشکل دقیقاً جمع‌آوری این رویدادهای ناشی از آن است که برای ادامه کار به نتیجه کل کار آنها نیاز داریم. به عنوان مثال، در لیست بالا به نتیجه 3+4+5 نیاز داریم و 7+8+9+10 را می توان نادیده گرفت.

در واقع، با الزامات بالا برای تکمیل اجباری تراکنش ها، باید هر زنجیره را تا انتها کنترل کنید، اما در ادامه به این موضوع خواهیم پرداخت.

طبیعتاً، اگر دیمون ما «آویز و منتظر بماند» و منابع را مصرف کند (به اصطلاح «بیکار»)، پس نمی‌توانید چنین سرویسی با بارگذاری بالا بسازید. نکته فقط این است که شیطان وظایف دیگر را حل می کند و درخواست های دیگر مشتریان را ارائه می دهد، در حالی که سه "رشته" جداگانه (3،4،5) وظایف فرعی خود را حل می کنند. مشکلات نیز با این واقعیت اضافه می شود که رویدادهای حاصل می توانند به ترتیب دلخواه رخ دهند. با این حال، همه اینها به راحتی و به سادگی حل می شود:

تا آنجا که نویسنده می داند، هیچ یک از پیاده سازی های خارج از جعبه AMQP که امروزه وجود دارد، امکان انتظار و "چسباندن" چندین رویداد را در یک رویداد به منظور دریافت تنها آن - یک نتیجه، فراهم نمی کند. پس باید خودت ازش مراقبت کنی، مثل این:

1. قبل از ارسال رویداد به AMQP، متعهد شوید حافظه سریع(از هر فضای ذخیره سازی مناسب در حافظه استفاده کنید) فهرستی از نام رویدادهای حاصل که سرویس انتظار دارد دریافت کند، و همچنین نام رویداد (بیایید آن را "R" بنامیم) که باید با مجموع ارسال شود. نتایج.

2. پس از آن، سرویس به چرخه پردازش رویداد جاری پایان می دهد و برای کارهای بعدی آزاد می شود.

3. به محض ورود هر رویدادی از لیستی که در حافظه ذخیره کرده ایم، سرویس آن را «باز می کند» و محتویات آن را در حافظه ذخیره می کند و آن را به نام رویداد نسبت می دهد. در همان زمان، بررسی می کند که آیا رویدادهای دیگری در این لیست وجود دارد که برای آنها پاسخی دریافت نشده است. اگر اینطور باشد، حلقه در آن نقطه به پایان می رسد.

4. در غیر این صورت، یعنی اگر همه رویدادهای لیست پاسخ دریافت کنند، سرویس آنها را به هم می چسباند و نتیجه چسبانده شده را تحت نام رویداد "R" که قبلاً به خاطر سپرده شده است ارسال می کند. پس از آن، به منظور صرفه جویی در حافظه، لیست نتایج به سادگی از حافظه حذف می شود - دیگر مورد نیاز نیست.

5. همان سرویس یا سرویس دیگری (به صلاحدید طراح سیستم) رویداد حاصل "R" را با تمام نتایج پردازش موازی دریافت می کند. آنچه در ادامه می آید آشکار است.

اگر از توضیحات به نظر می رسید که این مدت طولانی است، پس توضیح خواهم داد - ما در مورد هزاران و ده ها هزار رویداد در ثانیه (!) در یک سرور متوسط ​​صحبت می کنیم.

استفاده از فضای ذخیره سازی در حافظه به این معنی است که حتی اگر سرویس متوقف شود (سقوط، به روز رسانی)، روند کسب و کار فعلی از بین نخواهد رفت. پس از شروع مجدد سرویس، به دریافت رویدادها از ESB و پردازش آنها بر اساس الگوریتم توضیح داده شده در بالا ادامه خواهد داد.

سناریوهای تراکنش، بازگشت عملیات (بازگشت) و شکست در SOA

از آنجا که در SOA، رویدادهای همتا از طریق ESB «راه می‌روند»، به نوعی نشانه نیاز دارید که نشان دهد «این پاسخ اینجا» به «آن درخواست در آنجا» اشاره دارد. در اینجا نیازی به اختراع مجدد چرخ نیست - در مشخصات هر پروتکل محبوب، پارامتری با نامی مانند correlation_id پیدا خواهید کرد. به طور معمول، این یک رشته است. باید در پارامترهای همه رویدادهای هر فرآیند تجاری، از ورود تا خروج، گنجانده شود تا زنجیره پیام متعلق به این فرآیند تجاری شناسایی شود. با نگاه کردن از سمت رابط وب، برنامه وب درخواست‌های فعال (ارسالی) فعلی را ذخیره می‌کند و با correlation_id، «می‌فهمد» که هر پاسخ خاص در پاسخ به کدام درخواست آمده است.

بیایید به اصطلاح بپردازیم: تراکنشی ویژگی یک سیستم برای انجام چندین عمل به عنوان یک عملیات مشترک است که منطقی است و فقط می تواند به طور کامل تکمیل شود. از آنجایی که از نظر فیزیکی انجام چندین عملیات به صورت اتمی در یک سیستم توزیع شده با رشته های موازی غیرممکن است، سیستم به اصطلاح سناریوهای شکست و بازگشت را فراهم می کند.

سناریوی شکست عموماً اقدامی است که هنگام وقوع خطا انجام می شود. بازگشت مجدد، در این زمینه، یک اسکریپت از اقداماتی است که باید انجام شود تا یک سری اقدامات قبلی که در نهایت منجر به خطا شده اند، انجام شود. به طور کلی، بازگشت به عقب فرآیندهایی هستند که معکوس فرآیندهای تجاری عادی در سیستم هستند.

بازگشت به عقب همیشه مورد نیاز نیست و همیشه امکان پذیر نیست. برای مثال، اگر گزینه‌ای را به کاربر متصل کرده باشید و سپس روی صورت‌حساب «افتاده»، آنگاه می‌توان آن گزینه را دوباره غیرفعال کرد. خوب، پس می توانید دوباره امتحان کنید، به طور خودکار یا با دستور دوم از کاربر. و اگر به صورت فیزیکی محتوا را حذف کردید و برخی از عملیات های بعدی جواب نداد ... وضعیت مبهم است.

بنابراین، سناریوهای شکست و عقبگردها باید عاقلانه برخورد شود. نویسنده ممکن است مسیر زیر را توصیه کند: همیشه اسکریپت های شکست خورده بنویسید، اما همیشه بازگردانی ننویسید. شکست های شما فوراً در نظارت منعکس می شود - و تیم پشتیبانی می تواند به سرعت وضعیت را با دستان خود برطرف کند، تجربه کسب کند و الزامات فنی را برای برنامه نویسان تدوین کند. در حالی که نوشتن یک بازگشت صحیح منحصر به فرد از ابتدا می تواند بسیار بسیار دشوار باشد.

با این وجود، باید درک کرد که عقب‌نشینی‌ها، عقب‌نشینی‌ها، مدیریت موقعیت‌های نادرست همان فرآیندهای تجاری مانند هر چیز دیگری هستند. آنها همچنین بر اساس رویدادهایی هستند که از سیستم عبور می کنند. خوب است اگر در ابتدا دو مسیر متضاد (به جلو و عقب، موفقیت و شکست) را در هر رویداد و هر کنترل کننده قرار دهید تا آنها را در وظایف خاص پیاده سازی کنید.

مقیاس بندی SOA

هر سیستمی دیر یا زود نیاز به گسترش دارد. در مورد SOA، این کار به راحتی و به طور طبیعی انجام می شود:

1. نقطه ورودی تکراری. این به همان دروازه WebSocket اشاره دارد که در ابتدای مقاله در نظر گرفتیم. می توان آن را به تعداد نامحدودی کپی کرد، زیرا ارتباط بین آن و مشتری یکپارچه و از درونیات سیستم جدا شده است و ارتباط بین آن و سیستم به نوبه خود از ارتباطات با مشتری جدا می شود.

2. نمونه های تکراری(نمونه) خدمات. سرویس هایی که نیازی به پایگاه داده ندارند یا فقط از آنها "خوانده می شوند" به طور یکپارچه کپی می شوند. و عملکرد منظم RabbitMQ به شما این امکان را می دهد که N نمونه را در یک صف مشترک کنید، پیام هایی که از آنها به طور تصادفی در یک یا آن نمونه می رسد. هنگام کپی کردن سرویس هایی که با برنامه های کاربردی خارجی سروکار دارند (پایگاه های داده، نرم افزارهای شخص ثالث)، باید در نظر بگیرید که این برنامه ها چگونه درخواست های تراکنشی را از چندین مشتری موازی ارائه می کنند.

3. تکراری شدن انبارهای داده. در اینجا شما آزاد هستید که از هر اشتراک گذاری که برایتان شناخته شده است استفاده کنید. اگر با 10 میلیون کاربر سر و کار دارید و این به نظر شما زیاد است، آن را بر 10 میلیون مبنا تقسیم کنید (مثلاً بر اساس CRC32 از ورود کاربر یا روش دیگری که به صورت دور برگشتی است). اگر پایگاه داده یک سرویس به طور مداوم رشد می کند و پیچیده تر می شود، آن را به دو سرویس تقسیم کنید.

4. کپی کردن یک کارگزار AMQPو ذخیره سازی در حافظه از روی تمرین نویسنده، RabbitMQ و Redis نقش خود را کاملاً ایفا می کنند. اگر تجهیزاتی در بیش از یک رک DC دارید، حالت خرگوشی را انتخاب کنید که در برابر خرابی اتصال شبکه تحمل کند.

5. تکثیر کامل ماشین. از جانب فن آوری های مدرنمجازی سازی (KVM) و پیکربندی (Chef)، وظیفه "بالا بردن همان ماشین" به فشار دادن یک دکمه خلاصه می شود.

رمزگذاری ترافیک بین فرانت اند و باطن

توصیه می شود یک اتصال WebSocket را از طریق SSL سازماندهی کنید. علاوه بر این، "نفوذ" را در برابر ارائه دهندگان آفیس عقب مانده که "هر گونه ترافیک عجیب و غریب" را به جز HTTP[S] مسدود می کنند، افزایش می دهد.

با این حال، اگر این برای شما ناکافی به نظر می‌رسد، می‌توانید تولید جفت‌های کلید را با هر ورود کلاینت ترتیب دهید (یک جفت در قسمت جلویی، دومی در انتهای پشت)، کلیدهای عمومی را مبادله کنید و بیشتر ترافیک را با RSA معمولی رمزگذاری کنید.

محافظت در برابر DDOS و سوء استفاده های مشابه

نویسنده عمداً سؤال حفاظت "سطح پایین" را در مورد سیلاب SYN و کانال های سیل با صدها گیگابیت حذف می کند، زیرا صدها کتاب ادبیات تخصصی در این مورد نوشته شده است. بیایید در مورد نحوه محافظت از سیستم در حال حاضر در سطوح منطقی آن صحبت کنیم، زمانی که یک مهاجم راهی برای "سیل" کردن سیستم شما (SOA + ESB) با هزاران رویداد پیدا کرده است.

1. قانون اول: هیچ چیزی نباید تا زمانی که اعتبار آن تایید نشده است پردازش شود. اگر انتظار دارید یک متن کوچک در JSON به عنوان ورودی در BASE64 پیچیده شده باشد، رشته ورودی طولانی تر از یک مگابایت باید به صراحت کنار گذاشته شود - سعی نکنید آن را باز کنید. رشته ای که شامل کاراکترهای «غیر لاتین» است مشابه است. وقتی رشته را باز کردید، سعی نکنید بلافاصله json_decode را انجام دهید، ابتدا تعداد و برابری پرانتزها را بررسی کنید. و غیره.

به نظر می رسد پارانویا است، اما در غیر این صورت می توانید به راحتی "از حافظه پر شوید"، یعنی منجر به خرابی سرویس شوید و آن را مجبور کنید که تمام RAM موجود در آن را اشغال کند.

2. سرویسی که پیام های دریافتی را پردازش می کند، نباید چیزی در حافظه، پایگاه داده و سایر حافظه ها بنویسد. دلیلش هم همینه ابتدا مطمئن شوید که پیام به طور کلی معتبر است و تنها پس از آن اجازه دهید "عمیق تر" به سیستم وارد شود.

3. سرویسی که اغلب می تواند مجبور به «رفتن به پایگاه داده» شود، باید توسط کش محافظت شود. یک مثال ساده یک سرویس مجوز کاربر است. اگر محافظت نشده باشد، مهاجم می تواند هزاران درخواست مجوز را پشت سر هم ارسال کند و در نتیجه پایگاه داده را بیش از حد بارگذاری کند.

4. در ورودی سیستم، به سرویسی نیاز دارید که درخواست های منابع "مشکوک" را رد کند، به عنوان مثال، از آدرس های IP از "لیست سیاه".

به نظر می رسد توصیه کلاسیک است، بنابراین معامله چیست؟ مشکل اصلی این است که اگر سرویس آنالایزر-فیلتر را در ورودی سیستم قرار دهیم (همانطور که در پروژه های وب کلاسیک انجام می شود)، عملکرد کل سیستم از عملکرد این فیلتر-آنالیزور بالاتر نخواهد بود. بررسی هر گزارش برای مشکوک بودن «در زمان واقعی» بسیار گران است. شما می توانید و باید این کار را بعد از واقعیت انجام دهید، و در اینجا چگونه است:

1. سرویسی بسازید که به تمام پیام های سیستم "گوش دهد". در RabbitMQ این با عضویت در کلید مسیریابی "#" به دست می آید.

2. به این سرویس قوانین خاصی را آموزش دهید که با استفاده از آنها می تواند فرستنده های "مشکوک" را تشخیص دهد. به عنوان مثال، اینها فرستنده هایی هستند که پیام های مشابه زیادی را در یک بازه زمانی ارسال می کنند، یا پیام های مکرر ارسال می کنند، یا از طرف یک کاربر از آدرس های IP مختلف پیام ارسال می کنند ... گزینه های زیادی وجود دارد، خود را روشن کنید. خیال پردازی. در عین حال، مهم نیست که چنین سرویسی با چه سرعتی کار می کند (البته در محدوده معقول) - بر سرعت کل سیستم تأثیر نمی گذارد.

3. به محض اینکه سرویس به این نتیجه برسد که فلان فرستنده مشکوک است، این رویداد را به سیستم ارسال می کند و به کار خود ادامه می دهد.

4. یک دیمون بسیار ساده و سریع را در ورودی قرار دهید - یک سرویس فیلتر که وظیفه آن به سادگی مسدود کردن "احمقانه" فرستنده های مشکوک خواهد بود. بدون تجزیه و تحلیل، بدون تجزیه، بدون هزینه اضافی. به راحتی می توان حدس زد که چه کسی مشکوک در نظر گرفته می شود: این سرویس از رویدادهای شرح داده شده در پاراگراف قبلی در مورد آنها مطلع می شود و آنها را به لیست سیاه داخلی خود اضافه می کند.

پایان قسمت اول. ادامه: SOA: معماری توزیع شده و نگهداری آن.

من در پروژه های IT مربی هستم. این به این معنی است که اگر مالک یا رهبر هستید، من می‌توانم به شما کمک کنم تا ارتفاعات جدیدی را به دست آورید. فرآیندها را تمیز کنید، انگیزه تیم را درک کنید، ابزارها را پیاده سازی کنید و به اهداف خاص دست یابید. من به شما یاد نمی دهم که چگونه تجارت کنید، بلکه فقط به دور زدن چنگک پراکنده سخاوتمندانه در راه شما کمک می کنم. .