full
This commit is contained in:
80
utils/redisClient.js
Normal file
80
utils/redisClient.js
Normal file
@@ -0,0 +1,80 @@
|
||||
const { createClient } = require('redis')
|
||||
|
||||
// یک کلاینتِ Redis مشترک برای کل اپ (presence، کش، rate-limit، session) — Redis
|
||||
// اختیاریه: اگه سرورش در دسترس نباشه (چه موقع boot، چه بعداً قطع بشه)، هیچجای اپ
|
||||
// نباید کرش کنه یا هنگ کنه. برای همین getRedisClient() بهجای خودِ کلاینت، فقط وقتی
|
||||
// واقعاً "ready"ه اون رو برمیگردونه؛ صدازنندهها همیشه باید مسیرِ fallback
|
||||
// (مستقیم Mongo/memory) رو هم داشته باشن، دقیقاً عین چیزی که توی lastOnline.js و
|
||||
// middlewares/index.js پیاده شده
|
||||
let client = null
|
||||
let isReady = false
|
||||
|
||||
function buildRedisUrl () {
|
||||
if (process.env.REDIS_URL) return process.env.REDIS_URL
|
||||
const host = process.env.REDIS_HOST || '127.0.0.1'
|
||||
const port = process.env.REDIS_PORT || '6379'
|
||||
const password = process.env.REDIS_PASSWORD || ''
|
||||
const db = process.env.REDIS_DB || '0'
|
||||
const auth = password ? `:${encodeURIComponent(password)}@` : ''
|
||||
return `redis://${auth}${host}:${port}/${db}`
|
||||
}
|
||||
|
||||
function getClient () {
|
||||
if (client) return client
|
||||
|
||||
client = createClient({
|
||||
url: buildRedisUrl(),
|
||||
socket: {
|
||||
// بکآف نمایی با سقف ۱۰ ثانیه — تا ابد retry میکنه (وقتی سرورِ Redis بعداً بالا
|
||||
// اومد خودش وصل میشه)، ولی هیچوقت کل اپ رو برای اتصال بلاک نمیکنه چون
|
||||
// connectRedis() پایین await نمیشه
|
||||
reconnectStrategy: (retries) => Math.min(retries * 200, 10000),
|
||||
// بدون این، وقتی REDIS_HOST به یه آدرسِ اشتباه/غیرقابلدسترس (نه صرفاً «رد شد»،
|
||||
// بلکه ساکت هیچ پاسخی نمیده) اشاره کنه، تلاشِ اتصال میتونه تا timeoutِ پیشفرضِ
|
||||
// TCP سطحِ سیستمعامل (روی بعضی سیستمها ۱۰۰+ ثانیه) طول بکشه — و هر درخواستی که
|
||||
// منتظرِ همون تلاشِ اتصاله (مثلاً session store) به همون اندازه کند میشه
|
||||
connectTimeout: 5000
|
||||
}
|
||||
})
|
||||
|
||||
// کتابخونهی redis v4+: بدون این listener، هر خطای اتصال کل پروسهی Node رو
|
||||
// کرش میکنه (unhandled 'error' event) — این مهمترین خطِ این فایله
|
||||
client.on('error', (err) => {
|
||||
isReady = false
|
||||
console.error('[redis] connection error:', err.message)
|
||||
})
|
||||
client.on('ready', () => {
|
||||
isReady = true
|
||||
console.log('[redis] connected')
|
||||
})
|
||||
client.on('end', () => {
|
||||
isReady = false
|
||||
})
|
||||
|
||||
return client
|
||||
}
|
||||
|
||||
// عمداً await نمیشه توی boot — اگه Redis در دسترس نباشه، بوتشدنِ کل سرور نباید
|
||||
// معطلِ چندینبار retry بمونه؛ اتصال توی بکگراند انجام میشه و هر وقت آماده شد
|
||||
// isReady=true میشه
|
||||
function connectRedis () {
|
||||
const c = getClient()
|
||||
c.connect().catch((err) => {
|
||||
console.error('[redis] initial connection failed — app continues without Redis:', err.message)
|
||||
})
|
||||
}
|
||||
|
||||
// همهجای اپ باید از همین تابع استفاده کنن، نه مستقیم از getRawClient() — چون فقط
|
||||
// وقتی واقعاً متصله چیزی برمیگردونه
|
||||
function getRedisClient () {
|
||||
return isReady ? client : null
|
||||
}
|
||||
|
||||
// فقط برای jaهایی مثل connect-redis که خودشون client رو نگه میدارن و باید همون
|
||||
// اول (حتی قبل از وصلشدن) بهش رفرنس بدیم — چون کتابخونهی redis v4 دستورهای
|
||||
// قبل از اتصال رو صف میکنه (offline queue)، نه اینکه فوراً خطا بده
|
||||
function getRawClient () {
|
||||
return getClient()
|
||||
}
|
||||
|
||||
module.exports = { connectRedis, getRedisClient, getRawClient }
|
||||
Reference in New Issue
Block a user