معماری میکروسرویس چیست؟ — به زبان ساده

۳۴۷۸ بازدید
آخرین به‌روزرسانی: ۲۷ شهریور ۱۴۰۲
زمان مطالعه: ۹ دقیقه
معماری میکروسرویس چیست؟ — به زبان ساده

معماری میکروسرویس (Microservices Architecture) به مرور در حال کسب محبوبیت فزاینده‌ای است و امروزه تقریباً در همه پروژه‌های نرم‌افزاری عمده از آن استفاده می‌شود. دلیل اصلی این مسئله ناشی از مزیت‌های آن و مسائلی است که حل می‌کند. هدف ما در این مقاله ارائه مروری از معماری میکروسرویس به همراه مزیت‌ها و معایب آن است.

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

میکروسرویس چیست؟

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

میکروسرویس‌ها بر مبنای معماری مبتنی بر سرویس ساخته شده‌اند.

معماری مبتنی بر سرویس یا «Services Oriented Architecture» به اختصار (SOA) به اپلیکیشن‌ها امکان ارتباط با یکدیگر روی یک رایانه منفرد و یا در زمان توزیع اپلیکیشن‌ها روی چندین رایانه در یک شبکه را ارائه می‌کند. هر میکروسرویس ارتباط اندکی با سرویس‌های دیگر دارد. این سرویس‌ها خودکفا هستند و یک کارکرد منفرد (یا گروهی از کارکردهای مشترک) را ارائه می‌کنند.

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

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

سابقه تکامل برنامه‌نویسی نرم‌افزار

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

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

تکامل رایانه‌ها

ابتدا باید با فرایند تکامل رایانه‌ها در طی زمان آشنا شویم. در تصویر زیر این تکامل سیستم‌های رایانه‌ای به صورت خلاصه ارائه شده است:

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

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

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

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

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

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

تکامل نرم‌افزار

تکامل نرم‌افزار نیز چرخه مشابه را طی کرده است.

تصور زیر فرایند تکامل نرم‌افزار را نشان می‌دهد:

Software Evolution

  • نسل اول (اواخر دهه 1970 میلادی): مفاهیم شیءگرایی مانند وراثت، کپسوله‌سازی، و چندریختی معرفی شدند تا قابلیت استفاده مجدد از کد و قابلیت نگهداری فراهم شوند.
  • نسل دوم (دهه 1990 میلادی): اپلیکیشن‌ها با استفاده از معماری لایه‌بندی شده طراحی و پیاده‌سازی شدند. معماری لایه‌بندی شده برای کاهش در هم تنیدگی زیاد بین اجزای مختلف اپلیکیشن‌های نرم‌افزاری معرفی شد. در نتیجه تست نرم‌افزار و زمان برای تحویل نرم‌افزار کاهش یافت. اپلیکیشن‌ها همچنان یکپارچه بودند، یعنی یک واحد منفرد که همه کارکردهای اپلیکیشن وجود داشت در آن کپسوله‌سازی شده بود.
  • نسل سوم (دهه 2000 میلادی): در این زمان اپلیکیشن‌های با توزیع مبتنی بر سرویس معرفی شدند. روش طراحی مورد استفاده در این نسل شامل تعریف سرویس ریموت بود که به اپلیکیشن‌ها امکان مقیاس‌بندی و توزیع روی ماشین‌های چندگانه را می‌داد.

پیش از معرفی میکروسرویس‌ها، اغلب اپلیکیشن‌های سازمانی یکپارچه بودند. در ادامه مشکلات این پارادایم را بررسی می‌کنیم.

معماری اپلیکیشن‌های نرم‌افزاری در بازه‌های زمانی اخیر و مشکلات این معماری‌ها

در سال‌های اخیر اپلیکیشن‌های نرم‌افزاری در معماری‌های چندلایه پیاده‌سازی می‌شدند.

به عنوان نمونه در تصویر زیر یک معماری معمول برای اپلیکیشن نرم‌افزاری سازمانی ارائه شده است:

هر لایه در کد نرم‌افزاری پیاده‌سازی می‌شد و از چندین کلاس و اینترفیس تشکیل یافته بود:

لایه داده

این لایه برای ذخیره‌سازی داده‌ها در پایگاه داده و فایل‌ها پیاده‌سازی شده است. تنها مسئولیت این لایه ارائه داده‌ها از منابع داده‌ای مختلف است.

لایه تجاری

مسئولیت لایه تجاری، بازیابی داده‌ها از لایه داده و اجرای محاسبات است. لایه تجاری نمی‌داند که داده‌ها در فایل یا پایگاه داده یا در موارد دیگر هستند. لایه تجاری به لایه داده‌ها وابسته است.

لایه سرویس

لایه سرویس روی لایه تجاری قرار می‌گیرد. این لایه یک پوشش برای لایه تجاری ایجاد می‌کند که شامل امنیت/گزارش‌گیری/وساطت برای فراخوانی کارکردها است. لایه سرویس به لایه تجاری وابسته است.

لایه میزبانی

سرویس‌ها از طریق این لایه میزبانی می‌شوند. لایه میزبانی از فناوری‌های مبتنی بر سرویس مانند WCF یا REST API برای میزبانی با استفاده از پروتکل‌های مختلف مانند http ،https ،tcp ،named pipes و غیره بهره می‌گیرد. کل اپلیکیشن به صورت یک پردازش نرم‌افزاری برای نمونه سرویس ویندوز یا وب‌سرویس IIS اجرا می‌شود. اپلیکیشن‌ها از طریق URL مانند https://example.com/myapplication در دسترس هستند.

لایه رابط کاربری

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

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

طراحی یکپارچه لایه‌بندی شده مزایای زیادی دارد؛ اما نواقصی نیز دارد. در ادامه فهرستی از مشکلات رایج این نوع معماری را مورد اشاره قرار داده‌ایم:

  1. این طراحی برای مقیاس‌بندی و نگهداری اپلیکیشن سرراست نیست. این طراحی زمان مورد نیاز برای قرار دادن کارکردهای جدید در اختیار کاربر را افزایش داده است، چون چرخه توسعه زمان بیشتری طول می‌کشد.
  2. از آنجا که کل اپلیکیشن به صورت یک پردازش منفرد میزبانی می‌شود، هر بار که لازم باشد یک به‌روزرسانی اجرا شود، کل اپلیکیشن باید متوقف شود و سپس نسخه جدیدی از اپلیکیشن باید توزیع شود.
  3. برای ایجاد تعادل در بار کاری، کل اپلیکیشن نرم‌افزاری روی چند ماشین توزیع می‌شود. به علاوه، امکان توزیع کارکردهای خاص روی سرورهای چندگانه برای متوازن‌سازی بار وجود ندارد.
  4. طراحی اپلیکیشن پیچیده است، چون همه ویژگی‌ها در یک اپلیکیشن یکپارچه منفرد پیاده‌سازی شده‌اند.
  5. زمانی که تعداد اپلیکیشن‌ها در سازمان افزایش می‌یابد، توزیع اپلیکیشن‌های یکپارچه نیازمند اطلاع‌رسانی و هماهنگی با همه تیم‌های توسعه ویژگی‌های جدید است. این امر موجب افزایش زمان مورد نیاز برای تست و توزیع اپلیکیشن می‌شود.
  6. بدین ترتیب در طراحی یک «نقطه شکست منفرد» (Single Point of Failure) ایجاد می‌شود که یک خطای غیر قابل بازیابی منفرد نمی‌تواند پردازشی را که اپلیکیشن روی آن میزبانی شده است متوقف کند.
  7. این معماری اپلیکیشن را وادار می‌کند که در یک مجموعه فناوری منفرد پیاده‌سازی شود.
  8. در مواردی که زمان تحویل طولانی‌تر شود، در طی زمان نیازمند پول بیشتری برای توسعه و نگهداری اپلیکیشن خواهد بود.
  9. از آنجا که همه کد درون یک اپلیکیشن منفرد قرار دارد، نگهداری کد پس از مدتی به سرعت دشوار می‌شود.

معماری میکروسرویس چگونه است؟

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

میکروسرویس‌ها برای حل مسائل اشاره شده فوق معرفی شده‌اند. معماری میکروسرویس یک بهینه‌سازی در زمینه معماری فوق‌الذکر محسوب می‌شود. در این معماری هر کارکرد تجاری به صورت یک سرویس ارائه می‌شود. هر سرویس می‌تواند به صورت مستقل از سرویس‌های دیگر میزبانی و توزیع شود.

معماری میکروسرویس

Microservices Architecture
<a href="https://blog.faradars.org/wp-content/uploads/2018/12/1-ZbVgTZr04isL-KnzbFymmw.png">برای مشاهده تصویر در ابعاد اصلی روی این لینک کلیک کنید.</a>

هر سرویس را می‌توان یک اپلیکیشن کوچک تصور کرد.

  • همه سرویس‌ها می‌توانند حتی زمانی که سرویس‌ها روی ماشین‌های مختلف هستند، با همدیگر ارتباط داشته باشند. این وضعیت در ادامه امکان پیاده‌سازی کارکردهای جدید در سرویس‌ها را فراهم می‌سازد.
  • میکروسرویس‌ها، سازمان‌ها را تشویق می‌کنند که از فرایند توزیع و تحویل پیوسته خودکار پیروی کنند.
  • اپلیکیشن‌ها در نهایت بسیار پایدارتر می‌شوند، چون هر ویژگی می‌تواند به صورت مستقلانه تست و توزیع شود.
  • از آنجا که هر سرویس روی پردازش مجزایی میزبانی می‌شود، اگر یک سرویس به نقطه تنگنای اپلیکیشن تبدیل شود و به منابع زیادی نیاز داشته باشد، در این صورت می‌توان آن را بدون هیچ گونه تأثیر سوء روی سرویس‌های دیگر، به ماشین دیگری انتقال داد.
  • زمانی که کاربران بیشتری شروع به استفاده از یک ویژگی اپلیکیشن بکنند، آن سرویس می‌تواند با توزیع روی رایانه‌های قدرتمندتر یا از طریق استفاده از کش بدون این که روی سرویس‌های دیگر هیچ تأثیری بگذارد، مقیاس‌بندی شود.
  • این معماری پایداری اپلیکیشن را نیز افزایش می‌دهد، چون هر سرویس می‌تواند به صورت مستقلانه ساخته، تست، توزیع و استفاده شود.
  • کد اپلیکیشن می‌تواند به سادگی نگهداری شود و پردازش‌ها می‌توانند به صورت مجزا تحت نظارت قرار بگیرند.
  • توسعه‌دهندگان اختصاصی می‌توانند سرویس‌ها را به صورت مستقل از هم پیاده‌سازی کرده و این سرویس‌ها را بدون تأثیرگذاری روی سرویس‌های دیگر انتشار دهند.
  • بدین ترتیب نقطه شکست منفرد نیز از بین می‌رود، زیرا یک سرویس می‌تواند بدون تأثیرگذاری روی ویژگی‌های دیگری که اپلیکیشن نرم‌افزاری ارائه می‌کند، متوقف شود.
  • در نتیجه این طراحی زمان مورد نیاز برای تحویل نسخه‌های جدید را کاهش می‌دهد و بنابراین هزینه را در طی زمان کاهش می‌دهد.
  • قابلیت استفاده مجدد از کد افزایش می‌یابد، زیرا یک ویژگی به صورت سرویس میزبانی شده است و امکان استفاده چند سرویس از یک ویژگی به جای پیاده‌سازی مجدد کد در هر مورد وجود دارد.
  • معماری مبتنی بر سرویس امکان استفاده از مجموعه متنوعی از فناوری برای رفع نیازها وجود دارد. به عنوان نمونه بسته‌های تحلیل داده زبان R یا پایتون می‌توانند به صورت مجزا توزیع و میزبانی شوند و همزمان می‌توان از C#.Net برای پیاده‌سازی سرویس‌ها استفاده کرد. به علاوه می‌توان از NodeJS در سمت سرور استفاده کرد و AngularJs و ReactJs نیز برای پیاده‌سازی رابط کاربری مورد استفاده قرار گیرند. هر ویژگی تجاری می‌تواند با استفاده از مجموعه مختلفی و از طریق تیم متفاوتی، مستقل از ویژگی‌های دیگر پیاده‌سازی شود.

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

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

عیب معماری میکروسرویس چیست؟

معماری میکروسرویس نیز برای خود نقایصی دارد که در ادامه فهرستی از آن‌ها را ارائه کرده‌ایم.

  1. برای ساختن هر میکروسرویس به یک گردش کاری متفاوت برای ایجاد، توزیع و انتشار نیاز داریم. از این رو مطمئن شدن از این که گردش کاری توسعه خودکار است کاملاً حائز اهمیت است، چون در غیر این صورت، بار کاری تیم‌های عملیات IT افزایش می‌یابد.
  2. از آنجا که هر سرویس به صورت پردازش مجزایی پیاده‌سازی شده است، ابزارهای نظارت و نگهداری برای هر پردازش به صورت مستقل مورد نیاز هستند. این فرایند یک فرایند یک‌باره محسوب می‌شود؛ اما این گزارش‌ها و ابزارها نیز خود باید مورد نظارت قرار بگیرند. مجموعه ELS یا Prometheus به این منظور مناسب هستند.
  3. میکروسرویس‌ها بار کار را روی سیستم پیکربندی افزایش می‌دهد. پیکربندی سرویس‌های بیرونی معمولاً از طریق سرویس‌های مشترک صورت می‌گیرند و می‌توانند به وظیفه‌ای زمان‌بر تبدیل شوند.
  4. لغو یک وظیفه در حال اجرا زمانی که سرویس به صورت اپلیکیشن مستقلی توزیع یافته است، دشوارتر می‌شود.
  5. این طراحی می‌تواند روی عملکرد تأثیر منفی بگذارد، زیرا سربار شبکه برای هر فراخوانی به یک سرویس وجود دارد. در این موارد معمولاً از کَش کردن و همزمانی برای بهبود عملکرد استفاده می‌شود.
  6. میکروسرویس‌ها مشکلاتی در ارتباط با محاسبات توزیع یافته مانند امنیت، تراکنش‌ها، همزمانی و غیره ایجاد می‌کنند.
  7. تست‌های پایداری در سطح اپلیکیشن جهت تضمین این که ویژگی‌های جدید، کارکرد سرویس‌های موجود را از کار نمی‌اندازند، ضروری است.
  8. این معماری می‌تواند موجب افزایش کار مستندسازی برای هر سرویس بشود، چون هر سرویس نسخه، برنامه انتشار و چرخه انتشار خاص خود را دارد.
  9. زمانی که تعداد اپلیکیشن‌ها و کد آن افزایش می‌یابد، نگهداری و مدیریت صحیح آن به وظیفه دشواری تبدیل می‌شود.

فناوری‌ها و نرم‌افزارهای مورد نیاز برای پیاده‌سازی میکروسرویس‌ها

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

  • Microsoft Azure Container Service  و Azure Service Fabric
  • CoreOs
  • Docker
  • Swarm
  • Kubernetes
  • Mesosphere
  • OpenShift
  • Apprenda

سخن پایانی

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

معماری میکروسرویس معایب خاص خود را نیز دارد، چون ارتباط بین سرویس‌ها از طریق فراخوانی API به جای فراخوانی‌های درون حافظه‌ای و درون پردازشی صورت می‌گیرد؛ اما از سوی دیگر در زمینه توسعه، تست، نگهداری و توزیع مستقل ویژگی‌های مختلف، انعطاف‌پذیری ایجاد می‌کند. امیدواریم این نوشته مورد توجه شما قرار گرفته باشد، هر گونه سؤال یا پیشنهاد خود را در بخش نظرات این نوشته با ما و دیگر خوانندگان فرادرس در میان بگذارید.

اگر این نوشته برای شما مفید بوده است، پیشنهاد ما استفاده از آموزش‌های زیر است:

==

بر اساس رای ۲۶ نفر
آیا این مطلب برای شما مفید بود؟
اگر بازخوردی درباره این مطلب دارید یا پرسشی دارید که بدون پاسخ مانده است، آن را از طریق بخش نظرات مطرح کنید.
منابع:
fintechexplained
۵ دیدگاه برای «معماری میکروسرویس چیست؟ — به زبان ساده»

متشکرم خیلی عالی و مفید بود

سلام خسته نباشید.
واقعا متن خوب و مفیدی بود.
ممنونم از شما

سلام.
جای یک اموزش ویدئویی کامل درباره مبحث میکروسرویس ها در .net در فرادرس کاملا احساس میشه.
امیدوارم فرادرس اولین سایت فارسی زبانی باشه که یک آموزش جامع درباره مبحث میکروسرویس ها ارائه کنه.
با تشکر از سایت خوب شما.

سلام وقت بخیر ، ممنون از مطلب مفید و قابل اطمینانی که به اشتراک گذاشتید
آیا فرادرس ، آموزش ویدیویی در این زمینه داره که به توسعه دهندگان وب کمک کنه ؟

سلام و عرض وقت بخیر؛

آموزش‌های ویدیویی فرادرس در زمینه طراحی و برنامه‌نویسی وب از این آدرس در دسترس هستند:
https://faradars.org/how-to-learn/web-design-and-programming

شما می‌توانید جهت پیدا کردن آموزش مورد نیاز خود این فهرست را بررسی کرده یا جهت دریافت راهنمایی بیشتر و دقیق‌تر از طریق شماره تماس ۵۷۹۱۶۰۰۰ با واحد مخاطبین فرادرس ارتباط برقرار کنید.

نظر شما چیست؟

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *