1.0.9 • Published 1 year ago
@everydaydoctor/shared-redis v1.0.9
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 รูปแบบ:
Prefix ที่กำหนดในตอนสร้าง client:
const client = redis.createClient({ prefix: 'myapp:' });Prefix นี้จะถูกใช้กับทุก key ใน Redis โดยอัตโนมัติ
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