Skip to content

Latest commit

 

History

History
155 lines (96 loc) · 22.3 KB

Competing Consumers pattern.md

File metadata and controls

155 lines (96 loc) · 22.3 KB

Competing Consumers pattern

چندین مصرف‌کننده هم‌زمان (concurrent consumers) را فعال کنید تا پیام‌های دریافت شده در یک کانال پیام‌رسانی (messaging channel) را پردازش کنند. با چندین مصرف‌کننده هم‌زمان، یک سیستم می‌تواند چندین پیام را به طور هم‌زمان پردازش کند تا توان عملیاتی را بهینه کند، مقیاس‌پذیری (scalability) و دردسترس‌بودن (availability) را بهبود بخشد و حجم کار (workload) را متعادل کند.

زمینه و مشکل

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

تعداد درخواست‌ها به دلایل زیادی در طول زمان می‌تواند به طور قابل‌توجهی متفاوت باشد. افزایش ناگهانی فعالیت کاربر یا درخواست‌های انبوهی که از چندین tenant می‌آیند می‌تواند باعث ایجاد حجم کاری غیرقابل‌پیش‌بینی شود. در ساعات اوج مصرف، یک سیستم ممکن است نیاز به پردازش صدها درخواست در ثانیه داشته باشد، درحالی‌که در زمان‌های دیگر این تعداد ممکن درحالی‌که کم باشد. علاوه بر این، ماهیت کار انجام شده برای رسیدگی به این درخواست‌ها ممکن است بسیار متغیر باشد. با استفاده از یک نمونه از سرویس مصرف‌کننده، می‌توانید باعث شوید که آن نمونه اشباع از درخواست‌ها شود. همین‌طور یک سیستم پیام‌رسانی ممکن است به دلیل هجوم پیام‌هایی که از برنامه می‌آید، بیش از حد بارگذاری (overloaded) شود. برای مدیریت این حجم کاری نوسانی و متغیر، سیستم می‌تواند چندین نمونه از سرویس مصرف‌کننده را اجرا کند. بااین‌حال، این مصرف‌کنندگان (consumers) باید هماهنگ شوند تا اطمینان حاصل شود که هر پیام تنها به یک مصرف‌کننده تحویل داده می‌شود. همین‌طور حجم کار (workload) همچنین باید در بین مصرف‌کنندگان متعادل باشد تا از تبدیل‌شدن یک نمونه از برنامه به گلوگاه جلوگیری شود.

راه‌حل

از یک صف پیام (message queue) برای پیاده‌سازی کانال ارتباطی بین برنامه و نمونه‌های از سرویس مصرف‌کننده (consumer service) استفاده کنید. برنامه درخواست‌ها را در قالب پیام به صف ارسال می‌کند و نمونه‌های سرویس مصرف‌کننده پیام‌هایی را از صف دریافت می‌کنند و آنها را پردازش می‌کنند. این رویکرد، مجموعه مشابهی از نمونه‌های سرویس مصرف‌کننده را قادر می‌سازد تا پیام‌های هر نمونه از برنامه را مدیریت کنند. شکل استفاده از صف پیام (message queue) برای توزیع تسک‌ها در نمونه‌های یک سرویس را نشان می‌دهد.

competing-consumers-diagram

این راه‌حل دارای مزایای زیر است:

*‏ این یک سیستم load-leveled ارائه می‌کند که می‌تواند تغییرات گسترده‌ای در حجم درخواست‌های ارسال شده توسط نمونه‌های application را مدیریت کند. صف به‌عنوان یک بافر بین نمونه‌های application و نمونه‌های سرویس مصرف‌کننده عمل می‌کند. این بافر می‌تواند به‌حداقل‌رساندن تأثیر بر دردسترس‌بودن و پاسخگویی، هم برای نمونه application و هم برای سرویس کمک کند. برای اطلاعات بیشتر، الگوی Queue-based Load Leveling pattern را ببینید. مدیریت پیامی که نیاز به پردازش طولانی‌مدت دارد، مانع از رسیدگی هم‌زمان پیام‌های دیگر توسط سایر نمونه‌های سرویس مصرف‌کننده نمی‌شود.

*‏ این مورد قابلیت اطمینان را بهبود می‌بخشد. اگر یک تولیدکننده (producer) به‌جای استفاده از این الگو، مستقیماً با مصرف‌کننده (producer) ارتباط برقرار کند، اما بر مصرف‌کننده نظارت نداشته باشد، احتمال زیادی وجود دارد که در صورت شکست مصرف‌کننده، پیام‌ها گم شوند یا پردازش نشوند. در این الگو، پیام‌ها به یک نمونه سرویس خاصی ارسال نمی‌شوند. یک نمونه سرویس ناموفق تولیدکننده(producer) را مسدود یا block نمی‌کند و پیام‌ها می‌توانند توسط هر نمونه‌ای از سرویس در حال کارپردازش شوند.

*‏ این موردنیازی به هماهنگی پیچیده بین مصرف‌کنندگان یا بین نمونه‌های تولیدکننده و مصرف‌کننده ندارد. صف پیام تضمین می‌کند که هر پیام حداقل یک‌بار تحویل داده می‌شود.

*‏ این مورد مقیاس‌پذیر است. هنگامی که مقیاس‌دهی خودکار(auto-scaling) را اعمال می‌کنید، سیستم می‌تواند به‌صورت پویا تعداد نمونه‌های سرویس مصرف‌کننده را با نوسانات حجم پیام‌ها افزایش یا کاهش دهد.

*‏ اگر صف پیام عملیات خواندنِ تراکنشی (transactional read) را ارائه دهد، می‌تواند انعطاف‌پذیری (resiliency) را بهبود بخشد. اگر یک نمونه سرویس مصرف‌کننده پیام را به‌عنوان بخشی از یک عملیات تراکنشی (transactional operation) بخواند و پردازش کند و نمونه سرویس مصرف‌کننده با شکست مواجه شود، پس این الگو می‌تواند اطمینان حاصل کند که پیام به صف بازگردانده می‌شود تا توسط نمونه دیگری از سرویس مصرف‌کننده دریافت و مدیریت شود. به‌منظور کاهش خطر شکست مداوم یک پیام، توصیه می‌کنیم از صف‌های مرده (dead-letter queues) استفاده کنید.

مسائل و ملاحظات:

هنگام تصمیم‌گیری در مورد نحوه اجرای این الگو به نکات زیر توجه کنید:

*‏ ترتیب پیام‌ها (Message ordering). ترتیبی که نمونه‌های سرویس مصرف‌کننده پیام‌ها را دریافت می‌کنند تضمینی نیست و لزوماً نشان‌دهنده ترتیب ایجاد پیام‌ها نیست. سیستم را طوری طراحی کنید که از عدم توانایی پردازش پیام اطمینان حاصل کنید؛ زیرا این امر به حذف هرگونه وابستگی به ترتیب مدیریت پیام‌ها کمک می‌کند. برای اطلاعات بیشتر، الگوهای Idempotency Patterns را در وبلاگ  Jonathon Oliver ببینید.

مایکروسافت Azure Service Bus Queues می‌تواند با استفاده از message sessions، مرتب‌سازی تضمین شده پیام‌ها را به ترتیب first-in-first-out اجرا کند. برای اطلاعات بیشتر، الگوی  Messaging Patterns Using Sessions را ببینید.

*‏ طراحی سرویس برای تاب‌آوری (Designing services for resiliency). اگر سیستم برای شناسایی و بازنشانی نمونه‌هایی از failed service طراحی شده باشد، پیشنهاد می‌شود پردازش انجام شده توسط نمونه‌های سرویس به‌عنوان عملیات idempotent اجرا شود که idempotent به این معنی است که یک عمل را می‌توان چندین بار بدون تغییر در نتیجه اولیه تکرار کرد تا اثرات یک پیام واحد که بیش از یک‌بار بازیابی و پردازش می‌شود به حداقل برسد.

*‏ **تشخیص پیام‌های سمی - Detecting poison messages **. یک پیام مخرب یا task ای که نیاز به دسترسی به منابعی دارد که در دسترس نیستند، می‌تواند باعث ازکارافتادن یک نمونه سرویس شود. سیستم باید از بازگرداندن چنین پیام‌هایی به صف جلوگیری کند و در عوض جزئیات این پیام‌ها را در جای دیگری ضبط و ذخیره کند تا در صورت لزوم بتوان آن‌ها را تجزیه‌وتحلیل کرد.

*‏ رسیدگی به نتیجه‌ها - Handling results. نمونه سرویسی که یک پیام را مدیریت می‌کند به طور کامل از منطق برنامه‌ای که پیام را تولید می‌کند جدا شده است و ممکن است نتوانند مستقیماً با آن ارتباط برقرار کنند. اگر نمونه سرویس نتایجی تولید کند که باید به منطق برنامه بازگردانده شود، این اطلاعات باید در مکانی ذخیره شود که برای هر دو قابل‌دسترسی باشد. برای جلوگیری از بازیابی اطلاعات ناقص توسط منطق برنامه، سیستم باید نشان دهد که پردازش کامل شده است.

اگر از Azure استفاده می‌کنید، یک worker process می‌تواند نتایج را با استفاده از یک صف پاسخ اختصاصی پیام به منطق برنامه بازگرداند. منطق برنامه باید بتواند این نتایج را با پیام اصلی مرتبط کند. این سناریو با جزئیات بیشتر در Asynchronous Messaging Primer توضیح داده شده است.

*‏ مقیاس‌بندی سیستم پیام‌رسانی - Scaling the messaging system. در یک راه‌حل به‌صورت  large-scale، یک  message queue منفرد می‌تواند توسط تعداد زیادی از پیام‌ها شلوغ (overwhelmed) شود و به یک گلوگاه در سیستم تبدیل شود. در این شرایط، سیستم پیام‌رسانی را برای ارسال پیام از تولیدکنندگان خاص به یک صف خاص، پارتیشن‌بندی کنید، یا از توزیع‌کننده بار(load balancing) برای توزیع پیام‌ها در چندین صف پیام استفاده کنید.

*‏ اطمینان از قابلیت اطمینان سیستم پیام‌رسانی - Ensuring reliability of the messaging system. یک سیستم پیام‌رسانی قابل‌اعتماد لازم است تا تضمین کند که پس از اینکه برنامه یک پیام را در نوبت قرارداد، دیگر آن پیام از بین نخواهد رفت. این سیستم برای اطمینان از اینکه همه پیام‌ها حداقل یک‌بار تحویل داده (delivered) می‌شوند ضروری است.

چه زمانی از این الگو استفاده کنیم؟

از این الگو زمانی استفاده کنید که:

*‏ حجم کار (workload) برای یک برنامه به taskهایی تقسیم می‌شود که می‌توانند به‌صورت ناهم‌زمان(asynchronous) اجرا شوند. *‏ وظایف یا taskها مستقل هستند و می‌توانند به‌صورت موازی اجرا شوند. *‏ حجم کار (workload) بسیار متغیر است و به یک راه‌حل مقیاس‌پذیر (scalable) نیاز دارد. *‏ راه‌حل باید دردسترس‌بودن (availability) بالایی داشته باشد و اگر پردازش یک کار با شکست مواجه شد، باید انعطاف‌پذیر(resilient) باشد.

این الگو ممکن است زمانی مفید نباشد که:

*‏ تفکیک حجم کاری(workload) برنامه به وظایف مجزا کار آسانی نیست، همین‌طور وابستگی بالایی بین کارها وجود دارد. *‏ وظایف یا Taskها باید به‌صورت هم‌زمان(synchronous) انجام شوند و application logic قبل از ادامه باید منتظر تکمیل‌شدن task موردنظر باشد. *‏ وظایف باید در یک توالی خاص انجام شود.

برخی از سیستم‌های پیام‌رسان sessionهایی را پشتیبانی می‌کنند که تولیدکننده (producer) را قادر می‌سازد تا پیام‌ها را با هم گروه‌بندی کند و اطمینان حاصل کند که همه آنها توسط یک مصرف‌کننده (consumer) بررسی می‌شوند. این سازوکار می‌تواند با پیام‌های اولویت‌دار (در صورت پشتیبانی) برای پیاده‌سازی شکلی از سفارشی‌سازی پیام که پیام‌ها را به ترتیب از یک تولیدکننده به یک مصرف‌کننده تحویل می‌دهد، استفاده شود.

مثال

مایکروسافت Azure امکان استفاده از Service Bus Queue و Azure Function را فراهم می‌کند. توابع Azure از طریق triggerها و اتصالات با Azure Service Bus یکپارچه می‌شوند. ادغام با Service Bus به شما امکان می‌دهد تا توابعی بسازید که پیام‌های صف ارسال شده توسط منتشرکنندگان (publishers) را مصرف(consume) می‌کنند. در واقع این برنامه‌ها انتشار پیام‌ها را به یک صف ارسال می‌کند و مصرف‌کنندگان که به‌عنوان Azure Function پیاده‌سازی شده‌اند، می‌توانند پیام‌ها را از این صف بازیابی کرده و آنها را مدیریت یا بررسی و استفاده کنند.

برای انعطاف‌پذیری(resiliency)، یک  Service Bus queue enables به مصرف‌کننده امکان می‌دهد از حالت "PeekLock" زمانی که پیامی را از صف بازیابی می‌کند استفاده کند. این حالت در واقع پیام را حذف نمی‌کند، بلکه به‌سادگی آن را از سایر مصرف‌کنندگان پنهان می‌کند. زمان اجرا Azure Functions پیامی را در حالت "PeekLock" دریافت می‌کند، اگر این تابع با موفقیت تمام شود، گزینه Complete را روی پیام فراخوانی می‌کند یا ممکن است گزینه Abandon را در صورت ازکارافتادن این تابع فراخوانی کند. پس پیام دوباره قابل‌مشاهده است و به مصرف‌کننده دیگری اجازه می‌دهد آن را بازیابی کند. اگر این تابع برای مدت زمانی طولانی‌تر از timeout مربوط به"PeekLock" اجرا شود، تا زمانی که تابع در حال اجرا باشد، این قفل به طور خودکار تمدید می‌شود.

این Azure Function می‌توانند بر اساس عمق صف scale out/in شوند و به‌عنوان مصرف‌کنندگان رقیب صف (competing consumers of the queue) عمل کنند. اگر چندین نمونه از توابع ایجاد شوند، همه آنها با کشیدن(pull) و پردازش مستقل پیام‌ها به رقابت می‌پردازند.

برای اطلاعات دقیق در مورداستفاده از Azure Service Bus queue به Service Bus queues, topics, and subscriptions مراجعه کنید.

برای اطلاعات در مورد صف triggered Azure Functions ، به  Azure Service Bus trigger for Azure Functions مراجعه کنید.

کد زیر نشان می‌دهد که چگونه می‌توانید با استفاده از یک نمونه ServiceBusClient یک پیام جدید ایجاد کنید و آن را به ServiceBusClient ارسال کنید.

private string serviceBusConnectionString = ...;
...

  public async Task SendMessagesAsync(CancellationToken  ct)
  {
   try
   {
    var msgNumber = 0;

    var serviceBusClient = new ServiceBusClient(serviceBusConnectionString);

    // create the sender
    ServiceBusSender sender = serviceBusClient.CreateSender("myqueue");

    while (!ct.IsCancellationRequested)
    {
     // Create a new message to send to the queue
     string messageBody = $"Message {msgNumber}";
     var message = new ServiceBusMessage(messageBody);

     // Write the body of the message to the console
     this._logger.LogInformation($"Sending message: {messageBody}");

     // Send the message to the queue
     await sender.SendMessageAsync(message);

     this._logger.LogInformation("Message successfully sent.");
     msgNumber++;
    }
   }
   catch (Exception exception)
   {
    this._logger.LogException(exception.Message);
   }
  }

مثال موجود کد زیر، مصرف‌کننده‌ای را نشان می‌دهد که به‌عنوان یک C# Azure Function نوشته شده است که metadata پیام را می‌خواند و یک پیام Service Bus Queue message را ثبت می‌کند. توجه داشته باشید که چگونه ویژگی ServiceBusTrigger برای اتصال آن به Service Bus Queue message استفاده می‌شود.

[FunctionName("ProcessQueueMessage")]
public static void Run(
    [ServiceBusTrigger("myqueue", Connection = "ServiceBusConnectionString")]
    string myQueueItem,
    Int32 deliveryCount,
    DateTime enqueuedTimeUtc,
    string messageId,
    ILogger log)
{
    log.LogInformation($"C# ServiceBus queue trigger function consumed message: {myQueueItem}");
    log.LogInformation($"EnqueuedTimeUtc={enqueuedTimeUtc}");
    log.LogInformation($"DeliveryCount={deliveryCount}");
    log.LogInformation($"MessageId={messageId}");
}

قدم بعدی

*‏ Asynchronous Messaging Primer. صف‌های پیام یک سازوکار ارتباطی ناهم‌زمان هستند. اگر یک سرویس مصرف‌کننده نیاز به ارسال پاسخ به یک برنامه را داشته باشد، ممکن است لازم باشد که نوعی پیام پاسخ را پیاده‌سازی کند. Asynchronous Messaging Primer اطلاعاتی در مورد نحوه اجرای پیام request/reply با استفاده از message queues ارائه می‌دهد.

*‏ Autoscaling Guidance. ممکن است بتوان نمونه‌هایی از سرویس مصرف‌کننده را شروع کرد و سپس متوقف کرد، زیرا طول صف پیام‌ها برای هر برنامه‌ای متفاوت است. مقیاس خودکار(Autoscaling) می‌تواند به حفظ توان در زمان اوج پردازش (peak processing) کمک کند.

منابع مرتبط

الگوها و راهنمایی‌های زیر ممکن است هنگام اجرای این الگو مرتبط باشند:

*‏ Compute Resource Consolidation pattern ممکن است بتوان چندین نمونه از سرویس مصرف‌کننده را در یک فرایند واحد ادغام کرد تا هزینه‌ها و سربار بررسی و پردازش کاهش یابد. Compute Resource Consolidation pattern مزایا و معایب پیروی از این رویکرد را توصیف می‌کند.

*‏ Queue-based Load Leveling pattern. معرفی یک صف پیام می‌تواند انعطاف‌پذیری را به سیستم اضافه کند و نمونه‌های سرویس را قادر می‌سازد تا حجم بسیار متفاوتی از درخواست‌ها را از نمونه‌های برنامه مدیریت کنند. صف پیام به‌عنوان یک بافر عمل می‌کند که بار مصرفی را تراز می‌کند. الگوی تراز بار مبتنی بر صف (Queue-based) این سناریو را با جزئیات بیشتری توصیف می‌کند.