الگوی جعبه خروجی
Outbox یک الگوی معماری است که در آن یک سرویس دامنه یک تغییر تجاری و رویداد مربوطه را در یک معامله محلی به مخزن خود می نویسد. انتشار رویداد به اتوبوس/صف خارجی به صورت ناهمگام توسط یک فرایند امن جداگانه (ناشر) انجام می شود که جدول «outbox» را می خواند و پرونده ها را رله می کند. این رویکرد مسابقه را «ابتدا به پایگاه داده، سپس به اتوبوس» حذف می کند و حتی در صورت شکست، تحویل قابل اعتماد را فراهم می کند.
1) هنگامی که برای درخواست
مناسب:- میکروسرویس ها و مونولیت های مدولار با رویدادهای بین زمینه ها
- لازم است اطمینان حاصل شود که «دولت ثابت است ↔ رویداد نمی تواند از دست برود».
- ما نیاز به توانایی و تحویل مجدد کنترل شده داریم.
- معاملات دشوار جهانی در چندین منبع بسیار مهم هستند (بهتر از TCC/sagas با قراردادهای صریح).
- هیچ منبع اختصاصی از حقیقت وجود ندارد (دولت در جایی که رویداد تولید می شود ذخیره نمی شود).
2) اهداف و خواص
Atomic write: رکورد دامنه + outbox - در یک معامله.
حداقل یک بار انتشار: ما اجازه تکرار، حذف از دست دادن.
idemotence مصرف کننده: حفاظت در برابر طول می کشد در سمت مشترک.
دقیقا یک بار موثر: با ترکیبی از outbox + idempotent consumer + dedup به دست می آید.
پاک کردن تله متری - معاملات و رویدادهای تجاری را مرتبط کنید.
3) طرح داده (مثال)
sql
-- Domain table (example: orders)
CREATE TABLE orders (
id UUID PRIMARY KEY,
tenant_id TEXT NOT NULL,
status TEXT NOT NULL,
total_amount NUMERIC(12,2) NOT NULL,
updated_at TIMESTAMP NOT NULL DEFAULT now()
);
-- Outbox
CREATE TABLE outbox (
id UUID PRIMARY KEY, -- event_id aggregate_type TEXT NOT NULL, -- 'order'
aggregate_id UUID NOT NULL, -- order_id tenant_id TEXT NOT NULL,
type TEXT NOT NULL, -- 'OrderCreated'
payload JSONB NOT NULL, -- serialized headers event JSONB NOT NULL DEFAULT '{}':: jsonb,
occurred_at TIMESTAMP NOT NULL, -- time in domain transaction available_at TIMESTAMP NOT NULL, -- earliest publish time (backoff)
published_at TIMESTAMP, - is filled by the attempts INT NOT NULL DEFAULT 0,
error TEXT
);
CREATE INDEX ON outbox (available_at) WHERE published_at IS NULL;
CREATE INDEX ON outbox (tenant_id, available_at) WHERE published_at IS NULL;
4) لایه کاربرد
pseudo begin tx domainChange () # INSERT/UPDATE in domain table insert into outbox (event) # event with aggregate/tenant commit tx keys
اگر تعهد موفقیت آمیز باشد، رویداد در صندوق پستی تضمین شده است. اگر درخواست پس از یک تعهد کاهش یابد، ناشر خواهد شد.
5) ناشر (خواننده → ناشر)
وظایف:- به صورت دوره ای رویدادهای منتشر نشده را بخوانید ('published _ at IS NULL' و 'available _ at <= now ()')، دسته ها.
- سعی کنید برای انتشار به اتوبوس/صف ؛ اگر موفق شدید، 'published _ at' را علامت بزنید.
- در صورت خطا - افزایش «تلاش»، قرار دادن «available _ at» برای آینده (عقب نشینی نمایی)، نوشتن «خطا».
- احترام به محدودیت در مستاجران/کلید (عدالت)، محصول را مسدود نکنید.
pseudo loop:
events = select from outbox where published_at is null and available_at <= now()
order by occurred_at limit BATCH_SIZE for update skip locked
for e in events:
try:
broker. publish(topicFor(e), serialize(e. payload), headers(e))
markPublished(e. id, now())
except Retryable:
backoff = computeBackoff(e. attempts)
reschedule(e. id, now()+backoff, attempts+1, last_error)
except NonRetryable:
moveToDLQ (e) or markError (e) # by sleep (POLL_INTERVAL) policy
6) Idempotency و deduplication
در سمت مصرف کننده (صندوق ورودی/فروشگاه Idempotency):sql
CREATE TABLE inbox (
consumer_name TEXT,
event_id UUID,
processed_at TIMESTAMP NOT NULL,
PRIMARY KEY (consumer_name, event_id)
);
الگوریتم: هنگام دریافت یک رویداد، ابتدا سعی کنید 'INSERT' را در 'inbox' وارد کنید ؛ اگر یک درگیری کلیدی وجود داشته باشد، این رویداد قبلا انجام شده است → no-op. نکته بعدی منطق کسب و کار است.
در سمت ناشر: 'Idempotency-Key' در هدر (به عنوان مثال، 'event _ id') به طوری که bus/broker/proxy می تواند تکراری را فیلتر کند.
7) نظم و علیت
ترتیب محلی توسط «agregate _ id» با مرتب سازی «occurced _ at» و انتشار «توسط کلید» ارائه می شود.
برای log-buses با پارتیشن بندی - پارتیشن بندی با کلید 'agregate _ id '/' tenant _ id' به طوری که رویدادهای یک aggregate در پارتیشن بندی مشابه هستند.
اگر نظم مهم است، از مسابقات ناشر تک کلیدی متقابل اجتناب کنید.
8) CDC (تغییر داده ها ضبط)
به جای یک ناشر فعال، می توانید از CDC استفاده کنید: موتور ورود به سیستم معامله پایگاه داده را می خواند و خطوط «outbox» را به اتوبوس ترجمه می کند. مزایا - حداقل بار در پایگاه داده، توالی دقیق، بدون نظرسنجی. معایب - عوارض عملیات و کراوات به ویژگی های DBMS. هر دو روش معتبر هستند ؛ مهارت ها و SLO را انتخاب کنید.
9) خطاها، DLQ و Redrive
Retryable (شبکه، محدودیت ها) - افزایش 'attempts'، به تعویق انداختن 'available _ at' (عقب نشینی نمایشی + jitter).
Non-retryable (invalid scheme/contract) - انتقال به موضوع DLQ/Dead-Letter با ابرداده غنی.
Safe Redrive: Batches, Rate-Limit, Validation of the Scheme, اولویت زیر ترافیک تولید.
10) چند اجاره و محدودیت
برچسب های مورد نیاز: 'tenant _ id', 'plan', 'region' - in 'outbox. هدر ها
انصاف برای هر مستاجر: ناشر «پنجره» انتشارات و محدودیت تلاش برای مستاجران را توزیع می کند.
محل اقامت: صندوق پستی را در همان منطقه به عنوان داده های دامنه ذخیره کنید. انتشار بین منطقه ای - فقط جمع ها/خلاصه ها.
11) ایمنی و انطباق
نسخه PII در بار/هدر در سیاست مستاجر/منطقه.
امضا/رمزگذاری محموله اگر اتوبوس خارجی باشد.
حسابرسی تمام انتقال حالت: ایجاد، منتشر شده، خطا، redrave.
12) قابلیت مشاهده
معیارها:- تاخیر انتشار ("در حال حاضر - occurred_at' p50/p95/p99).
- نرخ موفقیت، نرخ خطا، توزیع علت.
- اندازه جعبه خروجی (تعداد منتشر نشده)، دوباره/ثانیه
- نمودار هر مستاجر توان و تاخیر.
- همبستگی 'event _ id '/' aggregate _ id '/' saga _ id'; «db-tx»، «انتشار»، «تلاش مجدد» را نشان می دهد.
- حاشیه نویسی: 'تلاش'، 'backoff _ ms'، 'dlq = درست'.
- سوابق کوتاه برای موفقیت جزئیات کامل در هر خطا/redrave.
13) آزمایش و هرج و مرج
آزمون اتمی: به طور مصنوعی «سقوط» پس از انجام یک معامله دامنه قبل از انتشار - رویداد باید بعدا منتشر شود.
تست تکراری: ما یک رویداد را چندین بار منتشر می کنیم - مصرف کننده دقیقا یک اثر (صندوق ورودی) را انجام می دهد.
آزمون سفارش: دسته ای از حوادث توسط یک دانه - بررسی توالی/idempotence.
هرج و مرج: شکست کارگزار، افزایش زمان تاخیر پایگاه داده، ناشران تقسیم مغز، clock-skew.
14) قالب های پیکربندی (مثال)
yaml outbox:
poll_interval_ms: 200 batch_size: 200 order_by: occurred_at backoff:
strategy: exponential_full_jitter initial_ms: 250 max_ms: 10_000 max_attempts: 20 fairness:
per_tenant_parallelism: 4 per_key_serial: true
publisher:
rate_limit_per_sec: 500 headers:
idempotency_key: event_id schema_version: v3 dlq:
enabled: true topic: myapp. events. dlq include_metadata:
- error
- attempts
- source_table
- tenant_id
- aggregate_id
15) ادغام با sagas و عقب نشینی
Outbox - «حمل و نقل امنیتی» برای مراحل حماسه: معامله محلی اثر و فرمان/رویداد را می نویسد ؛ انتشار - قابل اعتماد و دوز.
سیاست های تکرار و عقب نشینی باید با «Retry-After» و Circuit Breaker سازگار باشد. جلوگیری از «طوفان»
16) خطاهای معمول
آنها نوشتن یک رویداد پس از دولت دامنه مرتکب - از دست دادن در طول سقوط امکان پذیر است.
بدون نمایهها/بایگانی در «جعبه خروجی» → افزایش تأخیر انتشار.
ناشر بدون «SKIP LOCKED» یا بدون sharding - رقابت و مسدود کردن.
عدم توانایی در میان مصرف کنندگان - تکراری و عوارض جانبی.
PII مخلوط بدون ماسک در DLQ/سیاهههای مربوط.
یک صف انتشار جهانی بدون انصاف - مستاجر «پر سر و صدا» همه را کند می کند.
عدم نظارت بر تاخیر → تخریب پنهان.
17) انتخاب استراتژی سریع
سطح شروع: نظرسنجی از پایگاه داده، 100-500 دسته، عقب نشینی کامل، صندوق ورودی برای مصرف کنندگان.
بار بالا: CDC از ورود به سیستم معامله، sharding توسط 'tenant _ id/aggregate _ id'، WFQ توسط مستاجر.
ترتیب دقیق توسط aggregate: انتشار سریال در هر کلید (mutex)، پارتیشن بندی موضوع با یک کلید.
انطباق/PII: رمزگذاری بار، نسخه DLQ، صندوق پستی منطقه ای.
18) چک لیست پیش فروش
- تغییرات دامنه و می نویسد: «خارج از جعبه» در همان معامله رخ می دهد.
- ناشر دسته ها را اداره می کند، از «SKIP LOCKED» استفاده می کند، با jitter و محدودیت ها عقب نشینی می کند.
- مصرف کنندگان idempotent (جدول 'inbox '/deadup ورود به سیستم).
- DLQ و انتشار امن پیکربندی شده اند.
- تاخیر/خطا و معیارهای هشدار در آستانه p95/p99.
- سفارش کلیدی تضمین شده است (دسته/سریال).
- بایگانی/نگهداری 'outbox' و پاک کردن سوابق منتشر شده است.
- سیاست های PII و حسابرسی انتقال دولت است.
- رها کردن آزمون بین مرتکب و انتشار، تکراری و سفارش.
- مستندات قرارداد رویداد (طرح/نسخه/سازگاری).
نتیجه گیری
الگوی outbox بسته نرم افزاری «شکننده» DB ↔ bus را به یک خط لوله قابل اعتماد تبدیل می کند: تثبیت حالت اتمی، انتشار تضمین شده (البته «حداقل یک بار»)، مشترکان بی نظیر و کنترل مجدد. با تله متری مناسب، محدودیت ها، و نظم و انضباط طرح، آن را می دهد عملی دقیقا یک بار رفتار، کاهش پیچیدگی معاملات توزیع شده و افزایش انعطاف پذیری سیستم را به سقوط و بارهای اوج.