بلک ہیڈ پیٹرن: فالٹ ٹولرنٹ مائیکرو سروسز ڈیزائن کرنا

بلک ہیڈ پیٹرن: فالٹ ٹولرنٹ مائیکرو سروسز ڈیزائن کرنا

مائیکرو سروسز آرکیٹیکچر میں، ایک ایپلیکیشن کو درجنوں یا سینکڑوں آزاد، تعاون کرنے والی خدمات میں تقسیم کیا جاتا ہے۔ اگرچہ یہ ڈیزائن ماڈیولریٹی اور اسکیل ایبلٹی کو بہتر بناتا ہے، لیکن یہ ایک بڑا خطرہ بھی پیش کرتا ہے: ایک سروس میں ناکامی پورے نظام کو جھنجھوڑ کر رکھ سکتی ہے۔

اگر کوئی ڈاون اسٹریم سروس سست یا غیر جوابدہ ہو جاتی ہے، تو آپ کی اپ اسٹریم سروسز کو آنے والی درخواستیں جمع ہونا شروع ہو جائیں گی۔ اگر وہ سب ایک ہی میموری، سی پی یو، یا تھریڈ پول کا اشتراک کرتے ہیں، تو سست انحصار تمام دستیاب وسائل کو تیزی سے ختم کر سکتا ہے، جس سے آپ کی پوری ایپلیکیشن کریش ہو جاتی ہے۔

اس جھرن کی ناکامی کو ڈومینو اثر کے نام سے جانا جاتا ہے۔ اس کو روکنے کے لیے، سسٹم آرکیٹیکٹس بلک ہیڈ پیٹرن کا استعمال کرتے ہیں۔

اس گائیڈ میں، ہم دریافت کریں گے کہ بلک ہیڈ پیٹرن کیا ہے، یہ کیسے کام کرتا ہے، اور جاوا (Resilience4j) اور Go میں سادہ تشبیہات، تعمیراتی تصورات، اور کوڈ کی مثالوں کا استعمال کرتے ہوئے اسے کیسے نافذ کیا جائے۔


حقیقی دنیا کی تشبیہ: واٹر ٹائٹ شپ بلک ہیڈز

اس پیٹرن کا نام جہاز سازی کی صنعت سے آیا ہے۔

ایک بلک ہیڈ ایک واٹر ٹائٹ دیوار ہے جو جہاز کے ہل کے اندر بنی ہے۔ جہاز کے ہل کے اندر ایک واحد، بڑے پیمانے پر کھلی جگہ رکھنے کے بجائے، اندرونی حصے کو کئی آزاد، مہر بند حصوں میں تقسیم کیا گیا ہے۔

بلک ہیڈ پیٹرن آرکیٹیکچر ڈایاگرام مشترکہ بمقابلہ الگ تھلگ تھریڈ پول دکھا رہا ہے۔

اگر جہاز کسی رکاوٹ سے ٹکراتا ہے اور اس کی پٹڑی ٹوٹ جاتی ہے تو پانی تباہ شدہ کمپارٹمنٹ میں بھر جائے گا۔ تاہم، واٹر ٹائٹ بلک ہیڈز کی وجہ سے، پانی اس ایک ڈبے میں موجود ہے۔ باقی جہاز خشک اور خوش کن رہتا ہے، جس کی وجہ سے وہ تیرتا رہتا ہے اور حفاظت تک پہنچ سکتا ہے۔

بلک ہیڈز کے بغیر، پانی پورے ہل میں آزادانہ طور پر بہے گا، بالآخر جہاز ڈوب جائے گا۔

سافٹ ویئر انجینئرنگ میں: *** جہاز** آپ کی پوری درخواست یا خدمت ہے۔

  • کمپارٹمنٹس الگ تھلگ وسائل کے تالاب ہیں (دھاگے، کنکشن، سی پی یو)۔ The Hull Breach ایک ڈاون اسٹریم مائیکرو سروس میں ناکامی یا سست روی ہے۔ سیلاب وسائل کی تھکن ہے۔

مسئلہ: مشترکہ وسائل کے تالاب اور دھاگے کی تھکن

یہ سمجھنے کے لیے کہ بلک ہیڈز کیوں ضروری ہیں، آئیے دیکھتے ہیں کہ جب وسائل کا عالمی سطح پر اشتراک کیا جاتا ہے تو کیا ہوتا ہے۔

ایک API گیٹ وے یا صارف کی درخواستوں کو سنبھالنے والا ویب سرور تصور کریں۔ تمام آنے والی کالوں کو پروسیس کرنے کے لیے اس میں 100 تھریڈز کا واحد عالمی تھریڈ پول ہے۔ سرور تین بہاو خدمات کے ساتھ تعامل کرتا ہے:

  1. کیٹلاگ سروس (تیز، مصنوعات کی فہرست پڑھتا ہے)
  2. ادائیگی کی خدمت (تیز، پراسیس چیک آؤٹ)
  3. سفارش کی خدمت (سست، ذاتی اشیاء کا حساب لگاتا ہے)

عام طور پر، سب کچھ ٹھیک کام کرتا ہے. لیکن فرض کریں کہ سفارشی سروس ڈیٹا بیس کے تعطل کا شکار ہے اور جواب دینے میں 200 ملی سیکنڈ کے بجائے 30 سیکنڈ لگنا شروع کر دیتی ہے۔

یہاں کیا ہوتا ہے:

  1. صارفین ہوم پیج پر جاتے رہتے ہیں، اور سفارشی سروس کی درخواستوں کو متحرک کرتے ہیں۔
  2. سرور ہر درخواست کے لیے عالمی پول سے ایک دھاگہ تفویض کرتا ہے۔
  3. چونکہ سفارشی سروس سست ہے، یہ تھریڈز جوابات کے انتظار میں بیٹھے ہیں۔
  4. سیکنڈوں میں، پول میں موجود تمام 100 تھریڈز سفارشی سروس پر منتظر ہیں۔
  5. جب کوئی نیا صارف چیک آؤٹ کرنے یا کیٹلاگ دیکھنے کی کوشش کرتا ہے، تو سرور کے پاس ان کی درخواست پر کارروائی کرنے کے لیے کوئی دھاگہ نہیں بچا ہے۔

اگرچہ کیٹلاگ اور ادائیگی کی خدمات مکمل طور پر صحت مند ہیں، اب وہ ناقابل رسائی ہیں کیونکہ سست سفارشی سروس نے مشترکہ تھریڈ پول کو ختم کردیا ہے۔ پورا سسٹم آف لائن ہو گیا ہے۔


حل: بلک ہیڈ پیٹرن

بلک ہیڈ پیٹرن وسائل کے تالابوں کو تقسیم کرکے اس مسئلے کو حل کرتا ہے تاکہ ایک علاقے میں ناکامی دوسرے کو متاثر نہ کرے۔

ایک عالمی تالاب کے بجائے، ہم ہر سروس یا نیچے کی دھارے پر انحصار کے لیے علیحدہ، باؤنڈڈ پول مختص کرتے ہیں۔

اگر ہم سفارشی سروس کے لیے خاص طور پر 10 تھریڈز مختص کرتے ہیں، تو اس کے انتظار میں زیادہ سے زیادہ 10 تھریڈز کو کبھی بھی بلاک کیا جا سکتا ہے۔ اگر سفارشی سروس سست ہو جاتی ہے، تو وہ 10 تھریڈز ختم ہو جائیں گے، اور بعد میں آنے والی سفارش کی درخواستوں کو فوری طور پر مسترد کر دیا جائے گا (ناکام تیزی سے)۔

تاہم، باقی 90 تھریڈز ابھی بھی کیٹلاگ اور ادائیگی کی خدمات کے لیے محفوظ ہیں۔ صارف اب بھی مصنوعات کو براؤز کر سکتے ہیں اور خریداریاں کر سکتے ہیں، چاہے سفارشی ویجیٹ عارضی طور پر دستیاب نہ ہو۔


بلک ہیڈ آئسولیشن کی اقسام

سافٹ ویئر سسٹمز میں بلک ہیڈز کو لاگو کرنے کے دو بنیادی طریقے ہیں:

1. تھریڈ پول آئسولیشن

اس ماڈل میں، ہر بہاو پر انحصار کو اس کا اپنا مخصوص تھریڈ پول اور عمل درآمد کی قطار تفویض کی گئی ہے۔

تھریڈ پول آئسولیشن ڈایاگرام کارکن تھریڈز کے لیے قطار میں آنے والی درخواستوں کو دکھا رہا ہے۔
  • یہ کیسے کام کرتا ہے: مرکزی ایپلیکیشن تھریڈ ٹاسک کو مخصوص تھریڈ پول کے حوالے کر دیتی ہے۔ اگر پول بھرا ہوا ہے تو، درخواست یا تو قطار میں ہے یا مسترد کر دی گئی ہے۔
  • ** پیشہ **: مکمل تنہائی فراہم کرتا ہے۔ اگر کوئی سروس سست ہو جاتی ہے تو صرف اس کا تھریڈ پول متاثر ہوتا ہے۔ آپریٹنگ سسٹم/جے وی ایم کی سطح پر تھریڈز الگ تھلگ ہیں۔ کنز: تھریڈ شیڈولنگ، سیاق و سباق کی تبدیلی، اور قطار کے انتظام کی وجہ سے اضافی CPU اوور ہیڈ متعارف کرایا۔

2. سیمفور آئسولیشن

نئے تھریڈ پول بنانے کے بجائے، سیمفور آئسولیشن ایک کاؤنٹر (سیمافور) کا استعمال کرتا ہے تاکہ کسی مخصوص سروس کو اجازت دی جانے والی ہم آہنگی کالوں کی تعداد کو محدود کیا جا سکے۔

سیمفور آئسولیشن ڈایاگرام پرمٹ حاصل کرنے کے بعد کاموں کو انجام دینے والے درخواست کے دھاگوں کو دکھا رہا ہے

یہ کیسے کام کرتا ہے: جب کوئی درخواست شروع ہوتی ہے، تو یہ سیمفور سے اجازت نامہ حاصل کرنے کی کوشش کرتی ہے۔ اگر اجازت نامہ دستیاب ہے، تو یہ کالنگ تھریڈ پر درخواست پر عمل کرتا ہے اور مکمل ہونے پر اجازت نامہ جاری کرتا ہے۔ اگر کوئی اجازت نامہ دستیاب نہیں ہے، تو درخواست فوری طور پر مسترد کر دی جاتی ہے۔

  • ** پیشہ : عملی طور پر صفر اوور ہیڈ کے ساتھ بہت ہلکا پھلکا کیونکہ کوئی تھریڈ سیاق و سباق سوئچنگ شامل نہیں ہے۔ *** Cons: کوئی دھاگے کی علیحدگی نہیں۔ اگر کسی نیٹ ورک ساکٹ پر کال کو بغیر کسی مناسب ٹائم آؤٹ کے بلاک کر دیا جاتا ہے، تب بھی یہ کالنگ تھریڈ کو بلاک کر سکتا ہے۔

نفاذ کی مثالیں۔

آئیے دیکھتے ہیں کہ دو مشہور بیک اینڈ زبانوں میں بلک ہیڈز کو کیسے نافذ کیا جائے۔

1. Java (Resilience4j اور Spring Boot)

Resilience4j ایک ہلکا پھلکا، استعمال میں آسان فالٹ ٹولرنس لائبریری ہے جسے جاوا کے لیے ڈیزائن کیا گیا ہے۔ ذیل میں بتایا گیا ہے کہ آپ سپرنگ بوٹ ایپلیکیشن میں ڈاؤن اسٹریم ادائیگی کی خدمت کے لیے بلک ہیڈ کو کس طرح تشکیل دیتے ہیں۔

کنفیگریشن (application.yml)

resilience4j.bulkhead:
  instances:
    paymentService:
      maxConcurrentCalls: 10
      maxWaitDuration: 10ms

resilience4j.threadpoolbulkhead:
  instances:
    paymentService:
      maxThreadPoolSize: 10
      coreThreadPoolSize: 5
      queueCapacity: 20

کوڈ کا نفاذ

import io.github.resilience4j.bulkhead.annotation.Bulkhead;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;

@Service
public class OrderService {

    private final RestTemplate restTemplate;

    public OrderService(RestTemplate restTemplate) {
        this.restTemplate = restTemplate;
    }

    // Apply semaphore bulkhead
    @Bulkhead(name = "paymentService", fallbackMethod = "paymentFallback")
    public String processPayment(OrderDetails details) {
        return restTemplate.postForObject("http://payment-service/charge", details, String.class);
    }

    // Fallback method executed when the bulkhead is full
    public String paymentFallback(OrderDetails details, Throwable throwable) {
        return "Payment service is currently busy. Please try again later.";
    }
}

2. جاؤ (گولانگ)

گو میں، ہمیں لازمی طور پر کسی بھاری فریم ورک کی ضرورت نہیں ہے کیونکہ زبان مقامی ہم آہنگی کی ابتدائی چیزیں جیسے گوروٹینز اور بفرڈ چینلز فراہم کرتی ہے۔ ہم بفرڈ چینل کا استعمال کرتے ہوئے کلین سیمفور بلک ہیڈ کو نافذ کر سکتے ہیں:

package main

import (
	"errors"
	"fmt"
	"net/http"
	"time"
)

// Bulkhead represents a concurrency limiter
type Bulkhead struct {
	semaphore chan struct{}
}

// NewBulkhead initializes a bulkhead with a max concurrency limit
func NewBulkhead(maxConcurrency int) *Bulkhead {
	return &Bulkhead{
		semaphore: make(chan struct{}, maxConcurrency),
	}
}

// Execute runs the task if resource permit is available, otherwise returns error
func (b *Bulkhead) Execute(task func() error) error {
	select {
	case b.semaphore <- struct{}{}:
		// Acquired permit
		defer func() { <-b.semaphore }() // Release permit
		return task()
	default:
		// Bulkhead is full, reject immediately
		return errors.New("bulkhead is full: request rejected")
	}
}

func main() {
	// Allow maximum of 3 concurrent calls
	paymentBulkhead := NewBulkhead(3)

	mockTask := func() error {
		fmt.Println("Processing payment...")
		time.Sleep(2 * time.Second) // Simulate network delay
		return nil
	}

	// Simulate 5 rapid requests
	for i := 1; i <= 5; i++ {
		go func(reqID int) {
			err := paymentBulkhead.Execute(mockTask)
			if err != nil {
				fmt.Printf("Request %d failed: %v\n", reqID, err)
			} else {
				fmt.Printf("Request %d completed successfully\n", reqID)
			}
		}(i)
	}

	// Keep main alive to watch output
	time.Sleep(3 * time.Second)
}

بلک ہیڈ پیٹرن کے لیے عام استعمال کے کیسز

یہاں کچھ عام حالات ہیں جہاں بلک ہیڈ پیٹرن کو نافذ کرنا اہم ہے: *API گیٹ وے روٹنگ: مختلف بیک اینڈ سروسز کے لیے راستوں کو الگ کرنا۔ اگر سفارشی سروس بند ہوجاتی ہے تو گیٹ وے پر آرڈر سروس کے راستے مکمل طور پر کام کرتے رہتے ہیں۔

  • ڈیٹا بیس کنکشن پولز: ڈیٹا بیس کنکشن پولز کو سروس یا کرایہ دار کے لحاظ سے تقسیم کرنا۔ ایک کرایہ دار سے بھاری تجزیاتی سوالات کا اضافہ تمام دستیاب کنکشن ہینڈلز کو ختم نہیں کرے گا، جس سے دوسرے کرایہ داروں کے لیے لین دین کے سوالات کی بچت ہوگی۔
  • ملٹی کرایہ دار SaaS ایپلی کیشنز: مفت کرایہ داروں کے مقابلے پریمیم کے لیے کمپیوٹ کے وسائل یا عمل درآمد کی قطاروں کو الگ کرنا۔ مفت درجے کے وسائل کی بڑھتی ہوئی تعداد CPU یا میموری کی پریمیم درجے کی درخواستوں کو بھوکا نہیں کرے گی۔
  • تھرڈ پارٹی API انٹیگریشنز: بیرونی ادائیگی کے گیٹ ویز، شپنگ فراہم کنندگان، یا نوٹیفکیشن انجنز کے لیے علیحدہ HTTP کلائنٹ پولز کو وقف کرنا۔ If one third-party service slows down, other external interactions continue without blockages.

کیوں کافکا/میسج بروکرز بلک ہیڈ پیٹرن کو تبدیل نہیں کر سکتے

ایک عام سوال یہ ہے: “اگر ہمارے پاس اپاچی کافکا جیسے میسج بروکرز ہیں، تو ہمیں بلک ہیڈ پیٹرن کی ضرورت کیوں ہے؟ کیا ہم صرف درخواستوں کو بفر کرنے کے لیے قطاروں کا استعمال نہیں کر سکتے؟”

جبکہ میسج بروکرز سسٹم کو ڈی جول کرتے ہیں، وہ بلک ہیڈ پیٹرن کو بدل نہیں سکتے۔ یہاں ہے کیوں:

1. ہم وقت ساز بمقابلہ غیر مطابقت پذیر مواصلات

کافکا کو غیر مطابقت پذیر، ایونٹ سے چلنے والے فن تعمیرات کے لیے ڈیزائن کیا گیا ہے۔ پروڈیوسر کسی موضوع پر پیغام بھیجتا ہے، اور صارف بالآخر اس پر کارروائی کرتا ہے۔ تاہم، صارف کا سامنا کرنے والی ایپلیکیشنز کو اکثر مطابقت پذیر (درخواست-جواب) مواصلت کی ضرورت ہوتی ہے (مثال کے طور پر، پروڈکٹ کیٹلاگ لوڈ کرنا یا REST/gRPC API کے ذریعے کریڈٹ کارڈ چارج کرنا)۔ Introducing Kafka here requires complex request-reply patterns, adding high latency and overhead. بلک ہیڈز کو خاص طور پر حقیقی وقت میں ان ہم وقت سازی کے دھاگوں کی حفاظت کے لیے ڈیزائن کیا گیا ہے۔

2. کافکا صارفین کے اندر تھریڈ سٹوریشن

یہاں تک کہ اگر آپ کا سسٹم مکمل طور پر ایونٹ پر مبنی ہے اور کافکا استعمال کرتا ہے، تب بھی آپ کو بلک ہیڈز کی ضرورت ہے! فرض کریں کہ ایک صارف مائیکرو سروس متعدد کافکا کے عنوانات (مثلاً user-registrations اور video-transcoding) کو سنتا ہے۔ اگر صارف اپنے تمام اندرونی ورکر تھریڈز کو سست video-transcoding ملازمتوں کے بڑے بیچ پر کارروائی کرنے کے لیے مختص کرتا ہے، تو اسے تھریڈ کی بھوک کا سامنا کرنا پڑے گا۔ صارف ہلکے وزن والے user-registrations پیغامات پر کارروائی نہیں کر سکے گا، حالانکہ وہ تقسیم صحت مند ہے۔ کام کو الگ کرنے کے لیے آپ کو کنزیومر سروس کے اندر اندرونی بلک ہیڈز (علیحدہ تھریڈ پولز) کی ضرورت ہے۔

3. کلائنٹ سائیڈ اوور ہیڈ اور فیل فاسٹ کی ضرورت

جب ڈاؤن اسٹریم سروس ڈاؤن ہوتی ہے تو، ایک بلک ہیڈ کالنگ سروس کو تیزی سے ناکام ہونے اور فوری طور پر فال بیک جواب واپس کرنے کی اجازت دیتا ہے۔ اگر آپ اس کے بجائے کافکا میں ہر چیز کو قطار میں لگاتے ہیں، تو قطار لامحدود بڑھ سکتی ہے، جس کے نتیجے میں باسی درخواستیں، زیادہ میموری کی کھپت، اور نظام کے ٹھیک ہونے پر ٹائم آؤٹ میں تاخیر ہوتی ہے۔

مختصراً، کافکا نیٹ ورک پر سسٹمز کے درمیان مواصلت کو جوڑتا ہے، جبکہ **بلک ہیڈز ایک چلتی ہوئی ایپلیکیشن مثال کے اندر وسائل کے عمل کو الگ کر دیتے ہیں۔ وہ تکمیلی ہیں، باہمی طور پر خصوصی نہیں۔


بلک ہیڈز استعمال کرتے وقت بہترین طریقے

  • ہمیشہ ٹائم آؤٹ سیٹ کریں: ایک بلک ہیڈ ہم آہنگی کو محدود کرتا ہے، لیکن یہ سست ساکٹ ریڈز کو حل نہیں کرتا ہے۔ بلک ہیڈز کو سخت نیٹ ورک ٹائم آؤٹ کے ساتھ جوڑیں تاکہ تھریڈز کو جلد از جلد ریلیز کریں۔
  • سرکٹ بریکرز کے ساتھ جوڑیں: سرکٹ بریکرز کے ساتھ بلک ہیڈز کا استعمال کریں۔ اگر کوئی بلک ہیڈ مسلسل درخواستوں کو مسترد کرنا شروع کر دیتا ہے، تو سرکٹ بریکر کو ٹریفک کو مکمل طور پر روکنے کے لیے ٹرپ کرنا چاہیے اور ڈاؤن اسٹریم سروس روم کو بحال کرنے کے لیے دینا چاہیے۔
  • پول سیچوریشن کو مانیٹر کریں: اپنی بلک ہیڈ قطار کی لمبائی اور فعال دھاگوں کی گنتی پر الرٹس نافذ کریں۔ اگر بلک ہیڈ مسلسل بھرا ہوا ہے، تو آپ کو اپنے بنیادی ڈھانچے کو پیمانہ کرنے یا ڈاؤن اسٹریم سروس کو بہتر بنانے کی ضرورت پڑسکتی ہے۔
  • انفرادی طور پر سائز کو ٹیون کریں: ایک سائز کی تمام حد کے مطابق استعمال نہ کریں۔ بلک ہیڈ کی صحیح حدود کا تعین کرنے کے لیے ہر انحصار کی تاخیر اور درخواست کی شرح کی پیمائش کریں۔

نتیجہ

بلک ہیڈ پیٹرن لچکدار، کلاؤڈ اسکیل سسٹم بنانے کے لیے ایک ضروری ڈیزائن پیٹرن ہے۔ اپنے وسائل کو تقسیم کرکے، آپ ناکامیوں کو الگ تھلگ کرتے ہیں، جھڑپوں کے اثرات کو روکتے ہیں، اور اس بات کو یقینی بناتے ہیں کہ ایک مقامی بگ عالمی بندش میں تبدیل نہ ہو۔