ویژگی تصویر

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

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

aioredis یک کتابخانه غیرهمزمان (asynchronous) برای کار با Redis در پایتون است که با استفاده از asyncio اجازه می‌دهد عملیات I/O مرتبط با دیتابیس Redis بدون مسدودسازی حلقه رویداد اجرا شوند. با رشد نیاز به برنامه‌های همزمان (مانند وب‌سرورها، بوت‌ها و سرویس‌های real-time)، استفاده از رابط‌های async برای Redis اهمیت ویژه‌ای یافته است.

توضیح تاریخی و حالت فعلی اکوسیستم

در طی زمان دو مسیر وجود داشت: نسخهٔ اولیهٔ پروژه aioredis (از گروه aio-libs) و سپس ادغام قابلیت‌های async داخل بستهٔ رسمی redis-py (نسخه‌های 4.x به بعد) که با خروجی redis.asyncio ارائه می‌شود. امروزه توصیه می‌شود برای پروژه‌های جدید از پیاده‌سازی رسمی redis.asyncio استفاده کنید اما مفاهیم و الگوهای aioredis سنتی همچنان کاربردی هستند.

مزایا و موارد استفاده

  • عدم مسدودسازی حلقهٔ رویداد (event loop) برای بارهای همزمان بالا.
  • مناسب برای وب‌فریم‌ورک‌های async مانند FastAPI، Starlette، Aiohttp.
  • پشتیبانی از Pub/Sub، پایت‌لاین‌ها، تراکنش‌ها و اسکریپت‌های Lua به صورت async.
  • قابلیت استفاده به عنوان cache، message broker ساده، و store برای session/lock.

نمونهٔ پایه: اتصال و عملیات ساده با redis.asyncio

import asyncio
import redis.asyncio as redis

async def main():
    r = redis.Redis(host='localhost', port=6379, db=0)
    await r.set('mykey', 'hello')
    val = await r.get('mykey')
    print(val)  # b'hello'
    await r.close()
    await r.connection_pool.disconnect()

asyncio.run(main())

در این مثال از بستهٔ رسمی redis.asyncio استفاده شده است. ابتدا اتصال ایجاد می‌شود، سپس با await عملیات SET و GET اجرا می‌شوند. در پایان اتصال بسته و connection pool جدا می‌شود تا منابع آزاد شوند. استفاده از await باعث می‌شود این فراخوانی‌ها غیرهمزمان باشند و حلقهٔ رویداد تا دریافت پاسخ بلوکه نشود.

استفادهٔ امن‌تر با connection pool و context manager

import asyncio
import redis.asyncio as redis

async def main():
    pool = redis.ConnectionPool.from_url("redis://localhost:6379/0")
    async with redis.Redis(connection_pool=pool) as r:
        await r.set('k', 'v')
        print(await r.get('k'))
    # pool در خروج از context manager همچنان قابل استفاده است، اما می‌توانید disconnect کنید:
    await pool.disconnect()

asyncio.run(main())

در این کد از ConnectionPool و context manager استفاده شده تا مدیریت منابع بهتر انجام شود. Context manager برای Redis باعث بسته شدن اتصالات مرتبط با کلاینت می‌شود و pool.disconnect برای قطع کامل اتصالات استفاده می‌شود.

Pub/Sub به صورت غیرهمزمان

import asyncio
import redis.asyncio as redis

async def reader(channel):
    async for msg in channel.iter_messages():
        if msg is None:
            continue
        print("Received:", msg['data'])

async def main():
    r = redis.Redis()
    pubsub = r.pubsub()
    await pubsub.subscribe('my-channel')
    ch = pubsub.channels['my-channel']
    await reader(ch)

asyncio.run(main())

در این مثال یک Subscriber غیرهمزمان ساخته شده که با استفاده از iter_messages پیام‌ها را دریافت می‌کند. توجه داشته باشید که Pub/Sub در شرایط بار بالا باید با دقت مدیریت شود (مثلاً پردازش پیام‌ها در تسک‌های جداگانه تا از مسدودسازی جلوگیری شود).

پایپ‌لاین و تراکنش‌ها

import asyncio
import redis.asyncio as redis

async def main():
    r = redis.Redis()
    # pipeline به صورت atomic با transaction=True
    async with r.pipeline(transaction=True) as pipe:
        pipe.set('a', 1)
        pipe.incr('a')
        results = await pipe.execute()
        print(results)

asyncio.run(main())

pipeline در اینجا به شکل تراکنش اجرا می‌شود تا مجموعه‌ای از عملیات به صورت atomic انجام شوند. استفاده از pipeline باعث کاهش round-trip‌ها و افزایش کارایی می‌شود.

اجرای اسکریپت Lua

import asyncio
import redis.asyncio as redis

async def main():
    r = redis.Redis()
    lua = """
    local v = redis.call('GET', KEYS[1])
    if not v then
        redis.call('SET', KEYS[1], ARGV[1])
        return ARGV[1]
    end
    return v
    """
    res = await r.eval(lua, 1, 'mykey', 'default')
    print(res)

asyncio.run(main())

با استفاده از EVAL می‌توان منطق پیچیده را داخل سرور Redis با Lua اجرا کرد که کارآمد و atomic است. در مثال بالا اگر کلید وجود نداشته باشد مقدار جدید قرار داده و برگردانده می‌شود؛ در غیر این صورت مقدار موجود بازگردانده می‌شود.

بهینه‌سازی، خطاها و الگوهای عملیاتی

  • مدیریت reconnect: هنگام استفاده در محیط‌های پراکسیمیتو یا شبکهٔ ناپایدار از مکانیزم retry با backoff استفاده کنید.
  • اندازهٔ pool را متناسب با concurrency تنظیم کنید؛ مقدار خیلی کم باعث صف شدن و مقدار زیاد باعث مصرف منابع می‌شود.
  • برای عملیات سنگین روی پیام‌ها، پردازش را به تسک‌های جداگانه واگذار کنید تا subscriber بلوکه نشود.
  • در خوشه Redis یا Sentinel از پارامترهای مربوطه (cluster/sentinel) در پیکربندی connection استفاده کنید.

مقایسهٔ مختصر: aioredis (کهنه) vs redis.asyncio (توصیه‌شده)

ویژگیaioredis (قدیمی)redis.asyncio (امروزی)
پشتیبانی رسمیکمی قدیمی، پروژه اصلی کمتر فعالبستهٔ رسمی redis-py با پشتیبانی جامع
سازگاریسازگار با asyncio اما تفاوت API داردسازگاری کامل با redis-py و مستندات رسمی
مهاجرتنیاز به تغییر API در پروژه‌های جدیدپیشنهادی برای پروژه‌های جدید

نتیجه‌گیری و توصیه‌ها

برای برنامه‌های جدید توصیه می‌شود از redis.asyncio (نسخهٔ رسمی redis-py با پشتیبانی async) استفاده کنید. در هر حال مفاهیم اتصال غیرهمزمان، connection pool، pub/sub، pipeline و اجرای Lua در هر دو راهکار وجود دارند. حتماً مدیریت منابع، retry و مانیتورینگ را در طراحی سیستم مدنظر قرار دهید تا از پایداری و کارایی مطلوب برخوردار شوید.

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

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