ویژگی تصویر

معرفی کتابخانه pyzmq در پایتون

  /  پایتون   /  کتابخانه pyzmq در پایتون
بنر تبلیغاتی الف

pyzmq یک پیمانه (binding) پایتون برای کتابخانه قدرتمند پیام‌رسانی ZeroMQ است که امکانات متنوعی برای ارتباط بین پروسس‌ها، ماشین‌ها و نخ‌ها فراهم می‌کند. این کتابخانه سبک، سریع و انعطاف‌پذیر است و برای پیاده‌سازی الگوهای پیام‌رسانی مانند درخواست/پاسخ، انتشار/اشتراک، صف کاری و مسیریابی مناسب است.

چرا pyzmq؟

  • سرعت بالا و تاخیر پایین
  • الگوهای پیام‌رسانی آماده (REQ/REP, PUB/SUB, PUSH/PULL, ROUTER/DEALER)
  • پشتیبانی از پروتکل‌های مختلف: tcp://, ipc://, inproc://
  • قابلیت ادغام با asyncio و کتابخانه‌های همزمانی
  • مناسب برای سیستم‌های توزیع‌شده، میکروسرویس‌ها، و بار پردازشی توزیع‌شده

نصب و پیش‌نیازها

برای نصب pyzmq به سادگی از pip استفاده کنید. در اکثر حالات نیازی به نصب دستی ZeroMQ نیست چون wheel شامل باینری است:

pip install pyzmq

اگر به CURVE یا قابلیت‌های خاص نیاز دارید، ممکن است لازم باشد zeromq سیستم را نصب کنید.

مفاهیم پایه: Context و Socket

در pyzmq یک Context به‌عنوان کارخانهٔ ایجاد socketها عمل می‌کند. هر Socket نوع خاصی از الگو (pattern) را پیاده‌سازی می‌کند و معمولاً در سطح پروسس ایمن و در سطح نخ‌ها ایمن نیست؛ یعنی یک socket را نباید از چند نخ به‌صورت همزمان استفاده کرد.

نمونه ساده REQ/REP

import zmq

context = zmq.Context()

# Server (REPLY)
rep = context.socket(zmq.REP)
rep.bind("tcp://*:5555")

# Client (REQUEST)
req = context.socket(zmq.REQ)
req.connect("tcp://localhost:5555")

# Client sends
req.send(b"Hello")
msg = rep.recv()
print("Server received:", msg)
rep.send(b"World")
print("Client received:", req.recv())

این کد ساختار پایهٔ REQ/REP را نشان می‌دهد. سرور REP روی پورت 5555 بایند شده و کلاینت REQ به آن متصل می‌شود. REQ باید پس از ارسال، منتظر پاسخ باشد و REP پس از دریافت پیام، پاسخ می‌دهد. این الگو برای درخواست/پاسخ همزمان مناسب است.

الگوهای پیام‌رسانی متداول

الگوکاربرد
REQ/REPدرخواست و پاسخ همزمان
PUB/SUBارسال یک‌طرفه به چند مشترک (Broadcast)
PUSH/PULLصف کار دسته‌ای و توزیع وظایف
ROUTER/DEALERمسیردهی پیشرفته و میانجی‌سازی پیام‌ها

مثال PUB/SUB

import zmq
import time

context = zmq.Context()

# Publisher
pub = context.socket(zmq.PUB)
pub.bind("tcp://*:5556")

# Subscriber (in another process)
sub = context.socket(zmq.SUB)
sub.connect("tcp://localhost:5556")
sub.setsockopt_string(zmq.SUBSCRIBE, "topic1")

# Publisher loop
time.sleep(0.5)  # allow subscriber to connect
pub.send_string("topic1 Hello subscribers!")

در PUB/SUB، سابسکرایبرها می‌توانند با تعیین فیلتر (topic) پیام‌هایی که دریافت می‌کنند را محدود کنند. دقت کنید که قبل از ارسال اولیه، بهتر است زمان کوتاهی برای متصل شدن سابسکرایبرها در نظر بگیرید (به‌ویژه در آزمایش محلی).

نکات پیشرفته و بهینه‌سازی

  • تنظیم HWM (High Water Mark): با zmq.SNDHWM و zmq.RCVHWM می‌توانیم حافظه بافر را کنترل کنیم و از مصرف بیش‌ازحد جلوگیری کنیم.
  • linger: zmq.LINGER برای مشخص‌کردن زمان باقی‌ماندن پیام‌ها هنگام بستن سوکت مفید است.
  • Threading: هر سوکت را تنها در یک نخ استفاده کنید یا از inproc:// برای ارتباط میان نخ‌ها بهره ببرید.
  • امنیت: ZeroMQ از مکانیزم CURVE برای رمزنگاری و احراز هویت پشتیبانی می‌کند؛ برای محیط‌های باز توصیه می‌شود.
  • فشرده‌سازی و سریال‌سازی: برای پیام‌های پیچیده از msgpack یا protobuf استفاده کنید تا بهینه‌تر و سریع‌تر از JSON عمل کند.

نمونه asyncio با zmq.asyncio

import asyncio
import zmq
import zmq.asyncio

ctx = zmq.asyncio.Context()
sock = ctx.socket(zmq.PULL)
sock.bind("tcp://*:5557")

async def worker():
    while True:
        msg = await sock.recv()
        print("Received:", msg)

asyncio.run(worker())

pyzmq یک پیاده‌سازی سازگار با asyncio دارد (zmq.asyncio) که امکان استفاده از await/async را فراهم می‌کند. در این مثال یک PULL که به صورت آسنکرون منتظر پیام می‌ماند نشان داده شده است؛ مناسب برای سرویس‌هایی که با حلقهٔ رویداد کار می‌کنند.

قابلیت‌های مفید دیگر

  • Multipart Messages: ارسال چندبخشی با send_multipart و recv_multipart برای تفکیک فریم‌های پیام.
  • Poller: zmq.Poller یا zmq.asyncio.Poller جهت نظارت همزمان روی چند سوکت.
  • Fair Queueing: با ترکیب PUSH/PULL می‌توان بار را بین ورکرها به‌صورت متعادل توزیع کرد.
  • Proxy: zmq.proxy برای ساخت پل بین socketها و مسیریابی ساده مفید است.

مثال multipart و poller

import zmq

ctx = zmq.Context()
sock = ctx.socket(zmq.ROUTER)
sock.bind("tcp://*:5558")

while True:
    parts = sock.recv_multipart()
    # parts[0] = identity, parts[1:] = message frames
    sock.send_multipart([parts[0], b"ACK"])

این مثال از سوکت ROUTER استفاده می‌کند که هویت فرستنده را به‌صورت بخشی از پیام نگه می‌دارد و با recv_multipart می‌توان فریم‌ها را جدا کرد. سپس با send_multipart می‌توان پاسخ را به هویت مشخص شده فرستاد.

موارد استفاده و سناریوهای واقعی

  • توزیع وظایف در خوشه‌های پردازشی (task queue)
  • سیستم‌های تله‌متری و جمع‌آوری لاگ به صورت Push/Pull
  • انتشار وقایع بازار در سیستم‌های مالی با الگوی PUB/SUB
  • مسیردهی پیام‌ها بین میکروسرویس‌ها با ROUTER/DEALER

نکات عملی و ریسک‌ها

pyzmq ابزار قدرتمندی است اما نیازمند طراحی دقیق است: باید دانش کافی درباره مدل حافظه و قوانین پیام‌رسانی ZeroMQ داشت. در شبکه‌های ناهموار باید موضوعات مربوط به reconnect و buffering را مدیریت کنید و برای امنیت از CURVE یا TLS استفاده کنید. همچنین توجه داشته باشید که مدیریت خطا و بستن صحیح sockets با set_linger ضروری است.

جمع‌بندی

pyzmq یک کتابخانه منعطف و سریع برای ارتباطات بین فرآیندی و توزیع‌شده در پایتون است. با یادگیری الگوهای اصلی (REQ/REP, PUB/SUB, PUSH/PULL, ROUTER/DEALER) و رعایت نکات عملکردی و امنیتی می‌توان سیستم‌های مقیاس‌پذیر و قابل اعتمادی پیاده‌سازی کرد. شروع با نمونه‌های ساده و سپس رفتن به سمت proxy، poller و CURVE بهترین مسیر یادگیری است.

آیا این مطلب برای شما مفید بود ؟

خیر
بله
موضوعات شما در انجمن: