1.0.9 • Published 1 year ago

@everydaydoctor/shared-redis v1.0.9

Weekly downloads
-
License
MIT
Repository
gitlab
Last release
1 year ago

Shared Redis

ไลบรารีสำหรับแชร์ข้อมูลผ่าน Redis สำหรับ Node.js ที่ช่วยให้การใช้งาน Redis ง่ายขึ้น โดยรองรับการใช้งานพื้นฐานทั้ง Cache และ Pub/Sub พร้อมความสามารถในการกำหนด prefix

คุณสมบัติหลัก

  • 🚀 ง่ายต่อการใช้งาน - API ที่เรียบง่ายและเข้าใจง่าย
  • 🔄 Cache - จัดการข้อมูล cache ได้อย่างง่ายดาย
  • 📡 Pub/Sub - ระบบรับส่งข้อความระหว่างแอปพลิเคชัน
  • 🔑 Prefix - รองรับการใช้ prefix เพื่อแยกข้อมูลระหว่างแอปพลิเคชัน
  • 🌐 Global & Prefixed Instances - สามารถใช้งานได้ทั้งแบบ global และแบบกำหนด prefix
  • 🔒 Singleton - ใช้ Singleton pattern เพื่อจัดการการเชื่อมต่อ

การติดตั้ง

npm install @everydaydoctor/shared-redis

หรือ

yarn add @everydaydoctor/shared-redis

การใช้งาน

การเชื่อมต่อกับ Redis

วิธีที่ 1: ใช้ค่าเริ่มต้น

const redis = require('@everydaydoctor/shared-redis');

// เชื่อมต่อกับ Redis ด้วยค่าเริ่มต้น (localhost:6379)
// redis.client จะถูกสร้างโดยอัตโนมัติเมื่อ import ไลบรารี

// ทดสอบการเชื่อมต่อ
const result = await redis.client.ping();
console.log(result); // PONG

วิธีที่ 2: กำหนดค่า config เอง

const redis = require('@everydaydoctor/shared-redis');
const { createClient } = redis;

// สร้าง client ใหม่ด้วยการตั้งค่าเอง
const client = createClient({
  host: 'redis-server',  // ชื่อโฮสต์หรือ IP ของ Redis server
  port: 6379,            // พอร์ตของ Redis server
  password: 'your-password', // รหัสผ่าน (ถ้ามี)
  db: 0,                 // เลือกฐานข้อมูล Redis (0-15)
  prefix: 'app:'         // กำหนด prefix สำหรับทุก key
});

// ทดสอบการเชื่อมต่อ
const result = await client.ping();
console.log(result); // PONG

// สร้าง cache และ pubsub จาก client ที่กำหนดค่าเอง
const customCache = redis.createCache(client);
const customPubSub = redis.createPubSub(client);

// ใช้งาน cache
const myCache = customCache.getGlobalCache();
await myCache.set('custom-key', 'custom-value');

วิธีที่ 3: ใช้ Redis URL

const redis = require('@everydaydoctor/shared-redis');

// สร้าง client ด้วย Redis URL
const client = redis.createClient({
  url: 'redis://:password@redis-server:6379/0'
});

// ทดสอบการเชื่อมต่อ
const result = await client.ping();
console.log(result); // PONG

วิธีที่ 4: ใช้ Redis Cluster

const Redis = require('ioredis');
const redis = require('@everydaydoctor/shared-redis');

// สร้าง Redis Cluster client ด้วย ioredis โดยตรง
const cluster = new Redis.Cluster([
  { host: 'redis-node1', port: 6379 },
  { host: 'redis-node2', port: 6379 },
  { host: 'redis-node3', port: 6379 }
]);

// ใช้ cluster client กับ shared-redis
// (ต้องปรับแต่งเพิ่มเติมเนื่องจาก shared-redis ไม่รองรับ cluster โดยตรง)

การใช้งาน Cache แบบ Global

const redis = require('@everydaydoctor/shared-redis');

// ใช้งาน cache แบบ global (ไม่มี prefix)
await redis.cache.set('key', { name: 'value' });
const value = await redis.cache.get('key');
console.log(value); // { name: 'value' }

// บันทึกข้อมูลพร้อมกำหนดเวลาหมดอายุ (TTL)
await redis.cache.set('key-with-ttl', 'value', { ttl: 60 }); // หมดอายุใน 60 วินาที

// ตรวจสอบว่ามีข้อมูลในคีย์หรือไม่
const exists = await redis.cache.exists('key');
console.log(exists); // true

// เพิ่มค่าตัวเลข
await redis.cache.set('counter', 0);
await redis.cache.increment('counter'); // เพิ่มค่าเป็น 1
await redis.cache.increment('counter', 5); // เพิ่มค่าอีก 5 เป็น 6

// ลดค่าตัวเลข
await redis.cache.decrement('counter'); // ลดค่าเป็น 5

// ลบข้อมูลออกจาก cache
await redis.cache.delete('key');

// ล้างข้อมูลทั้งหมดใน cache
await redis.cache.clear();

การใช้งาน Cache กับ Prefix

const redis = require('@everydaydoctor/shared-redis');

// ใช้งาน cache กับ prefix 'user:'
const userCache = redis.getCacheWithPrefix('user:');

// บันทึกข้อมูลลงใน cache
// key จริงที่เก็บใน Redis จะเป็น "user:123"
await userCache.set('123', { name: 'John', age: 30 });

// ดึงข้อมูลจาก cache
const user = await userCache.get('123');
console.log(user); // { name: 'John', age: 30 }

// ตรวจสอบ prefix ที่ใช้อยู่
console.log(userCache.getPrefix()); // "user:"

// ใช้งาน cache กับอีก prefix
const productCache = redis.getCacheWithPrefix('product:');
await productCache.set('456', { name: 'Laptop', price: 1000 });
const product = await productCache.get('456');
console.log(product); // { name: 'Laptop', price: 1000 }

การใช้งาน Cache อย่างละเอียด

การจัดเก็บข้อมูลประเภทต่างๆ

const redis = require('@everydaydoctor/shared-redis');
const cache = redis.cache;

// เก็บข้อมูลประเภท string
await cache.set('string-key', 'Hello World');

// เก็บข้อมูลประเภท number
await cache.set('number-key', 42);

// เก็บข้อมูลประเภท boolean
await cache.set('boolean-key', true);

// เก็บข้อมูลประเภท object
await cache.set('object-key', { 
  name: 'John', 
  age: 30, 
  active: true,
  skills: ['JavaScript', 'Node.js', 'Redis']
});

// เก็บข้อมูลประเภท array
await cache.set('array-key', [1, 2, 3, 4, 5]);

// เก็บข้อมูลประเภท Date
await cache.set('date-key', new Date());
// หมายเหตุ: Date จะถูกแปลงเป็น string เมื่อเก็บใน Redis
// เมื่อดึงข้อมูลออกมา คุณต้องแปลงกลับเป็น Date object เอง
const dateStr = await cache.get('date-key');
const date = new Date(dateStr);

การกำหนดเวลาหมดอายุ (TTL)

const redis = require('@everydaydoctor/shared-redis');
const cache = redis.cache;

// กำหนดเวลาหมดอายุตอนบันทึกข้อมูล
await cache.set('session', { userId: 123 }, { ttl: 3600 }); // หมดอายุใน 1 ชั่วโมง

// กำหนดเวลาหมดอายุหลังจากบันทึกข้อมูลแล้ว
await cache.set('temp-data', { status: 'processing' });
await cache.expire('temp-data', 300); // หมดอายุใน 5 นาที

// ตรวจสอบว่าข้อมูลยังอยู่หรือไม่ (หลังจากหมดอายุ)
setTimeout(async () => {
  const exists = await cache.exists('temp-data');
  console.log('ข้อมูลยังอยู่หรือไม่:', exists); // false หลังจาก 5 นาที
}, 5 * 60 * 1000 + 100); // 5 นาที + 100ms

การใช้งานกับข้อมูลตัวเลข

const redis = require('@everydaydoctor/shared-redis');
const cache = redis.cache;

// เริ่มต้นด้วยค่า 0
await cache.set('views', 0);

// เพิ่มค่าทีละ 1
await cache.increment('views'); // 1
await cache.increment('views'); // 2
await cache.increment('views'); // 3

// เพิ่มค่าตามที่กำหนด
await cache.increment('views', 10); // 13

// ลดค่าทีละ 1
await cache.decrement('views'); // 12

// ลดค่าตามที่กำหนด
await cache.decrement('views', 5); // 7

// อ่านค่าปัจจุบัน
const views = await cache.get('views');
console.log('จำนวนการเข้าชม:', views); // 7

การใช้งานกับหลาย prefix

const redis = require('@everydaydoctor/shared-redis');

// สร้าง cache สำหรับแต่ละส่วนของแอปพลิเคชัน
const userCache = redis.getCacheWithPrefix('user:');
const productCache = redis.getCacheWithPrefix('product:');
const orderCache = redis.getCacheWithPrefix('order:');
const sessionCache = redis.getCacheWithPrefix('session:');

// ใช้งานแต่ละ cache
await userCache.set('123', { name: 'John' });
await productCache.set('456', { name: 'Laptop' });
await orderCache.set('789', { userId: '123', productId: '456' });
await sessionCache.set('abc', { userId: '123' }, { ttl: 3600 });

// ข้อมูลจะถูกเก็บใน Redis ด้วย key ที่มี prefix ต่างกัน:
// - "user:123"
// - "product:456"
// - "order:789"
// - "session:abc"

การใช้งาน PubSub แบบ Global

const redis = require('@everydaydoctor/shared-redis');

// สมัครรับข้อความจากช่องทาง 'channel-name'
await redis.pubsub.subscribe('channel-name', (channel, message) => {
  console.log(`ได้รับข้อความจากช่อง ${channel}: ${message}`);
  
  // ถ้าข้อความเป็น JSON สามารถแปลงกลับได้ด้วย
  try {
    const data = JSON.parse(message);
    console.log(data);
  } catch (e) {
    // ไม่ใช่ JSON
  }
});

// ส่งข้อความไปยังช่องทาง 'channel-name'
await redis.pubsub.publish('channel-name', 'Hello, world!');

// ส่งข้อความที่เป็นออบเจ็กต์
await redis.pubsub.publish('channel-name', { hello: 'world' });

// สมัครรับข้อความจากช่องทางที่ตรงกับรูปแบบ
await redis.pubsub.psubscribe('user:*', (channel, message) => {
  console.log(`ได้รับข้อความจากช่อง ${channel}: ${message}`);
});

// ยกเลิกการรับข้อความจากช่องทาง 'channel-name'
await redis.pubsub.unsubscribe('channel-name');

// ยกเลิกการรับข้อความจากช่องทางที่ตรงกับรูปแบบ
await redis.pubsub.punsubscribe('user:*');

// ปิดการเชื่อมต่อ
await redis.pubsub.close();

การใช้งาน PubSub กับ Prefix

const redis = require('@everydaydoctor/shared-redis');

// ใช้งาน pubsub กับ prefix 'user:'
const userPubSub = redis.getPubSubWithPrefix('user:');

// สมัครรับข้อความจากช่องทาง 'notifications'
// channel จริงที่ใช้ใน Redis จะเป็น "user:notifications"
await userPubSub.subscribe('notifications', (channel, message) => {
  console.log(`ได้รับข้อความจากช่อง ${channel}: ${message}`);
});

// ส่งข้อความไปยังช่องทาง 'notifications'
await userPubSub.publish('notifications', { type: 'new_message', content: 'Hello!' });

// ใช้งาน pubsub กับอีก prefix
const systemPubSub = redis.getPubSubWithPrefix('system:');
await systemPubSub.subscribe('alerts', (channel, message) => {
  console.log(`ได้รับการแจ้งเตือนระบบ: ${message}`);
});

ตัวอย่างการใช้งานจริง

ใช้เป็น Session Store

const redis = require('@everydaydoctor/shared-redis');
const sessionCache = redis.getCacheWithPrefix('session:');

// บันทึก session
async function saveSession(sessionId, userData) {
  await sessionCache.set(sessionId, userData, { ttl: 3600 }); // หมดอายุใน 1 ชั่วโมง
}

// ดึงข้อมูล session
async function getSession(sessionId) {
  return await sessionCache.get(sessionId);
}

// ลบ session
async function deleteSession(sessionId) {
  await sessionCache.delete(sessionId);
}

ใช้เป็น Rate Limiter

const redis = require('@everydaydoctor/shared-redis');
const limiterCache = redis.getCacheWithPrefix('ratelimit:');

// ตรวจสอบ rate limit
async function checkRateLimit(userId, limit = 100, windowSec = 3600) {
  const key = `user:${userId}`;
  
  // ดึงจำนวนครั้งที่ใช้งานปัจจุบัน
  let count = await limiterCache.get(key) || 0;
  
  // ถ้ายังไม่เกิน limit ให้เพิ่มค่า
  if (count < limit) {
    if (count === 0) {
      // ถ้าเป็นครั้งแรก ให้ตั้งค่าเริ่มต้นและกำหนดเวลาหมดอายุ
      await limiterCache.set(key, 1, { ttl: windowSec });
    } else {
      // ถ้าไม่ใช่ครั้งแรก ให้เพิ่มค่า
      await limiterCache.increment(key);
    }
    return true;
  }
  
  return false; // เกิน limit
}

ใช้เป็นระบบแจ้งเตือนแบบ Real-time

const redis = require('@everydaydoctor/shared-redis');
const notificationPubSub = redis.getPubSubWithPrefix('notification:');

// ฝั่งเซิร์ฟเวอร์: ส่งการแจ้งเตือนไปยังผู้ใช้
async function sendNotification(userId, notification) {
  await notificationPubSub.publish(`user:${userId}`, notification);
}

// ฝั่งไคลเอนต์: รับการแจ้งเตือน
async function subscribeToNotifications(userId, callback) {
  await notificationPubSub.subscribe(`user:${userId}`, (channel, message) => {
    const notification = JSON.parse(message);
    callback(notification);
  });
}

หมายเหตุเกี่ยวกับ Prefix

ไลบรารีนี้รองรับการใช้งาน prefix ใน 2 รูปแบบ:

  1. Prefix ที่กำหนดในตอนสร้าง client:

    const client = redis.createClient({ prefix: 'myapp:' });

    Prefix นี้จะถูกใช้กับทุก key ใน Redis โดยอัตโนมัติ

  2. Prefix ที่กำหนดในตอนใช้งาน cache หรือ pubsub:

    const userCache = redis.getCacheWithPrefix('user:');
    const productCache = redis.getCacheWithPrefix('product:');

    วิธีนี้ช่วยให้คุณสามารถแยกข้อมูลระหว่างส่วนต่างๆ ของแอปพลิเคชันได้

การแก้ไขปัญหาทั่วไป

Prefix ไม่ทำงาน

ตรวจสอบว่าคุณได้กำหนด prefix ในตอนสร้าง client หรือใช้ getCacheWithPrefix:

// วิธีที่ 1: กำหนด prefix ในตอนสร้าง client
const client = redis.createClient({ prefix: 'myapp:' });

// วิธีที่ 2: ใช้ getCacheWithPrefix
const cache = redis.getCacheWithPrefix('myapp:');

การเชื่อมต่อล้มเหลว

ตรวจสอบว่า Redis server กำลังทำงานอยู่และสามารถเข้าถึงได้:

try {
  const pong = await redis.client.ping();
  console.log('การเชื่อมต่อสำเร็จ:', pong);
} catch (error) {
  console.error('การเชื่อมต่อล้มเหลว:', error);
}

การปิดการเชื่อมต่อ

เมื่อต้องการปิดการเชื่อมต่อกับ Redis:

// ปิดการเชื่อมต่อทั้งหมด
await redis.client.disconnectAll();

// หรือปิดเฉพาะ pubsub
await redis.pubsub.close();

ตัวอย่างโค้ด

ดูตัวอย่างการใช้งานเพิ่มเติมได้ที่ examples/basic.js

License

MIT

1.0.9

1 year ago

1.0.8

1 year ago

1.0.7

1 year ago

1.0.6

1 year ago

1.0.5

1 year ago

1.0.4

1 year ago

1.0.2

1 year ago

1.0.0

1 year ago