Redis والتخزين المؤقت المتقدم

Redis مع Node.js

18 دقيقة الدرس 6 من 30

Redis مع Node.js

في هذا الدرس، سنتعلم كيفية دمج Redis مع تطبيقات Node.js باستخدام مكتبة ioredis، وهي واحدة من أكثر عملاء Redis شعبية وثراءً بالميزات لـ Node.js.

تثبيت ioredis

أولاً، قم بتثبيت حزمة ioredis في مشروع Node.js الخاص بك:

npm install ioredis

يمكنك أيضاً التثبيت باستخدام yarn:

yarn add ioredis

الاتصال الأساسي

إليك كيفية إنشاء اتصال أساسي بـ Redis:

const Redis = require('ioredis');

// الاتصال بـ localhost:6379 الافتراضي
const redis = new Redis();

// أو تحديد خيارات الاتصال
const redisCustom = new Redis({
  host: '127.0.0.1',
  port: 6379,
  password: 'your_password',
  db: 0
});

الاتصال باستخدام متغيرات البيئة

أفضل ممارسة هي استخدام متغيرات البيئة للتكوين:

require('dotenv').config();
const Redis = require('ioredis');

const redis = new Redis({
  host: process.env.REDIS_HOST || 'localhost',
  port: process.env.REDIS_PORT || 6379,
  password: process.env.REDIS_PASSWORD,
  db: process.env.REDIS_DB || 0,
  retryStrategy: (times) => {
    const delay = Math.min(times * 50, 2000);
    return delay;
  }
});
نصيحة: أنشئ ملف .env مع بيانات اعتماد Redis الخاصة بك: REDIS_HOST=localhost, REDIS_PORT=6379, REDIS_PASSWORD=your_password

العمليات الأساسية

تنفيذ عمليات Redis الأساسية مع ioredis:

// أمر SET
await redis.set('user:1000', 'John Doe');

// أمر GET
const username = await redis.get('user:1000');
console.log(username); // 'John Doe'

// SET مع انتهاء الصلاحية (EX بالثواني)
await redis.set('session:abc123', 'user_data', 'EX', 3600);

// SETEX (اختصار لـ SET مع انتهاء الصلاحية)
await redis.setex('token:xyz789', 1800, 'auth_token_value');

// أمر DEL
await redis.del('user:1000');

// أمر EXISTS
const exists = await redis.exists('user:1000');
console.log(exists); // 0 (false) أو 1 (true)

العمل مع بيانات JSON

تخزين واسترجاع كائنات JavaScript كسلاسل JSON:

const user = {
  id: 1000,
  name: 'John Doe',
  email: 'john@example.com',
  role: 'admin'
};

// تخزين الكائن كسلسلة JSON
await redis.set('user:1000', JSON.stringify(user));

// الاسترجاع والتحليل
const userData = await redis.get('user:1000');
const parsedUser = JSON.parse(userData);
console.log(parsedUser.name); // 'John Doe'

عمليات Hash

تجزئات Redis مثالية لتخزين الكائنات بدون تسلسل JSON:

// HSET - تعيين حقل التجزئة
await redis.hset('user:1000', 'name', 'John Doe');
await redis.hset('user:1000', 'email', 'john@example.com');

// HMSET - تعيين حقول متعددة دفعة واحدة
await redis.hmset('user:1001', {
  name: 'Jane Smith',
  email: 'jane@example.com',
  age: 28
});

// HGET - الحصول على حقل واحد
const name = await redis.hget('user:1000', 'name');

// HGETALL - الحصول على جميع الحقول
const user = await redis.hgetall('user:1000');
console.log(user); // { name: 'John Doe', email: 'john@example.com' }

// HDEL - حذف حقل
await redis.hdel('user:1000', 'email');

أحداث الاتصال

مراقبة حالة اتصال Redis باستخدام مستمعي الأحداث:

const Redis = require('ioredis');
const redis = new Redis();

redis.on('connect', () => {
  console.log('✓ متصل بـ Redis');
});

redis.on('ready', () => {
  console.log('✓ Redis جاهز لقبول الأوامر');
});

redis.on('error', (err) => {
  console.error('✗ خطأ Redis:', err);
});

redis.on('close', () => {
  console.log('تم إغلاق الاتصال');
});

redis.on('reconnecting', () => {
  console.log('إعادة الاتصال بـ Redis...');
});

معالجة الأخطاء

قم دائماً بتنفيذ معالجة صحيحة للأخطاء عند العمل مع Redis:

async function getUserData(userId) {
  try {
    const userData = await redis.get(`user:${userId}`);
    if (!userData) {
      return null;
    }
    return JSON.parse(userData);
  } catch (error) {
    console.error('خطأ Redis GET:', error);
    throw new Error('فشل استرجاع بيانات المستخدم');
  }
}

async function setUserData(userId, data) {
  try {
    await redis.setex(
      `user:${userId}`,
      3600,
      JSON.stringify(data)
    );
    return true;
  } catch (error) {
    console.error('خطأ Redis SET:', error);
    return false;
  }
}
تحذير: لا تعرض أبداً أخطاء اتصال Redis مباشرة للمستخدمين. سجلها داخلياً وأرجع رسائل خطأ عامة للعملاء.

الإيقاف الرشيق

أغلق دائماً اتصالات Redis عند إيقاف تطبيقك:

// معالج الإيقاف الرشيق
process.on('SIGTERM', async () => {
  console.log('تم استلام SIGTERM، إغلاق اتصال Redis...');
  await redis.quit();
  process.exit(0);
});

process.on('SIGINT', async () => {
  console.log('تم استلام SIGINT، إغلاق اتصال Redis...');
  await redis.quit();
  process.exit(0);
});
ملاحظة: استخدم redis.quit() للإيقاف الرشيق (ينتظر الأوامر المعلقة) أو redis.disconnect() للانفصال الفوري.

تجميع الاتصالات

للتطبيقات ذات التزامن العالي، تدير ioredis تلقائياً تجميع الاتصالات:

const Redis = require('ioredis');

const redis = new Redis({
  host: 'localhost',
  port: 6379,
  maxRetriesPerRequest: 3,
  enableReadyCheck: true,
  enableOfflineQueue: true,
  connectTimeout: 10000,
  lazyConnect: false
});
تمرين: أنشئ وحدة Node.js تقوم بتصدير عميل Redis مُكَوَّن مع معالجة صحيحة للأخطاء، تكوين يعتمد على البيئة، وتسجيل أحداث الاتصال. اختبرها بتخزين واسترجاع كائن مستخدم مع انتهاء صلاحية لمدة 5 دقائق.