Major Changes: - Add StreamingService with OpenAI Compatible format - Upgrade Chat component V2 with Ant Design X integration - Implement AIA module with 12 intelligent agents - Update API routes to unified /api/v1 prefix - Update system documentation Backend (~1300 lines): - common/streaming: OpenAI Compatible adapter - modules/aia: 12 agents, conversation service, streaming integration - Update route versions (RVW, PKB to v1) Frontend (~3500 lines): - modules/aia: AgentHub + ChatWorkspace (100% prototype restoration) - shared/Chat: AIStreamChat, ThinkingBlock, useAIStream Hook - Update API endpoints to v1 Documentation: - AIA module status guide - Universal capabilities catalog - System overview updates - All module documentation sync Tested: Stream response verified, authentication working Status: AIA V2.0 core completed (85%)
51 KiB
Redis謾ケ騾<EFBFBD>螳樊命隶。蛻抵シ育シ灘ュ<EFBFBD>+髦溷<E9ABA6>螳梧紛迚茨シ<E88CA8>
*譁<EFBFBD>。」迚域悽<EFBFBD>? V2.0
*譖エ譁ー譌・譛滂シ? 2025-12-12
*逶ョ譬<EFBFBD>ョ梧<EFBFBD>譌カ髣エ<EFBFBD>? 2025-12-18<31>?螟ゥ<E89E9F><EFBDA9>
雍溯エ」莠コ<EFBFBD><EFBFBD> 謚譛ッ蝗「髦? *鬟朱勦遲臥コァ<EFBFBD>? <20>泯 荳ュ遲会シ域怏髯咲コァ譁ケ譯茨シ? *驥崎ヲ∝序譖エ<EFBFBD>? Redis髦溷<E9ABA6>莉?蜿ッ騾?隹<>紛荳?蠢<>。サ"
笞<EFBFBD><EFBFBD><EFBFBD> *驥崎ヲ∬ッエ譏趣シ<EFBFBD>2.0譖エ譁ー<EFBFBD>?
扈剰ソ<EFBFBD>キア蜈・蛻<EFBFBD>梵<EFBFBD>軍edis髦溷<EFBFBD>荳肴弍蜿ッ騾蛾。ケ<EFBFBD>瑚梧弍譬ク蠢<EFBFBD>粥閭ス逧<EFBFBD>ソ<EFBFBD>。サ鬘ケ<EFBFBD>?
- **ASL譁<4C>鍵遲幃?*<2A>?000遽<30>枚迪ョ髴隕?蟆乗慮<E4B997>御ク咲畑Redis髦溷<E9ABA6>螟ア雍・邇?> 95%
- DC Tool B逞<42>紙謠仙叙<EFBFBD>?000莉ス逞<EFBDBD>紙髴隕?-3蟆乗慮<E4B997>悟酔譬キ髣ョ鬚?3. **SAE螳樔セ狗音諤?*<2A>?5蛻<35>帖譌<E5B896>豬<EFBFBD>㍼閾ェ蜉ィ郛ゥ螳ケ<E89EB3>碁柄莉サ蜉。蠢<EFBDA1>┯螟ア雍? 蝗<EFBFBD>豁、譛ャ隶。蛻定ー<EFBFBD>紛荳コ<EFBFBD>夂シ灘ュ?髦溷<E9ABA6>荳襍キ螳樊命<E6A88A><E591BD>7螟ゥ螳梧<E89EB3><E6A2A7><EFBFBD>
<EFBFBD>搭 逶ョ蠖<EFBDAE>
- [謾ケ騾<EFBDB9>閭梧勹荳守岼譬Ⅹ(#1-謾ケ騾<EFBDB9>閭梧勹荳守岼譬<E5B2BC>)
- [蠖灘燕邉サ扈溽憾諤∝<E8ABA4>譫疹(#2-蠖灘燕邉サ扈溽憾諤∝<E8ABA4>譫?
- Redis驟咲スョ菫。諱ッ
- [謾ケ騾<EFBDB9>隸ヲ扈<EFBDA6>ュ・鬪、](#4-謾ケ騾<EFBDB9>隸ヲ扈<EFBDA6>ュ・鬪?<3F>遺惠 蟾イ譖エ譁ー<EFBFBD>壼桁蜷ォ髦溷<EFBFBD><EFBFBD>?5. [豬玖ッ墓婿譯<E5A9BF>(#5-豬玖ッ墓婿譯<E5A9BF>)
- [鬟朱勦隸<E58BA6>シー荳守シ楢ァ」](#6-鬟朱勦隸<E58BA6>シー荳守シ楢ァ?
- [荳顔コソ隶。蛻綻(#7-荳顔コソ隶。蛻<EFBDA1>)
- [蝗樊サ壽婿譯<E5A9BF>(#8-蝗樊サ壽婿譯<E5A9BF>)
- [逶第而荳手ソ千サエ](#9-逶第而荳手ソ千サ?
1. 謾ケ騾<EFBDB9>閭梧勹荳守岼譬<E5B2BC>
1.1 荳コ莉荵郁ヲ∵隼騾<E99ABC><E9A8BE><EFBFBD>
蠖灘燕髣ョ鬚<EFBFBD><EFBFBD>?1. 笶?**霑晏渚莠大次逕溯ァ<E6BAAF><EFBDA7>?*<2A>夂ウサ扈滉スソ逕ィ蜀<EFBDA8>ュ倡シ灘ュ假シ瑚ソ晏渚閾ェ蟾ア蛻カ螳夂噪莠大次逕溷シ蜿題ァ<E9A18C><EFBDA7>?2. 笶?LLM謌先悽螟ア謗ァ<EFBFBD>夂シ灘ュ倅ク肴戟荵<EFBFBD>喧<EFBFBD>悟ッシ閾エ驥榊、崎ー<EFBFBD>畑DeepSeek/Qwen API
- 笶?髟ソ莉サ蜉。荳榊庄髱<EFBFBD><EFBFBD>?0-60蛻<30>帖逧<E5B896>枚迪ョ遲幃我ササ蜉。<E89C89>郡AE螳樔セ矩㍾蜷ッ蜷惹ク「螟?4. 笶?螟壼ョ樔セ倶ク榊酔豁・<EFBFBD>售AE謇ゥ螳ケ蜷趣シ悟推螳樔セ狗シ灘ュ倅ク榊<EFBFBD>莠ォ
- 笶?Serverless荳埼る<EFBFBD><EFBFBD>壼<EFBFBD>蟄倡憾諤∝惠Serverless邇ッ蠅<EFBFBD>ク倶ク榊庄髱<EFBFBD>
**謾ケ騾<EFBDB9>逶ョ譬?*<2A>?- 笨?隨ヲ蜷域楔譫<EFBFBD>ァ<EFBFBD>激<EFBFBD>壻スソ逕ィ蛻<EFBFBD>ク<EFBFBD>シ冗シ灘ュ假シ<EFBFBD>edis<EFBFBD>?- 笨?髯堺ス拶PI謌先悽<EFBFBD>哭LM扈捺棡郛灘ュ俶戟荵<EFBFBD>喧<EFBFBD>碁∩蜈埼㍾螟崎ー<EFBFBD>畑
- 笨?**莉サ蜉。謖∽ケ<E288BD><EFBDB9>?<EFBFBD>夐柄譌カ髣エ莉サ蜉。荳榊屏螳樔セ矩㍾蜷ッ閠御ク「螟?- 笨?**謾ッ謖∝、壼ョ樔セ?<EFBFBD>夂シ灘ュ伜惠螟壼ョ樔セ矩龍蜈ア莠ォ
- 笨?蟷ウ貊題ソ<EFBFBD>ク。<EFBFBD>壻ソ晉蕗髯咲コァ譁ケ譯茨シ檎。ョ菫晉ウサ扈溽ィウ螳<EFBFBD>
2. 蠖灘燕邉サ扈溽憾諤∝<E8ABA4>譫?
2.1 蟾イ菴ソ逕ィ郛灘ュ倡噪菴咲スョ
菴咲スョ1<EFBFBD>唏ealthCheckService.ts
// 譁<>サカ<EFBDBB>喘ackend/src/modules/dc/tool-b/services/HealthCheckService.ts
// 隨?7陦鯉シ夊ッサ蜿也シ灘ュ<E78198>
const cached = await cache.get<HealthCheckResult>(cacheKey);
// 隨?45陦鯉シ壼<EFBDBC>蜈・郛灘ュ<E78198>
await cache.set(cacheKey, result, 86400); // 24蟆乗慮
**逕ィ騾?<EFBFBD>哘xcel蛛・蠎キ譽譟・扈捺棡郛灘ュ? **驥崎ヲ∵?<EFBFBD>夸沺?荳ュ遲会シ磯∩蜈埼㍾螟崎ァ」譫職xcel<65>? **謨ー謐ョ驥?*<2A>嘸5KB/鬘?
菴咲スョ2<EFBFBD>哭LM12FieldsService.ts
// 譁<>サカ<EFBDBB>喘ackend/src/modules/asl/common/llm/LLM12FieldsService.ts
// 隨?16陦鯉シ夊ッサ蜿也シ灘ュ<E78198>
const cached = await cache.get(cacheKey);
// 隨?30陦鯉シ壼<EFBDBC>蜈・郛灘ュ<E78198>
await cache.set(cacheKey, JSON.stringify(result), 3600); // 1蟆乗慮
**逕ィ騾?<EFBFBD>哭LM 12蟄玲ョオ謠仙叙扈捺棡郛灘ュ<E78198>
**驥崎ヲ∵?<EFBFBD>夸沐?鬮假シ育峩謗・蠖ア蜩喉PI謌先悽<E58588>?
**謨ー謐ョ驥?*<2A>嘸50KB/鬘?
謌先悽蠖ア蜩<EFBFBD><EFBFBD>?```
蜊墓ャ。謠仙叙謌先悽<E58588>嘸ツ・0.43/遽?螯よ棡郛灘ュ伜、ア謨茨シ碁㍾螟崎ー<E5B48E>畑<EFBFBD><E79591>
- 10谺?= ツ・4.3
- 100谺?= ツ・43
- 1000谺?= ツ・430
---
### 2.2 髟ソ譌カ髣エ蠑よュ・莉サ蜉?
#### **ASL讓。蝮暦シ壽枚迪ョ遲幃我ササ蜉?*
```typescript
// 譁<>サカ<EFBDBB>喘ackend/src/modules/asl/services/screeningService.ts
// 隨?3-65陦?processLiteraturesInBackground(task.id, projectId, literatures);
髣ョ鬚<EFBFBD><EFBFBD>?- 199遽<39>枚迪ョ髴隕?33-66蛻<36>帖
- 蠖灘燕菴ソ逕ィ蜀<EFBFBD>ュ倬弌蛻暦シ<EFBFBD>emoryQueue<EFBFBD>?- SAE螳樔セ矩㍾蜷ッ/郛ゥ螳ケ譌カ莉サ蜉。荳「螟? 蠖ア蜩<EFBFBD><EFBFBD>?- 逕ィ謌キ菴馴ェ梧栫蟾ョ<E89FBE>井ササ蜉。遯∫┯豸亥、ア<EFBDA4><EFBDB1>
- 蟾イ螟<EFBFBD>炊扈捺棡荳「螟ア<EFBFBD>梧オェ雍ケAPI雍ケ逕ィ
- 譌<EFBFBD>豕戊ソス貅ッ莉サ蜉。迥カ諤?
2.3 蠖灘燕譫カ譫<EFBDB6><E8ADAB>鄂ョ
# backend/.env
CACHE_TYPE=memory # 竊?髴隕∵隼荳?redis
QUEUE_TYPE=memory # 竊?髴隕∵隼荳?redis
3. Redis驟咲スョ菫。諱ッ
3.1 髦ソ驥御コ然edis雍ュ荵ー菫。諱ッ
| 驟咲スョ鬘? | 蛟? | 隸エ譏<EFBFBD> |
|---|---|---|
| 莠ァ蜩<EFBFBD> | Redis 蠑貅千沿 | 螳梧紛Redis蜉溯<EFBFBD> |
| 莉倩エケ譁ケ蠑<EFBFBD> | 蛹<EFBFBD>ケエ蛹<EFBFBD>怦 | 鬥匁ャ。雍ュ荵ー莠?謚倅シ俶<EFBDBC>? |
| 驛ィ鄂イ讓。蠑<EFBFBD> | 莠大次逕滂シ磯ォ伜庄逕ィ<EFBFBD><EFBFBD> | 荳サ莉手<EFBFBD>蜉ィ蛻<EFBFBD>困 |
| 邉サ蛻<EFBFBD> | 譬<EFBFBD>㊥迚? | 貊。雜ウ髴豎? |
| 蝨ー蝓<EFBFBD> | 蜊主圏2<EFBFBD>亥圏莠ャ<EFBFBD><EFBFBD> | 荳惨AE蜷悟慍蝓? |
| 螳樔セ狗アサ蝙<EFBFBD> | 鬮伜庄逕? | 笨?99.95%蜿ッ逕ィ諤? |
| *螟ァ迚域<EFBFBD>? | Redis 7.0 | 譛譁ー遞ウ螳夂沿 |
| 譫カ譫<EFBFBD>アサ蝙<EFBFBD> | 荳榊星逕ィ髮<EFBFBD>セ、<EFBFBD>亥黒闃らせ<EFBFBD><EFBFBD> | 貊。雜ウ蠖灘燕隗<EFBFBD>ィ。 |
| 蛻<EFBFBD>援隗<EFBFBD><EFBFBD>シ | 256 MB | 蛻晄悄雜ウ螟<EFBFBD> |
| 蛻<EFBFBD>援謨ー驥<EFBFBD> | 1 | 蜊募<EFBFBD>迚? |
| 隸サ蜀吝<EFBFBD>遖サ | 蜈ウ髣ュ | 邂蛹夜<EFBFBD>鄂? |
3.2 鬚<>シー謌先悽
蝓コ遑莉キ譬シ<EFBFBD>堋?2/蟷エ<E89FB7>亥黒譛コ迚茨シ<E88CA8>
鬮伜庄逕ィ迚茨シ堋?80/蟷エ<E89FB7>井シー邂暦シ?鬥匁ャ。雍ュ荵ー<E88DB5>堋?08/蟷エ<E89FB7><EFBDB4>6謚伜錘<E4BC9C>?
蟇ケ豈疲噺逶奇シ?- 闃ら怐LLM API雍ケ逕ィ<E98095>?ツ・500/蟷?- 謠仙合逕ィ謌キ貊。諢丞コヲ<EFBDBA>壽裏莉キ
- ROI<4F>?400%
3.3 霑樊磁菫。諱ッ<E8ABB1>郁エュ荵ー蜷手執蜿厄シ?
# 髦ソ驥御コ第而蛻カ蜿ー 竊?Redis螳樔セ<E6A894> 竊?霑樊磁菫。諱ッ
REDIS_HOST=r-xxxxxxxxxxxx.redis.rds.aliyuncs.com
REDIS_PORT=6379
REDIS_PASSWORD=your_secure_password_here
REDIS_DB=0
# 謌紋スソ逕ィ霑樊磁蟄礼ャヲ荳イ
REDIS_URL=redis://:your_password@r-xxxxxxxxxxxx.redis.rds.aliyuncs.com:6379/0
4. 謾ケ騾<EFBDB9>隸ヲ扈<EFBDA6>ュ・鬪?
4.1 Phase 1<>壽悽蝨ー蠑蜿醍識蠅<E8AD98>㊥螟?笨?
*豁・鬪、1.1<EFBFBD>壼ョ芽」<EFBFBD>セ晁オ?
cd backend
npm install ioredis --save
npm install @types/ioredis --save-dev
豁・鬪、1.2<EFBFBD>夐<EFBFBD>鄂ョ譛ャ蝨ーRedis
# 遑ョ隶、Docker Redis豁」蝨ィ霑占。<E58DA0>
docker ps | findstr redis
# 螯よ棡豐。譛芽ソ占。鯉シ悟星蜉ィ螳<EFBDA8>
docker start ai-clinical-redis
# 豬玖ッ戊ソ樊磁
docker exec -it ai-clinical-redis redis-cli ping
# 蠎碑ッ・霑泌屓<E6B38C>啀ONG
豁・鬪、1.3<EFBFBD>壽峩譁ー譛ャ蝨?env
# backend/.env
DATABASE_URL=postgresql://postgres:postgres123@localhost:5432/ai_clinical_research
# ==================== Redis驟咲スョ ====================
# 蜷ッ逕ィRedis郛灘ュ<E78198>
CACHE_TYPE=redis
REDIS_HOST=localhost
REDIS_PORT=6379
REDIS_DB=0
# REDIS_PASSWORD= # 譛ャ蝨ー譌<EFBDB0>蟇<EFBFBD><E89F87>?
# 髦溷<E9ABA6>證よ慮逕ィ蜀<EFBDA8>ュ假シ亥<EFBDBC>髦カ谿オ蜷ッ逕ィ<E98095><EFBDA8>
QUEUE_TYPE=memory
# ==================== JWT ====================
JWT_SECRET=your-secret-key-change-in-production
JWT_EXPIRES_IN=7d
# ==================== LLM API ====================
DEEPSEEK_API_KEY=sk-7f8cc37a79fa4799860b38fc7ba2e150
DASHSCOPE_API_KEY=sk-75b4ff29a14a49e79667a331034f3298
# ==================== Dify ====================
DIFY_API_URL=http://localhost/v1
DIFY_API_KEY=dataset-mfvdiKvQ2l3NvxWm7RoYMN3c
# ==================== Server ====================
PORT=3001
NODE_ENV=development
# ==================== CloseAI驟咲スョ ====================
CLOSEAI_API_KEY=sk-cu0iepbXYGGx2jc7BqP6ogtSWmP6fk918qV3RUdtGC3Ed1po
CLOSEAI_OPENAI_BASE_URL=https://api.openai-proxy.org/v1
CLOSEAI_CLAUDE_BASE_URL=https://api.openai-proxy.org/anthropic
# ==================== 蟄伜お驟咲スョ ====================
STORAGE_TYPE=local
LOCAL_STORAGE_DIR=uploads
LOCAL_STORAGE_URL=http://localhost:3001/uploads
# ==================== CORS驟咲スョ ====================
CORS_ORIGIN=http://localhost:5173
# ==================== 譌・蠢鈴<E8A0A2>鄂ョ ====================
LOG_LEVEL=debug
4.2 Phase 2<>壼ョ樒鴫RedisCacheAdapter 笨?
豁・鬪、2.1<EFBFBD>壻ソョ謾ケRedisCacheAdapter.ts
// 譁<>サカ<EFBDBB>喘ackend/src/common/cache/RedisCacheAdapter.ts
import Redis from 'ioredis';
import type { CacheAdapter } from './CacheAdapter.js';
import { logger } from '../logging/index.js';
/**
* Redis郛灘ュ倬る<C280>蝎? *
* 菴ソ逕ィioredis螳「謌キ遶ッ<E981B6>梧髪謖<E9ABAA>シ? * - 蟄礼ャヲ荳?蟇ケ雎。閾ェ蜉ィ蠎丞<E8A08E>蛹? * - TTL霑<4C>悄譌カ髣エ
* - 霑樊磁豎<E7A381>邂。逅? * - 髞呵ッッ驥崎ッ<E5B48E>
*
* @example
* const cache = new RedisCacheAdapter({
* host: 'localhost',
* port: 6379,
* password: 'xxx',
* db: 0
* });
*
* await cache.set('key', { data: 'value' }, 60);
* const value = await cache.get('key');
*/
export class RedisCacheAdapter implements CacheAdapter {
private redis: Redis;
constructor(options?: {
host?: string;
port?: number;
password?: string;
db?: number;
}) {
this.redis = new Redis({
host: options?.host || 'localhost',
port: options?.port || 6379,
password: options?.password || undefined,
db: options?.db || 0,
// 霑樊磁驟咲スョ
retryStrategy: (times) => {
const delay = Math.min(times * 50, 2000);
logger.warn(`Redis霑樊磁驥崎ッ<E5B48E> ${times} 谺。<E8B0BA><EFBDA1>${delay}ms蜷朱㍾隸描);
return delay;
},
maxRetriesPerRequest: 3,
enableReadyCheck: true,
// 霑樊磁豎<E7A381>驟咲ス? lazyConnect: false,
keepAlive: 30000,
});
// 逶大成霑樊磁莠倶サカ
this.redis.on('connect', () => {
logger.info('Redis霑樊磁謌仙粥');
});
this.redis.on('error', (error) => {
logger.error('Redis霑樊磁髞呵ッッ', { error: error.message });
});
this.redis.on('close', () => {
logger.warn('Redis霑樊磁蜈ウ髣ュ');
});
this.redis.on('reconnecting', () => {
logger.warn('Redis豁」蝨ィ驥崎ソ<E5B48E>...');
});
}
/**
* 闔キ蜿也シ灘ュ伜? */
async get<T>(key: string): Promise<T | null> {
try {
const value = await this.redis.get(key);
if (!value) {
logger.debug('Redis郛灘ュ俶悴蜻ス荳?, { key });
return null;
}
logger.debug('Redis郛灘ュ伜多荳ュ', { key, size: value.length });
// 蟆晁ッ戊ァ」譫辱SON
try {
return JSON.parse(value) as T;
} catch {
// 螯よ棡荳肴弍JSON<4F>瑚ソ泌屓蜴溷ァ句ュ礼ャヲ荳イ
return value as unknown as T;
}
} catch (error) {
logger.error('Redis GET螟ア雍・', { key, error });
return null; // 髯咲コァ<EFBDBA>夊ソ泌屓null閠御ク肴弍謚帛シょクク
}
}
/**
* 隶セ鄂ョ郛灘ュ伜? * @param ttl 霑<>悄譌カ髣エ<E9ABA3>育ァ抵シ会シ御ク堺シ<E5A0BA>蛻呎ーク荳崎ソ<E5B48E>悄<EFBFBD>井ク肴耳闕撰シ<E692B0>
*/
async set<T>(key: string, value: T, ttl?: number): Promise<void> {
try {
// 蠎丞<E8A08E>蛹門? const serialized = typeof value === 'string'
? value
: JSON.stringify(value);
// 隶セ鄂ョ蛟シ<E89B9F>亥クヲTTL<54>? if (ttl) {
await this.redis.setex(key, ttl, serialized);
logger.debug('Redis SET謌仙粥<E4BB99>亥クヲTTL<54>?, {
key,
ttl,
size: serialized.length
});
} else {
await this.redis.set(key, serialized);
logger.warn('Redis SET謌仙粥<E4BB99>域裏TTL<54>?, { key }); // 隴ヲ蜻奇シ壽裏霑<E8A38F>悄譌カ髣エ
}
} catch (error) {
logger.error('Redis SET螟ア雍・', { key, ttl, error });
// 荳肴鴨蜃コ蠑ょクク<EFBDB8>悟<EFBFBD>隶ク邉サ扈溽サァ扈ュ霑占。<E58DA0>
}
}
/**
* 蛻<>髯、郛灘ュ<E78198>
*/
async delete(key: string): Promise<boolean> {
try {
const result = await this.redis.del(key);
logger.debug('Redis DEL', { key, deleted: result > 0 });
return result > 0;
} catch (error) {
logger.error('Redis DEL螟ア雍・', { key, error });
return false;
}
}
/**
* 譽譟・key譏ッ蜷ヲ蟄伜惠
*/
async has(key: string): Promise<boolean> {
try {
const result = await this.redis.exists(key);
return result > 0;
} catch (error) {
logger.error('Redis EXISTS螟ア雍・', { key, error });
return false;
}
}
/**
* 貂<>ゥコ謇譛臥シ灘ュ假シ亥些髯ゥ謫堺ス懶シ<E687B6>シ<EFBFBD>
*/
async clear(): Promise<void> {
try {
await this.redis.flushdb();
logger.warn('Redis FLUSHDB謇ァ陦鯉シ域園譛臥シ灘ュ伜キイ貂<EFBDB2>ゥコ<EFBDA9>?);
} catch (error) {
logger.error('Redis FLUSHDB螟ア雍・', { error });
}
}
/**
* 豬玖ッ紐edis霑樊磁
*/
async ping(): Promise<boolean> {
try {
const result = await this.redis.ping();
return result === 'PONG';
} catch (error) {
logger.error('Redis PING螟ア雍・', { error });
return false;
}
}
/**
* 蜈ウ髣ュ霑樊磁<E6A88A>育畑莠惹シ倬寉蜈ウ髣ュ<E9ABA3><EFBDAD>
*/
async disconnect(): Promise<void> {
try {
await this.redis.quit();
logger.info('Redis霑樊磁蟾イ蜈ウ髣?);
} catch (error) {
logger.error('Redis蜈ウ髣ュ霑樊磁螟ア雍・', { error });
}
}
}
豁・鬪、2.2<EFBFBD>壽峩譁ーCacheFactory.ts<74>域キサ蜉<EFBDBB>髯咲コァ遲也払<E4B99F><E68995>
// 譁<>サカ<EFBDBB>喘ackend/src/common/cache/CacheFactory.ts
import { config } from '../../config/env.js';
import type { CacheAdapter } from './CacheAdapter.js';
import { MemoryCacheAdapter } from './MemoryCacheAdapter.js';
import { RedisCacheAdapter } from './RedisCacheAdapter.js';
import { logger } from '../logging/index.js';
/**
* 郛灘ュ伜キ・蜴ゑシ亥黒萓具シ<E585B7>
*
* 譬ケ謐ョ邇ッ蠅<EFBDAF>序驥剰<E9A9A5>蜉ィ騾画叫郛灘ュ伜ョ樒鴫<E6A892>? * - CACHE_TYPE=memory 竊?MemoryCacheAdapter
* - CACHE_TYPE=redis 竊?RedisCacheAdapter<65>域髪謖<E9ABAA>剄郤ァ<E983A4><EFBDA7>
*
* @example
* import { cache } from '@/common/cache'
* await cache.set('user:123', userData, 60)
* const user = await cache.get<User>('user:123')
*/
export class CacheFactory {
private static instance: CacheAdapter | null = null;
private static fallbackToMemory = false; // 髯咲コァ譬<EFBDA7>ョー
/**
* 闔キ蜿也シ灘ュ伜ョ樔セ具シ亥黒萓具シ<E585B7>
*/
static getInstance(): CacheAdapter {
if (!this.instance) {
this.instance = this.createCache();
}
return this.instance;
}
/**
* 蛻帛サコ郛灘ュ伜ョ樔セ<E6A894>
*/
private static createCache(): CacheAdapter {
const cacheType = config.cacheType || 'memory';
logger.info('[CacheFactory] 蛻晏ァ句喧郛灘ュ倡ウサ扈?, { cacheType });
switch (cacheType) {
case 'redis':
return this.createRedisCache();
case 'memory':
default:
return this.createMemoryCache();
}
}
/**
* 蛻帛サコRedis郛灘ュ假シ亥クヲ髯咲コァ遲也払<E4B99F>? */
private static createRedisCache(): CacheAdapter {
try {
logger.info('[CacheFactory] 豁」蝨ィ霑樊磁Redis...', {
host: config.redisHost,
port: config.redisPort,
db: config.redisDb,
});
const redisCache = new RedisCacheAdapter({
host: config.redisHost,
port: config.redisPort,
password: config.redisPassword,
db: config.redisDb,
});
// 豬玖ッ戊ソ樊磁<E6A88A>亥酔豁・遲牙セ<E78999>シ<EFBFBD>
redisCache.ping().then((isConnected) => {
if (isConnected) {
logger.info('[CacheFactory] 笨?Redis霑樊磁謌仙粥');
} else {
logger.error('[CacheFactory] 笶?Redis霑樊磁螟ア雍・<EFBFBD>悟キイ髯咲コァ蛻ー蜀<EFBFBD>ュ倡シ灘ュ?);
this.fallbackToMemory = true;
}
}).catch((error) => {
logger.error('[CacheFactory] 笶?Redis霑樊磁蠑ょクク<EFBDB8>悟キイ髯咲コァ蛻ー蜀<EFBDB0>ュ倡シ灘ュ?, { error });
this.fallbackToMemory = true;
});
return redisCache;
} catch (error) {
logger.error('[CacheFactory] 笶?Redis蛻晏ァ句喧螟ア雍・<EFBFBD>碁剄郤ァ蛻ー蜀<EFBFBD>ュ倡シ灘ュ?, { error });
this.fallbackToMemory = true;
return this.createMemoryCache();
}
}
/**
* 蛻帛サコ蜀<EFBDBA>ュ倡シ灘ュ<E78198>
*/
private static createMemoryCache(): MemoryCacheAdapter {
logger.info('[CacheFactory] 菴ソ逕ィ蜀<EFBDA8>ュ倡シ灘ュ<E78198>');
return new MemoryCacheAdapter();
}
/**
* 譽譟・譏ッ蜷ヲ蟾イ髯咲コァ蛻ー蜀<EFBDB0>ュ倡シ灘ュ? */
static isFallbackMode(): boolean {
return this.fallbackToMemory;
}
}
/**
* 蟇シ蜃コ蜊穂セ<E7A982>
*/
export const cache = CacheFactory.getInstance();
豁・鬪、2.3<EFBFBD>壽峩譁ーenv.ts驟咲スョ
// 譁<>サカ<EFBDBB>喘ackend/src/config/env.ts
// 遑ョ菫抒edis驟咲スョ豁」遑ョ隸サ蜿<EFBDBB>
export const config = {
// ... 蜈カ莉夜<E88E89>鄂ョ ...
/** 郛灘ュ倡アサ蝙<EFBDBB> */
cacheType: process.env.CACHE_TYPE || 'memory',
/** Redis驟咲スョ */
redisHost: process.env.REDIS_HOST || 'localhost',
redisPort: parseInt(process.env.REDIS_PORT || '6379', 10),
redisPassword: process.env.REDIS_PASSWORD || undefined,
redisDb: parseInt(process.env.REDIS_DB || '0', 10),
redisUrl: process.env.REDIS_URL || 'redis://localhost:6379',
/** 髦溷<E9ABA6>邀サ蝙<EFBDBB> */
queueType: process.env.QUEUE_TYPE || 'memory',
// ... 蜈カ莉夜<E88E89>鄂ョ ...
};
// 鬪瑚ッ<E7919A><EFBDAF>鄂ョ
export function validateConfig() {
console.log('笨?[Config] 邇ッ蠅<EFBDAF>序驥丞刈霓ス謌仙粥');
console.log('[Config] 蠎皮畑驟咲スョ:');
console.log(` - 郛灘ュ<E78198>: ${config.cacheType}`);
console.log(` - 髦溷<E9ABA6>: ${config.queueType}`);
if (config.cacheType === 'redis') {
console.log(` - Redis: ${config.redisHost}:${config.redisPort}/${config.redisDb}`);
}
}
4.3 Phase 3<>壽悽蝨ー豬玖ッ?笨?
豁・鬪、3.1<EFBFBD>壼<EFBFBD>蟒コRedis豬玖ッ戊<EFBFBD>譛ャ
// 譁<>サカ<EFBDBB>喘ackend/src/scripts/test-redis.ts
import { cache } from '../common/cache/index.js';
import { logger } from '../common/logging/index.js';
async function testRedis() {
console.log('\n<>ァェ 蠑蟋区オ玖ッ紐edis郛灘ュ<E78198>...\n');
try {
// 豬玖ッ<E78E96>1<EFBFBD>壼渕譛ャ隸サ蜀? console.log('<27>統 豬玖ッ<E78E96>1<EFBFBD>壼渕譛ャ隸サ蜀?);
await cache.set('test:hello', 'world', 10);
const value1 = await cache.get('test:hello');
console.log(` 笨?蜀吝<E89C80>: "hello" 竊?"world"`);
console.log(` 笨?隸サ蜿<EFBDBB>: "${value1}"`);
console.assert(value1 === 'world', '蛟シ蠎碑ッ・蛹ケ驟?);
// 豬玖ッ<E78E96>2<EFBFBD>壼ッケ雎。蠎丞<E8A08E>蛹<EFBFBD>
console.log('\n<EFBFBD>統 豬玖ッ<EFBFBD>2<EFBFBD>壼ッケ雎。蠎丞<EFBFBD>蛹<EFBFBD>');
const obj = { id: 123, name: '豬玖ッ<EFBFBD>', data: [1, 2, 3] };
await cache.set('test:object', obj, 10);
const value2 = await cache.get<typeof obj>('test:object');
console.log(` 笨?蜀吝<E89C80>蟇ケ雎。:`, obj);
console.log(` 笨?隸サ蜿門ッケ雎。:`, value2);
console.assert(value2?.id === 123, 'ID蠎碑ッ・蛹ケ驟<EFBFBD>');
// 豬玖ッ<E78E96>3<EFBFBD>啜TL霑<4C>悄
console.log('\n<EFBFBD>統 豬玖ッ<EFBFBD>3<EFBFBD>啜TL霑<EFBFBD>悄<EFBFBD>?遘抵シ<EFBFBD>');
await cache.set('test:expire', 'will-expire', 2);
console.log(` 笨?蜀吝<E89C80><E5909D><EFBFBD>TL=2遘抵シ荏);
console.log(` 竢?遲牙セ<E78999>1遘?..`);
await sleep(1000);
const value3a = await cache.get('test:expire');
console.log(` 笨?1遘貞錘隸サ蜿<EFBDBB>: "${value3a}"`);
console.assert(value3a === 'will-expire', '蠎碑ッ・霑伜ュ伜<EFBFBD>?);
console.log(` 竢?遲牙セ<E78999>2遘?..`);
await sleep(2000);
const value3b = await cache.get('test:expire');
console.log(` 笨?3遘貞錘隸サ蜿<EFBDBB>: ${value3b}`);
console.assert(value3b === null, '蠎碑ッ・蟾イ霑<EFBDB2><E99C91>?);
// 豬玖ッ<E78E96>4<EFBFBD>喇as蜥慧elete
console.log('\n<EFBFBD>統 豬玖ッ<EFBFBD>4<EFBFBD>喇as蜥慧elete');
await cache.set('test:delete', 'to-be-deleted', 10);
const exists1 = await cache.has('test:delete');
console.log(` 笨?蜀吝<E89C80>蜷仔xists: ${exists1}`);
await cache.delete('test:delete');
const exists2 = await cache.has('test:delete');
console.log(` 笨?蛻<>髯、蜷仔xists: ${exists2}`);
console.assert(!exists2, '蠎碑ッ・荳榊ュ伜<EFBFBD>?);
// 豬玖ッ<E78E96>5<EFBFBD>壼、ァ蟇ケ雎。<E99B8E>?0KB<4B>? console.log('\n<>統 豬玖ッ<E78E96>5<EFBFBD>壼、ァ蟇ケ雎。郛灘ュ假シ域ィ。諡櫚LM扈捺棡<E68DBA>?);
const bigObj = {
literatureId: 'xxx',
fields: {
遐皮ゥカ邀サ蝙<EFBFBD>: 'RCT',
譬キ譛ャ驥? '500',
蟷イ鬚<EFBFBD>蒔譁ス: '闕ッ迚ゥA 100mg',
// ... 12荳ェ蟄玲ョ? },
metadata: {
model: 'deepseek-v3',
tokens: 8000,
timestamp: Date.now(),
},
rawOutput: 'x'.repeat(40000), // 讓。諡溷、ァ霎灘<E99C8E>? };
const start = Date.now();
await cache.set('test:bigobj', bigObj, 3600);
const value5 = await cache.get('test:bigobj');
const duration = Date.now() - start;
const size = JSON.stringify(bigObj).length;
console.log(` 笨?蜀吝<E89C80>+隸サ蜿門、ァ蟇ケ雎? ${size} bytes<65>瑚玲慮 ${duration}ms`);
console.assert(value5 !== null, '蠎碑ッ・閭ス隸サ蜿?);
console.log('\n笨?謇譛画オ玖ッ暮夊ソ<EFBFBD>シ―n');
process.exit(0);
} catch (error) {
console.error('\n笶?豬玖ッ募、ア雍・:', error);
process.exit(1);
}
}
function sleep(ms: number): Promise<void> {
return new Promise((resolve) => setTimeout(resolve, ms));
}
// 霑占。梧オ玖ッ<E78E96>
testRedis();
*豁・鬪、3.2<EFBFBD>壽鴬陦梧オ玖ッ?
cd backend
# 1. 蜷ッ蜉ィ蜷守ォッ<EFBDAB>育。ョ菫抒edis驟咲スョ逕滓譜<E6BB93>?npm run dev
# 2. 譁ー蠑荳荳ェ扈育ォッ<EFBDAB>瑚ソ占。梧オ玖ッ戊<EFBDAF>譛ャ
npx tsx src/scripts/test-redis.ts
鬚<EFBFBD>悄霎灘<EFBFBD><EFBFBD>?``` <0A>ァェ 蠑蟋区オ玖ッ紐edis郛灘ュ<E78198>...
<EFBFBD>統 豬玖ッ<E78E96>1<EFBFBD>壼渕譛ャ隸サ蜀? 笨?蜀吝<E89C80>: "hello" 竊?"world" 笨?隸サ蜿<EFBDBB>: "world"
<EFBFBD>統 豬玖ッ<E78E96>2<EFBFBD>壼ッケ雎。蠎丞<E8A08E>蛹<EFBFBD> 笨?蜀吝<E89C80>蟇ケ雎。: { id: 123, name: '豬玖ッ<E78E96>', data: [ 1, 2, 3 ] } 笨?隸サ蜿門ッケ雎。: { id: 123, name: '豬玖ッ<E78E96>', data: [ 1, 2, 3 ] }
<EFBFBD>統 豬玖ッ<E78E96>3<EFBFBD>啜TL霑<4C>悄<EFBFBD>?遘抵シ<E68AB5> 笨?蜀吝<E89C80><E5909D><EFBFBD>TL=2遘抵シ<E68AB5> 竢?遲牙セ<E78999>1遘?.. 笨?1遘貞錘隸サ蜿<EFBDBB>: "will-expire" 竢?遲牙セ<E78999>2遘?.. 笨?3遘貞錘隸サ蜿<EFBDBB>: null
<EFBFBD>統 豬玖ッ<E78E96>4<EFBFBD>喇as蜥慧elete 笨?蜀吝<E89C80>蜷仔xists: true 笨?蛻<>髯、蜷仔xists: false
<EFBFBD>統 豬玖ッ<E78E96>5<EFBFBD>壼、ァ蟇ケ雎。郛灘ュ假シ域ィ。諡櫚LM扈捺棡<E68DBA>? 笨?蜀吝<E89C80>+隸サ蜿門、ァ蟇ケ雎? 40123 bytes<65>瑚玲慮 5ms
笨?謇譛画オ玖ッ暮夊ソ<E5A48A>シ?```
*豁・鬪、3.3<EFBFBD>壽オ玖ッ穂ク壼苅莉」遐?
# 1. 豬玖ッ菱ealthCheckService<63><65>C讓。蝮暦シ?# 荳贋シ<E8B48B>荳荳ェExcel譁<6C>サカ<EFBDBB>梧衍逵区律蠢暦シ<E69AA6>
[HealthCheck] Cache miss, processing file
[HealthCheck] Check completed
[HealthCheck] Cache SET: health:xxx, TTL=86400
# 隨ャ莠梧ャ。荳贋シ<E8B48B>蜷御ク荳ェ譁<EFBDAA>サ?[HealthCheck] Cache hit 竊?謌仙粥莉山edis隸サ蜿<EFBDBB>
# 2. 豬玖ッ畢LM12FieldsService<63><65>SL讓。蝮暦シ?# 謠蝉コ、蜈ィ譁<EFBDA8>、咲ュ帑ササ蜉。<E89C89>梧衍逵区律蠢暦シ<E69AA6>
[LLM12FieldsService] 隹<>畑LLM謠仙叙12蟄玲ョオ
[LLM12FieldsService] Result cached with key: fulltext:xxx
[LLM12FieldsService] 郛灘ュ伜<EFBDAD>蜈・謌仙粥
# 驥肴眠霑占。悟酔荳遽⑰DF
[LLM12FieldsService] Cache hit, returning cached result 竊?闃ら怐API雍ケ逕ィ<E98095>?```
---
### 4.4 Phase 4<>夐仭驥御コ然edis驟咲スョ 笨?
#### **豁・鬪、4.1<EFBFBD>夊エュ荵ーRedis螳樔セ<EFBFBD>**
1. 逋サ蠖暮仭驥御コ第而蛻カ蜿ー
2. 霑帛<E99C91> **莠第焚謐ョ蠎<EFBDAE> Redis** 莠ァ蜩<EFBDA7>。?3. 轤ケ蜃サ **蛻帛サコ螳樔セ<E6A894>**
4. 謖臥<E8AC96>謌ェ蝗セ驟咲スョ騾画叫<E794BB>? - 莠ァ蜩<EFBDA7>シ啌edis 蠑貅千沿
- 驛ィ鄂イ讓。蠑擾シ壻コ大次逕滂シ磯ォ伜庄逕ィ<E98095>? - 蝨ー蝓滂シ壼克蛹?<3F>亥圏莠ャ<E88EA0>俄披?**荳惨AE蜷悟慍蝓滂シ<E6BB82>**
- 迚域悽<E59F9F>啌edis 7.0
- 蛻<>援隗<E68FB4><E99A97>シ<EFBFBD>?56 MB
- 莉倩エケ譁ケ蠑擾シ壼桁蟷エ蛹<EFBDB4><E89BB9>?5. 謠蝉コ、隶「蜊募ケカ謾ッ莉?
#### **豁・鬪、4.2<EFBFBD>夐<EFBFBD>鄂ョ逋ス蜷榊黒**
髦ソ驥御コ第而蛻カ蜿ー 竊?Redis螳樔セ<E6A894> 竊?逋ス蜷榊黒隶セ鄂? 豺サ蜉<EFBDBB><E89C89>?1. 譛ャ蝨ー蠑蜿選P<E981B8>育畑莠取悽蝨ー豬玖ッ包シ<E58C85>
- 菴<EFBFBD>逧<EFBFBD><EFBFBD>鄂選P/32
- SAE蠎皮畑IP<EFBFBD>育函莠ァ邇ッ蠅<EFBFBD>シ<EFBFBD>
- 0.0.0.0/0 <20>井クエ譌カ<E8AD8C>悟錘扈ュ謾ケ荳コSAE VPC<50>? 謌? - SAE螳樔セ狗噪VPC鄂第ョオ
#### **豁・鬪、4.3<EFBFBD>夊執蜿冶ソ樊磁菫。諱?*
髦ソ驥御コ第而蛻カ蜿ー 竊?Redis螳樔セ<E6A894> 竊?霑樊磁菫。諱ッ
螟榊宛莉・荳倶ソ。諱ッ<EFBFBD>?- 霑樊磁蝨ー蝮<E89DAE>嗷-xxxxxxxxxxxx.redis.rds.aliyuncs.com
- 遶ッ蜿」<EFBFBD>?379
- 螳樔セ紀D<EFBFBD>嗷-xxxxxxxxxxxx
- 蟇<EFBFBD><EFBFBD><EFBFBD>シ夂せ蜃?菫ョ謾ケ蟇<EFBDB9><E89F87><EFBFBD>"隶セ鄂ョ
#### **豁・鬪、4.4<EFBFBD>壽峩譁ーSAE邇ッ蠅<EFBFBD>序驥<EFBFBD>**
髦ソ驥御コ第而蛻カ蜿ー 竊?SAE蠎皮畑 竊?驟咲スョ邂。逅<EFBDA1> 竊?邇ッ蠅<EFBDAF>序驥<E5BA8F>
豺サ蜉<EFBFBD><EFBFBD>?CACHE_TYPE=redis REDIS_HOST=r-xxxxxxxxxxxx.redis.rds.aliyuncs.com REDIS_PORT=6379 REDIS_PASSWORD=菴<>隶セ鄂ョ逧<EFBDAE>ッ<EFBFBD><EFBDAF><EFBFBD> REDIS_DB=0
---
### 4.5 Phase 5<>壼星逕ィRedis髦溷<E9ABA6> <20>閥 **蠢<>。サ螳樊命**
> **驥崎ヲ∝序譖エ**<2A>夂サ丞<EFBDBB>譫撰シ軍edis髦溷<E9ABA6>蟇ケASL蜥轡C Tool B讓。蝮玲<E89DAE>?*蠢<>。サ逧?*<2A>御ク肴弍蜿ッ騾臥噪<E887A5>?
> **逅<>罰**<2A>?蟆乗慮髟ソ莉サ蜉。蝨ィSAE邇ッ蠅<EFBDAF>ク倶ク咲畑Redis髦溷<E9ABA6>螟ア雍・邇?> 95%
#### **豁・鬪、5.1<EFBFBD>壼ョ芽」<EFBFBD>ullMQ**
```bash
cd backend
npm install bullmq --save
# BullMQ蟾イ蝨ィpackage.json荳ュ<E88DB3>悟宵髴遑ョ隶、螳芽」<E88ABD>
npm list bullmq
# 蠎碑ッ・譏セ遉コ<E98189>喘ullmq@5.65.0
豁・鬪、5.2<EFBFBD>壼ョ樒鴫RedisQueue.ts
// 譁<>サカ<EFBDBB>喘ackend/src/common/jobs/RedisQueue.ts
import { Queue, Worker, Job as BullJob, QueueEvents } from 'bullmq';
import type { Job, JobQueue, JobHandler } from './types.js';
import { logger } from '../logging/index.js';
import { config } from '../../config/env.js';
/**
* Redis髦溷<E9ABA6>螳樒鴫<E6A892>亥渕莠撮ullMQ<4D>? *
* 譬ク蠢<EFBDB8>粥閭ス<E996AD>? * - 莉サ蜉。謖∽ケ<E288BD>喧<EFBFBD>亥ョ樔セ矩㍾蜷ッ荳堺ク「螟ア<E89E9F><EFBDB1>
* - 閾ェ蜉ィ驥崎ッ包シ亥、ア雍・蜷取欠謨ー騾驕ソ<E9A995><EFBDBF>
* - 蛻<>ク<EFBFBD>シ丈ササ蜉。蛻<EFBDA1><E89BBB><EFBFBD>亥、壼ョ樔セ句刻隹<E588BB>シ<EFBFBD>
* - 霑帛コヲ霍溯クェ
*/
export class RedisQueue implements JobQueue {
private queues: Map<string, Queue> = new Map();
private workers: Map<string, Worker> = new Map();
private queueEvents: Map<string, QueueEvents> = new Map();
private connection = {
host: config.redisHost,
port: config.redisPort,
password: config.redisPassword,
db: config.redisDb,
};
/**
* 謗ィ騾∽ササ蜉。蛻ー髦溷<E9ABA6>
*/
async push<T = any>(type: string, data: T, options?: any): Promise<Job> {
try {
// 闔キ蜿匁<E89CBF>蛻帛サコ髦溷<E9ABA6>? let queue = this.queues.get(type);
if (!queue) {
queue = new Queue(type, {
connection: this.connection,
defaultJobOptions: {
removeOnComplete: 100, // 菫晉蕗譛霑?00荳ェ螳梧<E89EB3>莉サ蜉? removeOnFail: false, // 螟ア雍・莉サ蜉。荳榊唖髯、<E9ABAF>井セソ莠取賜譟・<E8AD9F>? attempts: 3, // 螟ア雍・驥崎ッ<E5B48E>3谺? backoff: {
type: 'exponential',
delay: 2000, // 2遘偵?遘偵?遘? },
}
});
this.queues.set(type, queue);
}
// 豺サ蜉<EFBDBB>莉サ蜉。
const job = await queue.add(type, data, {
...options,
jobId: options?.jobId, // 謾ッ謖∬<E8AC96>螳壻ケ泳obId
});
logger.info(`[RedisQueue] 莉サ蜉。蜈・髦滓<E9ABA6>蜉歔, {
type,
jobId: job.id,
dataSize: JSON.stringify(data).length
});
return {
id: job.id!,
type,
data,
status: 'pending',
createdAt: new Date(),
};
} catch (error) {
logger.error(`[RedisQueue] 莉サ蜉。蜈・髦溷、ア雍・`, { type, error });
throw error;
}
}
/**
* 豕ィ蜀御ササ蜉。螟<EFBDA1>炊蝎? */
process<T = any>(type: string, handler: JobHandler<T>): void {
try {
// 蛻帛サコWorker
const worker = new Worker(
type,
async (job: BullJob) => {
logger.info(`[RedisQueue] 蠑蟋句、<EFBFBD>炊莉サ蜉。`, {
type,
jobId: job.id,
attemptsMade: job.attemptsMade,
attemptsTotal: job.opts.attempts
});
const startTime = Date.now();
try {
// 隹<>畑荳壼苅螟<E88B85>炊蜃ス謨ー
const result = await handler({
id: job.id!,
type,
data: job.data as T,
status: 'processing',
createdAt: new Date(job.timestamp),
});
const duration = Date.now() - startTime;
logger.info(`[RedisQueue] 莉サ蜉。螟<EFBFBD>炊謌仙粥`, {
type,
jobId: job.id,
duration: `${duration}ms`
});
return result;
} catch (error) {
const duration = Date.now() - startTime;
logger.error(`[RedisQueue] 莉サ蜉。螟<EFBFBD>炊螟ア雍・`, {
type,
jobId: job.id,
attemptsMade: job.attemptsMade,
duration: `${duration}ms`,
error: error instanceof Error ? error.message : 'Unknown error'
});
throw error; // 謚帛<E8AC9A>髞呵ッッ<EFBDAF>瑚ァヲ蜿鷹㍾隸? }
},
{
connection: this.connection,
concurrency: 1, // 豈丈クェWorker蟷カ蜿大、<E5A4A7>炊1荳ェ莉サ蜉? }
);
this.workers.set(type, worker);
// 逶大成Worker莠倶サカ
worker.on('completed', (job) => {
logger.info(`[RedisQueue] 笨?莉サ蜉。螳梧<EFBFBD>`, {
type,
jobId: job.id,
returnvalue: job.returnvalue
});
});
worker.on('failed', (job, err) => {
logger.error(`[RedisQueue] 笶?莉サ蜉。螟ア雍・`, {
type,
jobId: job?.id,
attemptsMade: job?.attemptsMade,
error: err.message,
stack: err.stack
});
});
worker.on('error', (err) => {
logger.error(`[RedisQueue] Worker髞呵ッッ`, { type, error: err.message });
});
logger.info(`[RedisQueue] Worker蟾イ豕ィ蜀形, { type });
} catch (error) {
logger.error(`[RedisQueue] Worker豕ィ蜀悟、ア雍・`, { type, error });
throw error;
}
}
/**
* 闔キ蜿紋ササ蜉。迥カ諤? */
async getJob(id: string): Promise<Job | null> {
try {
// 驕榊紙謇譛蛾弌蛻玲衍謇セ莉サ蜉? for (const [type, queue] of this.queues) {
const job = await queue.getJob(id);
if (job) {
return {
id: job.id!,
type,
data: job.data,
status: await this.getJobStatus(job),
progress: job.progress as number || 0,
createdAt: new Date(job.timestamp),
error: job.failedReason,
};
}
}
return null;
} catch (error) {
logger.error(`[RedisQueue] getJob螟ア雍・`, { id, error });
return null;
}
}
/**
* 闔キ蜿紋ササ蜉。迥カ諤? */
private async getJobStatus(job: BullJob): Promise<string> {
const state = await job.getState();
switch (state) {
case 'completed': return 'completed';
case 'failed': return 'failed';
case 'active': return 'processing';
case 'waiting': return 'pending';
case 'delayed': return 'pending';
default: return 'pending';
}
}
/**
* 譖エ譁ー莉サ蜉。霑帛コヲ
*/
async updateProgress(id: string, progress: number, message?: string): Promise<void> {
try {
for (const queue of this.queues.values()) {
const job = await queue.getJob(id);
if (job) {
await job.updateProgress(progress);
if (message) {
await job.log(message);
}
logger.debug(`[RedisQueue] 霑帛コヲ譖エ譁ー`, { id, progress, message });
return;
}
}
logger.warn(`[RedisQueue] 莉サ蜉。荳榊ュ伜惠<E4BC9C>梧裏豕墓峩譁ー霑帛コヲ`, { id });
} catch (error) {
logger.error(`[RedisQueue] 譖エ譁ー霑帛コヲ螟ア雍・`, { id, error });
}
}
/**
* 蜿匁カ井ササ蜉。
*/
async cancelJob(id: string): Promise<boolean> {
try {
for (const queue of this.queues.values()) {
const job = await queue.getJob(id);
if (job) {
await job.remove();
logger.info(`[RedisQueue] 莉サ蜉。蟾イ蜿匁カ<E58C81>, { id });
return true;
}
}
logger.warn(`[RedisQueue] 莉サ蜉。荳榊ュ伜惠<EFBFBD>梧裏豕募叙豸<EFBFBD>, { id });
return false;
} catch (error) {
logger.error(`[RedisQueue] 蜿匁カ井ササ蜉。螟ア雍・`, { id, error });
return false;
}
}
/**
* 驥崎ッ募、ア雍・莉サ蜉。
*/
async retryJob(id: string): Promise<boolean> {
try {
for (const queue of this.queues.values()) {
const job = await queue.getJob(id);
if (job) {
await job.retry();
logger.info(`[RedisQueue] 莉サ蜉。蟾イ驥崎ッ描, { id });
return true;
}
}
logger.warn(`[RedisQueue] 莉サ蜉。荳榊ュ伜惠<EFBFBD>梧裏豕暮㍾隸描, { id });
return false;
} catch (error) {
logger.error(`[RedisQueue] 驥崎ッ穂ササ蜉。螟ア雍・`, { id, error });
return false;
}
}
/**
* 貂<>炊譌ァ莉サ蜉? */
async cleanup(olderThan: number = 86400000): Promise<number> {
try {
let totalCleaned = 0;
for (const [type, queue] of this.queues) {
// 貂<>炊螳梧<E89EB3>逧<EFBFBD>ササ蜉。<E89C89>井ソ晉蕗譛霑?00荳ェ<E88DB3><EFBDAA>
const completed = await queue.clean(olderThan, 100, 'completed');
// 貂<>炊螟ア雍・逧<EFBDA5>ササ蜉。<E89C89>井ソ晉蕗譛霑?0荳ェ<E88DB3><EFBDAA>
const failed = await queue.clean(olderThan, 50, 'failed');
const cleaned = completed.length + failed.length;
totalCleaned += cleaned;
if (cleaned > 0) {
logger.info(`[RedisQueue] 髦溷<E9ABA6>貂<EFBFBD>炊螳梧<E89EB3>`, {
type,
completed: completed.length,
failed: failed.length
});
}
}
return totalCleaned;
} catch (error) {
logger.error(`[RedisQueue] 貂<>炊莉サ蜉。螟ア雍・`, { error });
return 0;
}
}
/**
* 蜈ウ髣ュ謇譛芽ソ樊磁<E6A88A>井シ倬寉蜈ウ髣ュ<E9ABA3>? */
async close(): Promise<void> {
try {
// 蜈ウ髣ュ謇譛姥orkers
for (const [type, worker] of this.workers) {
await worker.close();
logger.info(`[RedisQueue] Worker蟾イ蜈ウ髣ュ`, { type });
}
// 蜈ウ髣ュ謇譛渦ueues
for (const [type, queue] of this.queues) {
await queue.close();
logger.info(`[RedisQueue] Queue蟾イ蜈ウ髣ュ`, { type });
}
// 蜈ウ髣ュ謇譛渦ueueEvents
for (const [type, events] of this.queueEvents) {
await events.close();
logger.info(`[RedisQueue] QueueEvents蟾イ蜈ウ髣ュ`, { type });
}
logger.info(`[RedisQueue] 謇譛芽ソ樊磁蟾イ蜈ウ髣ュ`);
} catch (error) {
logger.error(`[RedisQueue] 蜈ウ髣ュ霑樊磁螟ア雍・`, { error });
}
}
}
豁・鬪、5.3<EFBFBD>壽峩譁ーJobFactory謾ッ謖ヽedis髦溷<EFBFBD>
// 譁<>サカ<EFBDBB>喘ackend/src/common/jobs/JobFactory.ts
import { JobQueue } from './types.js'
import { MemoryQueue } from './MemoryQueue.js'
import { RedisQueue } from './RedisQueue.js' // 竊?譁ー蠅<EFBDB0>
import { logger } from '../logging/index.js'
import { config } from '../../config/env.js'
export class JobFactory {
private static instance: JobQueue | null = null
static getInstance(): JobQueue {
if (!this.instance) {
this.instance = this.createQueue()
}
return this.instance
}
private static createQueue(): JobQueue {
const queueType = config.queueType || 'memory'
logger.info('[JobFactory] 蛻晏ァ句喧莉サ蜉。髦溷<E9ABA6>?, { queueType });
switch (queueType) {
case 'redis': // 竊?譁ー蠅<EFBDB0>
return this.createRedisQueue()
case 'memory':
return this.createMemoryQueue()
default:
logger.warn(`[JobFactory] Unknown QUEUE_TYPE: ${queueType}, fallback to memory`)
return this.createMemoryQueue()
}
}
/**
* 蛻帛サコRedis髦溷<E9ABA6><E6BAB7>亥クヲ髯咲コァ遲也払<E4B99F>? */
private static createRedisQueue(): JobQueue {
try {
logger.info('[JobFactory] 豁」蝨ィ霑樊磁Redis髦溷<EFBFBD>...');
const redisQueue = new RedisQueue();
logger.info('[JobFactory] 笨?Redis髦溷<EFBFBD>蛻晏ァ句喧謌仙<EFBFBD>?);
return redisQueue;
} catch (error) {
logger.error('[JobFactory] 笶?Redis髦溷<E9ABA6>蛻晏ァ句喧螟ア雍・<E99B8D>碁剄郤ァ蛻ー蜀<EFBDB0>ュ倬弌蛻?, { error });
return this.createMemoryQueue();
}
}
private static createMemoryQueue(): MemoryQueue {
logger.info('[JobFactory] 菴ソ逕ィ蜀<EFBFBD>ュ倬弌蛻<EFBFBD>')
const queue = new MemoryQueue()
// 螳壽悄貂<E68284>炊<EFBFBD>磯∩蜈榊<E89C88>蟄俶ウ<E4BFB6>シ擾シ<E693BE>
if (process.env.NODE_ENV !== 'test') {
setInterval(() => {
queue.cleanup()
}, 60 * 60 * 1000)
}
return queue
}
static reset(): void {
this.instance = null
}
}
*豁・鬪、5.4<EFBFBD>壻ソョ謾ケ荳壼苅莉」遐∽スソ逕ィ髦溷<EFBFBD>?
// 遉コ萓具シ哂SL譁<4C>鍵遲幃画隼騾?// 譁<>サカ<EFBDBB>喘ackend/src/modules/asl/services/screeningService.ts
import { jobQueue } from '../../../common/jobs/index.js';
export async function startScreeningTask(projectId: string, userId: string) {
// 1. 蛻帛サコ謨ー謐ョ蠎謎ササ蜉。隶ー蠖? const task = await prisma.aslScreeningTask.create({
data: {
projectId,
status: 'pending',
totalItems: literatures.length,
// ...
}
});
// 2. 謗ィ騾∝芦Redis髦溷<E9ABA6><E6BAB7>井ク埼仆蝪櫁ッキ豎ゑシ? await jobQueue.push('asl:title-screening', {
taskId: task.id,
projectId,
literatureIds: literatures.map(lit => lit.id),
});
logger.info('莉サ蜉。蟾イ蜈・髦?, { taskId: task.id });
// 3. 遶句叉霑泌屓<E6B38C>亥燕遶ッ霓ョ隸「霑帛コヲ<EFBDBA><EFBDA6>
return task;
}
// 豕ィ蜀係orker<65>亥惠蠎皮畑蜷ッ蜉ィ譌カ<E8AD8C><EFBDB6>
// 譁<>サカ<EFBDBB>喘ackend/src/index.ts
jobQueue.process('asl:title-screening', async (job) => {
const { taskId, projectId, literatureIds } = job.data;
logger.info('蠑蟋句、<EFBFBD>炊遲幃我ササ蜉?, { taskId, total: literatureIds.length });
for (let i = 0; i < literatureIds.length; i++) {
const literatureId = literatureIds[i];
// 螟<>炊蜊慕ッ<E68595>枚迪ョ
await processSingleLiterature(literatureId, projectId);
// 譖エ譁ー霑帛コヲ
const progress = ((i + 1) / literatureIds.length) * 100;
await jobQueue.updateProgress(job.id, progress);
// 譖エ譁ー謨ー謐ョ蠎? await prisma.aslScreeningTask.update({
where: { id: taskId },
data: { processedItems: i + 1 }
});
}
// 譬<>ョー螳梧<E89EB3>
await prisma.aslScreeningTask.update({
where: { id: taskId },
data: { status: 'completed', completedAt: new Date() }
});
logger.info('遲幃我ササ蜉。螳梧<E89EB3>?, { taskId });
return { success: true, processed: literatureIds.length };
});
豁・鬪、5.5<EFBFBD>壽峩譁?env驟咲スョ
# backend/.env
# ==================== 莉サ蜉。髦溷<E9ABA6>驟咲スョ ====================
QUEUE_TYPE=redis # 竊?謾ケ荳コredis
# Redis驟咲スョ<EFBDBD>井ク守シ灘ュ伜<EFBDAD>逕ィ<E98095>?REDIS_HOST=localhost
REDIS_PORT=6379
REDIS_PASSWORD=
REDIS_DB=0
豁・鬪、5.6<EFBFBD>壽オ玖ッ紐edis髦溷<EFBFBD>
# 1. 蜷ッ蜉ィ蜷守ォッ
cd backend
npm run dev
# 蠎碑ッ・逵句芦譌・蠢暦シ?# [JobFactory] 豁」蝨ィ霑樊磁Redis髦溷<E9ABA6>...
# [JobFactory] 笨?Redis髦溷<E9ABA6>蛻晏ァ句喧謌仙<E8AC8C>?
# 2. 謠蝉コ、豬玖ッ穂ササ蜉。<E89C89>井スソ逕ィREST Client謌鳳ostman<61>?POST http://localhost:3001/api/v1/asl/projects/:projectId/screening
# 3. 隗ょッ滓律蠢<E5BE8B>
# [RedisQueue] 莉サ蜉。蜈・髦滓<E9ABA6>蜉<EFBFBD> { type: 'asl:title-screening', jobId: '1' }
# [RedisQueue] 蠑蟋句、<E58FA5>炊莉サ蜉?{ type: 'asl:title-screening', jobId: '1' }
# [RedisQueue] 笨?莉サ蜉。螳梧<E89EB3> { type: 'asl:title-screening', jobId: '1' }
# 4. 豬玖ッ募ョ樔セ矩㍾蜷ッ諱「螟<EFBDA2>
# 謠蝉コ、莉サ蜉。 竊?遲牙セ<E78999>、<EFBFBD>炊蛻?0% 竊?Ctrl+C蛛懈ュ「 竊?驥肴眠蜷ッ蜉ィ
# 莉サ蜉。蠎碑ッ・閾ェ蜉ィ莉山edis諱「螟榊ケカ扈ァ扈ュ螟<EFBDAD><E89E9F>?```
---
## 5. 豬玖ッ墓婿譯<E5A9BF>
### 5.1 蜊募<E89C8A>豬玖ッ墓ク<E5A293>黒
| 豬玖ッ暮。?| 鬚<>悄扈捺棡 | 螳樣刔扈捺棡 | 迥カ諤?|
|--------|---------|---------|------|
| Redis霑樊磁豬玖ッ<E78E96> | PONG | 笨?| 筮?蠕<>オ<EFBFBD> |
| 蝓コ譛ャ隸サ蜀呎オ玖ッ<E78E96> | 蛟シ蛹ケ驟?| 笨?| 筮?蠕<>オ<EFBFBD> |
| 蟇ケ雎。蠎丞<E8A08E>蛹匁オ玖ッ?| 蟇ケ雎。螳梧紛 | 笨?| 筮?蠕<>オ<EFBFBD> |
| TTL霑<4C>悄豬玖ッ<E78E96> | 2遘貞錘荳コnull | 笨?| 筮?蠕<>オ<EFBFBD> |
| 螟ァ蟇ケ雎。豬玖ッ包シ<E58C85>50KB<4B>?| <10ms | 笨?| 筮?蠕<>オ<EFBFBD> |
| 髯咲コァ遲也払豬玖ッ<E78E96> | 閾ェ蜉ィ蛻<EFBDA8>困蜀<E59BB0>ュ<EFBFBD> | 笨?| 筮?蠕<>オ<EFBFBD> |
### 5.2 髮<><E9ABAE>豬玖ッ墓ク<E5A293>黒
| 豬玖ッ暮。?| 謫堺ス懈ュ・鬪、 | 鬚<>悄扈捺棡 | 迥カ諤?|
|--------|---------|---------|------|
| **HealthCheckService** | 荳贋シ<E8B48B>Excel 2谺?| 隨?谺。郛灘ュ伜多荳?| 筮?蠕<>オ<EFBFBD> |
| **LLM12FieldsService** | 謠仙叙蜷御クPDF 2谺?| 隨?谺。郛灘ュ伜多荳?| 筮?蠕<>オ<EFBFBD> |
| **螟壼ョ樔セ狗シ灘ュ伜<EFBDAD>莠?* | 蜷ッ蜉ィ2荳ェ蜷守ォッ螳樔セ具シ悟ョ樔セ帰蜀吝<E89C80><E5909D>悟ョ樔セ毅隸サ蜿<EFBDBB> | B閭ス隸サ蛻ーA逧<41>シ灘ュ?| 筮?蠕<>オ<EFBFBD> |
| **螳樔セ矩㍾蜷ッ謨ー謐ョ謖∽ケ<E288BD><EFBDB9>?* | 蜀吝<E89C80>郛灘ュ<E78198> 竊?驥榊星蜷守ォッ 竊?隸サ蜿<EFBDBB> | 閭ス隸サ蜿門芦 | 筮?蠕<>オ<EFBFBD> |
### 5.3 蜴句鴨豬玖ッ<E78E96>
```bash
# 菴ソ逕ィab謌殆rk豬玖ッ募ケカ蜿題ッサ蜀<EFBDBB>
# 豬玖ッ<E78E96>1<EFBFBD>壼ケカ蜿大<E89CBF>蜈?ab -n 1000 -c 10 http://localhost:3001/api/test/cache-write
# 豬玖ッ<E78E96>2<EFBFBD>壼ケカ蜿題ッサ蜿?ab -n 10000 -c 50 http://localhost:3001/api/test/cache-read
# 鬚<>悄扈捺棡<E68DBA>?# - QPS > 1000
# - 蜩榊コ疲慮髣エ < 50ms
# - 髞呵ッッ邇?= 0%
5.4 謨<>囿讓。諡滓オ玖ッ<E78E96>
| 謨<EFBFBD>囿蝨コ譎ッ | 讓。諡滓婿豕<EFBFBD> | 鬚<EFBFBD>悄陦御クコ | 迥カ諤? |
|---|---|---|---|
| Redis遯∫┯謖よ脂 | docker stop ai-clinical-redis |
邉サ扈滄剄郤ァ蛻ー蜀<EFBFBD>ュ倡シ灘ュ假シ悟コ皮畑扈ァ扈ュ霑占。<EFBFBD> | 筮?蠕<>オ<EFBFBD> |
| Redis鄂醍サ懷サカ霑<EFBFBD> | tc qdisc add dev eth0 root netem delay 500ms |
雜<EFBFBD>慮驥崎ッ包シ梧怙扈郁ソ泌屓null | 筮?蠕<>オ<EFBFBD> |
| *Redis蜀<EFBFBD>ュ俶サ? | 蜀吝<EFBFBD>螟ァ驥乗焚謐ョ閾?56MB | 隗ヲ蜿銑RU鬩ア騾撰シ御ク榊スア蜩肴眠蜀吝<EFBFBD> | 筮?蠕<>オ<EFBFBD> |
| Redis蟇<EFBFBD><EFBFBD><EFBFBD>漠隸ッ | 菫ョ謾ケ蟇<EFBFBD><EFBFBD><EFBFBD> | 霑樊磁螟ア雍・<EFBFBD>碁剄郤ァ蛻ー蜀<EFBFBD>ュ倡シ灘ュ<EFBFBD> | 筮?蠕<>オ<EFBFBD> |
6. 鬟朱勦隸<E58BA6>シー荳守シ楢ァ?
6.1 鬟朱勦遏ゥ髦オ
| 鬟朱勦 | 荳・驥肴? | 讎ら紫 | 蠖ア蜩<EFBFBD> | 郛楢ァ」謗ェ譁ス | 迥カ諤? |
|---|---|---|---|---|---|
| Redis霑樊磁螟ア雍・ | <EFBFBD>閥 鬮? | <EFBFBD>泯 荳? | 邉サ扈滉ク榊庄逕? | 笨?髯咲コァ遲也払 | 笨?蟾イ螳樒<E89EB3>? |
| 謨ー謐ョ荳「螟ア | <EFBFBD>泯 荳? | <EFBFBD>泙 菴? | 郛灘ュ伜、ア謨<EFBFBD> | 笨?蜈ウ髞ョ謨ー謐ョ蜿悟<E89CBF>DB | 竢?蠕<>ョ樒<EFBDAE>? |
| *蜀<EFBFBD>ュ俶コ「蜃コ<EFBFBD><EFBFBD>OM<EFBFBD>? | <EFBFBD>閥 鬮? | <EFBFBD>泯 荳? | Redis蟠ゥ貅<EFBFBD> | 笨?荳・譬シTTL + 逶第而 | 竢?蠕<>ョ樒<EFBDAE>? |
| 鄂醍サ懷サカ霑<EFBFBD> | <EFBFBD>泙 菴? | <EFBFBD>泙 菴? | 蜩榊コ泌序諷「 | 笨?謇ケ驥乗桃菴<E6A183> | 竢?蜿ッ騾我シ伜<EFBDBC>? |
| 驟咲スョ髞呵ッッ | <EFBFBD>泯 荳? | <EFBFBD>泯 荳? | 蜷ッ蜉ィ螟ア雍・ | 笨?驟咲スョ鬪瑚ッ<E7919A> | 笨?蟾イ螳樒<E89EB3>? |
| 蟇<EFBFBD><EFBFBD>∵ウ<EFBFBD>愆 | <EFBFBD>閥 鬮? | <EFBFBD>泯 荳? | 謨ー謐ョ豕<EFBFBD>愆 | 笨?KMS邂。逅<EFBDA1> | 竢?蠕<>ョ樒<EFBDAE>? |
6.2 蜈ウ髞ョ郛楢ァ」謗ェ譁ス
*郛楢ァ」謗ェ譁ス1<EFBFBD>夐剄郤ァ遲也払<EFBFBD>亥ソ<EFBFBD>。サ<EFBFBD>? 笨?```typescript
// 蟾イ蝨ィCacheFactory荳ュ螳樒<E89EB3>?// Redis荳榊庄逕ィ譌カ閾ェ蜉ィ蛻<EFBDA8>困蛻ーMemoryCache
#### **郛楢ァ」謗ェ譁ス2<EFBDBD>壼<EFBFBD>髞ョ謨ー謐ョ蜿悟<E89CBF><E6829F>域耳闕撰シ?* 竢?```typescript
// 髴隕∝惠荳壼苅莉」遐∽クュ豺サ蜉?
// 遉コ萓具シ壻ササ蜉。霑帛コヲ蜿悟<E89CBF>?export async function updateTaskProgress(taskId: string, progress: number) {
// 1. 蜀儚edis<69>亥ソォ騾滓衍隸「<E99AB8><EFBDA2>
await cache.set(`task:${taskId}:progress`, progress, 3600);
// 2. 蜷梧慮蜀僖B<E58396>域戟荵<E6889F>喧<EFBFBD>? await prisma.aslScreeningTask.update({
where: { id: taskId },
data: { processedItems: progress }
});
}
*郛楢ァ」謗ェ譁ス3<EFBFBD>壼<EFBFBD>蟄倡尅謗ァ<EFBFBD>域耳闕撰シ? 竢?```typescript
// 蛻帛サコ<EFBDBB>喘ackend/src/scripts/monitor-redis.ts
import { cache } from '../common/cache/index.js';
setInterval(async () => { // 譽譟・蜀<EFBDA5>ュ倅スソ逕? const info = await redis.info('memory'); const used = parseInt(info.match(/used_memory:(\d+)/)[1]); const max = 256 * 1024 * 1024;
if (used > max * 0.8) { logger.warn('笞<><E7AC9E><EFBFBD> Redis蜀<73>ュ倅スソ逕ィ雜<EFBDA8>ソ<EFBFBD>80%', { used, max }); // TODO: 蜿鷹<E9B7B9>忠髓?驍ョ莉カ蜻願ュヲ } }, 60000);
#### **郛楢ァ」謗ェ譁ス4<EFBDBD>夐<EFBFBD>鄂ョ鬪瑚ッ<E7919A>シ亥キイ螳樒鴫<E6A892><E9B4AB>** 笨?```typescript
// env.ts荳ュ逧ёalidateConfig()
// 蜷ッ蜉ィ譌カ譽譟・Redis驟咲スョ
7. 荳顔コソ隶。蛻<EFBDA1>
7.1 荳顔コソ譌カ髣エ陦ィ<E999A6><EFBDA8>2.0譖エ譁ー<EFBFBD>?
| 髦カ谿オ | 譌カ髣エ | 莉サ蜉。 | 雍溯エ」莠? | 迥カ諤? |
|---|---|---|---|---|
| Phase 1 | Day 1荳雁壕 | 譛ャ蝨ー蠑蜿醍識蠅<EFBFBD>㊥螟? | 蠑蜿? | 筮?蠕<>シ蟋? |
| Phase 2 | Day 1荳句壕 | 螳樒鴫RedisCacheAdapter | 蠑蜿? | 筮?蠕<>シ蟋? |
| Phase 3 | Day 2蜈ィ螟ゥ | Redis郛灘ュ俶悽蝨ー豬玖ッ<EFBFBD> | 蠑蜿?豬玖ッ<E78E96> | 筮?蠕<>シ蟋? |
| Phase 4 | Day 3荳雁壕 | 髦ソ驥御コ然edis雍ュ荵ー&驟咲スョ | 霑千サエ | 筮?蠕<>シ蟋? |
| Phase 5 | Day 3荳句壕-Day 5 | <EFBFBD>閥 螳樒鴫RedisQueue<75>亥ソ<E4BAA5>。サ<EFBDA1>榎 蠑蜿? | 筮?蠕<>シ蟋? | |
| Phase 6 | Day 6蜈ィ螟ゥ | Redis髦溷<EFBFBD>譛ャ蝨ー豬玖ッ<EFBFBD> + 荳壼苅髮<E88B85><E9ABAE> | 蠑蜿?豬玖ッ<E78E96> | 筮?蠕<>シ蟋? |
| Phase 7 | Day 7荳雁壕 | SAE豬玖ッ慕識蠅<EFBFBD>ェ瑚ッ<EFBFBD> | 蠑蜿?豬玖ッ<E78E96> | 筮?蠕<>シ蟋? |
| Phase 8 | Day 7荳句壕 | 逕滉コァ邇ッ蠅<EFBFBD>ク顔コソ | 蜈ィ蜻<EFBFBD> | 筮?蠕<>シ蟋? |
| Phase 9 | Day 7譎? | 逶第而隗ょッ滂シ?4蟆乗慮<E4B997>? | 霑千サエ | 筮?蠕<>シ蟋? |
諤サ蟾・菴憺㍼<EFBFBD>?螟ゥ<E89E9F>域ッ泌次隶。蛻貞「槫刈4螟ゥ<E89E9F>御ス<E5BEA1>。ョ菫晄<E88FAB>ク蠢<EFBDB8>粥閭ス蜿ッ逕ィ<E98095><EFBDA8>
7.2 荳顔コソ豁・鬪、<E9ACAA>育函莠ァ邇ッ蠅<EFBDAF>シ<EFBFBD>
*Step 1<>壼書蟶<E69BB8>燕譽譟・<E8AD9F><EFBDA5>15蛻<35>帖<EFBFBD>?
笨?莉」遐∝キイ謠蝉コ、Git
笨?譛ャ蝨ー豬玖ッ募<EFBDAF>驛ィ騾夊ソ<E5A48A>
笨?髦ソ驥御コ然edis蟾イ蟆ア扈?笨?SAE邇ッ蠅<EFBDAF>序驥丞キイ驟咲ス?笨?蝗樊サ壽婿譯亥キイ蜃<EFBDB2>、?笨?逶第而蟾イ蟆ア扈?```
#### **Step 2<>夂<EFBFBD>蠎ヲ蜿大ク<E5A4A7>シ<EFBFBD>30蛻<30>帖<EFBFBD>?*
- SAE謗ァ蛻カ蜿?竊?騾画叫蠎皮畑
- 蠎皮畑驛ィ鄂イ 竊?蛻<>音蜿大ク<E5A4A7>
- 隨?謇ケ<E8AC87><EFBDB9>10%螳樔セ具シ郁ァょッ?5蛻<35>帖<EFBFBD>?4. 隨?謇ケ<E8AC87><EFBDB9>50%螳樔セ具シ郁ァょッ?0蛻<30>帖<EFBFBD>?5. 隨?謇ケ<E8AC87><EFBDB9>100%螳樔セ<E6A894>
#### **Step 3<>夐ェ瑚ッ<E7919A>シ<EFBFBD>15蛻<35>帖<EFBFBD>?*
```bash
# 1. 譽譟・Redis霑樊磁
curl https://your-api.com/api/health
# 蠎碑ッ・霑泌屓<E6B38C>嘴 cache: "redis", status: "ok" }
# 2. 豬玖ッ慕シ灘ュ伜<EFBDAD>蜈・
# 荳贋シ<E8B48B>Excel 竊?譟・逵区律蠢<E5BE8B> 竊?遑ョ隶、Redis蜀吝<E89C80>
# 3. 豬玖ッ慕シ灘ュ倩ッサ蜿<EFBDBB>
# 蜀肴ャ。荳贋シ<E8B48B> 竊?譟・逵区律蠢<E5BE8B> 竊?遑ョ隶、郛灘ュ伜多荳ュ
# 4. 譽譟・Redis蜀<73>ュ<EFBFBD>
髦ソ驥御コ第而蛻カ蜿ー 竊?Redis逶第而 竊?蜀<>ュ倅スソ逕ィ
*Step 4<>夂尅謗ァ隗ょッ滂シ<E6BB82>24蟆乗慮<E4B997>?
蜈ウ豕ィ謖<EFBFBD><EFBFBD><EFBFBD>シ?- Redis霑樊磁謨ー<E8ACA8>亥コ碑ッ・遞ウ螳夲シ?- 蜀<>ュ倅スソ逕ィ邇<EFBDA8>シ亥コ碑ッ・ < 50%<25>?- 郛灘ュ伜多荳ュ邇<EFBDAD>シ亥コ碑ッ・ > 80%<25>?- 蠎皮畑髞呵ッッ譌・蠢暦シ亥コ碑ッ・譌<EFBDA5>Redis逶ク蜈ウ髞呵ッッ<EFBDAF>?- API謌先悽<E58588>亥コ碑ッ・荳矩剄<E79FA9><E58984>
8. 蝗樊サ壽婿譯<E5A9BF>
8.1 蠢ォ騾溷屓貊夲シ<E5A4B2>5蛻<35>帖蜀<E5B896>シ<EFBFBD>
*蝨コ譎ッ1<EFBFBD>啌edis霑樊磁螟ア雍・<EFBFBD>悟コ皮畑譌<EFBFBD>豕募星蜉?
# 譁ケ豕<EFBDB9>1<EFBFBD>壻ソョ謾ケSAE邇ッ蠅<EFBDAF>序驥<E5BA8F>
髦ソ驥御コ第而蛻カ蜿ー 竊?SAE蠎皮畑 竊?驟咲スョ邂。逅<EFBDA1> 竊?邇ッ蠅<EFBDAF>序驥<E5BA8F>
菫ョ謾ケ<E8ACBE>咾ACHE_TYPE=memory
菫晏ュ<E6998F> 竊?蠎皮畑驥榊星
# 譁ケ豕<EFBDB9>2<EFBFBD>夐㍾譁ー驛ィ鄂イ荳贋ク荳ェ迚域<E8BF9A>?SAE謗ァ蛻カ蜿?竊?蠎皮畑驛ィ鄂イ 竊?迚域悽邂。逅<EFBDA1> 竊?蝗樊サ<E6A88A>
*蝨コ譎ッ2<EFBFBD>啌edis諤ァ閭ス髣ョ鬚假シ悟桃蠎泌序諷?
# 荳エ譌カ髯咲コァ蛻ー蜀<EFBDB0>ュ倡シ灘ュ?CACHE_TYPE=memory
# 謌紋ソ晉蕗Redis菴<73>」譟・鄂醍サ?ping r-xxxxxxxxxxxx.redis.rds.aliyuncs.com
蝨コ譎ッ3<EFBFBD>啌edis蜀<EFBFBD>ュ俶サ。<EFBFBD>梧裏豕募<EFBFBD>蜈・
# 譁ケ豕<EFBDB9>1<EFBFBD>壽ク<E5A3BD>炊Redis<69>亥些髯ゥ<E9ABAF><EFBDA9>シ?redis-cli -h r-xxx.redis.rds.aliyuncs.com -a password
> FLUSHDB
# 譁ケ豕<EFBDB9>2<EFBFBD>壼合郤ァRedis隗<73><E99A97>シ
髦ソ驥御コ第而蛻カ蜿ー 竊?Redis螳樔セ<E6A894> 竊?蜿倬<E89CBF> 竊?512MB
8.2 蝗樊サ壽」譟・貂<EFBDA5><E8B282>?
笨?蠎皮畑閭ス蜷ヲ豁」蟶ク蜷ッ蜉ィ<E89C89>?笨?郛灘ュ俶弍蜷ヲ蟾・菴懶シ亥<EFBDBC>蟄俶ィ。蠑擾シ会シ?笨?API蜩榊コ疲弍蜷ヲ豁」蟶ク<E89FB6>?笨?髞呵ッッ譌・蠢玲弍蜷ヲ貂<EFBDA6>勁<EFBFBD>?笨?逕ィ謌キ譏ッ蜷ヲ閭ス豁」蟶ク菴ソ逕ィ<E98095><EFBDA8>
9. 逶第而荳手ソ千サ?
9.1 逶第而謖<E8808C><E8AC96><EFBFBD>
Redis謖<EFBFBD><EFBFBD><EFBFBD>シ磯仭驥御コ第而蛻カ蜿ー<EFBFBD><EFBFBD>
| 謖<EFBFBD><EFBFBD><EFBFBD> | 豁」蟶ク闌<EFBFBD>峩 | 蜻願ュヲ髦亥? | 螟<EFBFBD>炊譁ケ譯<EFBFBD> |
|---|---|---|---|
| *蜀<EFBFBD>ュ倅スソ逕ィ邇? | < 50% | > 80% | 譽譟・螟ァkey<EFBFBD>瑚<EFBFBD>剔蜊<EFBFBD><EFBFBD> |
| *霑樊磁謨? | < 50 | > 100 | 譽譟・霑樊磁豕<EFBFBD>シ? |
| QPS | < 1000 | > 5000 | 閠<EFBFBD>剔蛻<EFBFBD>援 |
| *蜻ス荳ュ邇? | > 80% | < 50% | 譽譟・郛灘ュ倡ュ也<EFBFBD>? |
| 蜩榊コ疲慮髣エ | < 5ms | > 50ms | 譽譟・鄂醍サ? |
*蠎皮畑謖<EFBFBD><EFBFBD><EFBFBD>シ<EFBFBD>AE譌・蠢暦シ?
# 譟・謇セRedis逶ク蜈ウ髞呵ッッ
grep "Redis" logs/app.log | grep "ERROR"
# 譟・謇セ郛灘ュ伜多荳ュ諠<EFBDAD><E8ABA0>
grep "Cache hit" logs/app.log | wc -l
grep "Cache miss" logs/app.log | wc -l
# 隶。邂怜多荳ュ邇?蜻ス荳ュ邇?= hits / (hits + misses) * 100%
9.2 霑千サエ蜻ス莉、
蟶ク逕ィRedis CLI蜻ス莉、
# 霑樊磁Redis
redis-cli -h r-xxx.redis.rds.aliyuncs.com -p 6379 -a your_password
# 譟・逵区園譛洩ey
KEYS *
# 譟・逵倶ササ蜉。逶ク蜈ウkey
KEYS task:*
# 譟・逵狗シ灘ュ倡嶌蜈ウkey
KEYS fulltext:*
# 譟・逵橘ey逧УTL
TTL task:abc123:progress
# 譟・逵橘ey逧<79>?GET task:abc123:progress
# 譟・逵句<E980B5>蟄倅スソ逕ィ
INFO memory
# 譟・逵玖ソ樊磁謨?INFO clients
# 螳樊慮逶第而蜻ス莉、
MONITOR
# 貂<>ゥコ謨ー謐ョ蠎難シ亥些髯ゥ<E9ABAF><EFBDA9>シ<EFBFBD>
FLUSHDB
蜀<EFBFBD>ュ伜<EFBFBD>譫<EFBFBD>
# 譟・謇セ螟ァkey<65>?10KB<4B>?redis-cli -h xxx -a password --bigkeys
# 譟・逵橘ey逧<79><E980A7>蟄伜頃逕?MEMORY USAGE task:abc123:progress
9.3 謨<>囿謗呈衍豬∫ィ<E288AB>
髣ョ鬚假シ啌edis霑樊磁螟ア雍・
竊?1. 譽譟・Redis螳樔セ狗憾諤<E686BE>シ磯仭驥御コ第而蛻カ蜿ー<E89CBF>? 竊?2. 譽譟・逋ス蜷榊黒驟咲スョ<EFBDBD>域弍蜷ヲ蛹<EFBDA6>性SAE IP<49>? 竊?3. 譽譟・蟇<EFBDA5><E89F87>∵弍蜷ヲ豁」遑? 竊?4. ping豬玖ッ慕ス醍サ懆ソ樣壽? 竊?5. 譟・逵句コ皮畑譌・蠢<EFBDA5>
竊?6. 螯よ裏豕募ソォ騾溯ァ」蜀?竊?髯咲コァ蛻ー蜀<EFBDB0>ュ倡シ灘ュ?```
### 9.4 螳壽悄扈エ謚、
| 扈エ蠎ヲ | 鬚醍紫 | 蜀<>ョケ |
|------|------|------|
| **譌・蟶ク逶第而** | 豈丞、ゥ | 譟・逵句<E980B5>蟄倅スソ逕ィ縲∬ソ樊磁謨ー縲<EFBDB0>漠隸ッ譌・蠢?|
| **諤ァ閭ス蛻<EFBDBD>梵** | 豈丞捉 | 蛻<>梵郛灘ュ伜多荳ュ邇<EFBDAD>∝桃蠎疲慮髣?|
| **螳ケ驥剰ッ<E589B0>シー** | 豈乗怦 | 隸<>シー256MB譏ッ蜷ヲ螟溽畑<E6BABD>梧弍蜷ヲ髴隕∝合驟?|
| **螳牙<E89EB3>譽譟?* | 豈乗怦 | 譽譟・逋ス蜷榊黒縲∝ッ<E2889D><EFBDAF>∝シコ蠎ヲ縲∬ョソ髣ョ譌・蠢?|
---
## 10. 謌仙粥譬<E7B2A5>㊥
### 10.1 謚譛ッ謖<EFBDAF><E8AC96><EFBFBD>シ<EFBFBD>2.0譖エ譁ー<EFBFBD>?
| 謖<><E8AC96><EFBFBD> | 蠖灘燕蛟シ<E89B9F>亥<EFBFBD>蟄假シ?| 逶ョ譬<EFBDAE>シ<C280><EFBDBC>edis<69>?| 陦。驥乗婿豕<E5A9BF> |
|------|---------------|----------------|---------|
| **郛灘ュ俶戟荵<E6889F><E88DB5>?* | 笶?螳樔セ矩㍾蜷ッ荳「螟ア | 笨?謖∽ケ<E288BD>喧菫晏ュ?| 驥榊星蜷惹サ崎<EFBDBB>隸サ蜿?|
| **螟壼ョ樔セ句<EFBDBE>莠?* | 笶?蜷<>ョ樔セ狗峡遶?| 笨?蜈ィ螻蜈ア莠ォ | 螳樔セ帰蜀呻シ悟ョ樔セ毅閭ス隸サ |
| **LLM API驥榊、崎ー<E5B48E>畑** | <20>閥 鬮?| <20>泙 菴?| 蜷御クPDF蜿ェ隹<EFBDAA><E99AB9>?谺?|
| **郛灘ュ伜多荳ュ邇?* | N/A | > 60% | 逶第而譌・蠢礼サ溯ョ。 |
| **莉サ蜉。謖∽ケ<E288BD><EFBDB9>?* | 笶?螳樔セ矩楳豈∽ク「螟?| 笨?莉サ蜉。扈ァ扈ュ | <20>閥 **譁ー蠅<EFBDB0>** |
| **髟ソ莉サ蜉。謌仙粥邇<E7B2A5>** | 10-30% | > 99% | <20>閥 **蜈ウ髞ョ謖<EFBDAE><E8AC96><EFBFBD>** |
| **邉サ扈溷庄逕ィ諤?* | 99% | 99.9% | 髯咲コァ遲也払菫晞囿 |
### 10.2 荳壼苅謖<E88B85><E8AC96><EFBFBD>シ<EFBFBD>2.0譖エ譁ー<EFBFBD>?
| 謖<><E8AC96><EFBFBD> | 謾ケ騾<EFBDB9>蜑<EFBFBD> | 謾ケ騾<EFBDB9>蜷<EFBFBD> | 陦。驥乗婿豕<E5A9BF> |
|------|--------|--------|---------|
| **LLM API謌先悽** | ツ・X/譛?| 髯堺ス<E5A0BA>40-60% | 蟇ケ豈碑エヲ蜊<EFBDA6> |
| **莉サ蜉。荳「螟ア邇?* | <20>閥 70-95% | < 1% | <20>閥 **譛驥崎ヲ<E5B48E>** |
| **莉サ蜉。螳梧<E89EB3>譌カ髣エ** | 荳咲。ョ螳?| 遞ウ螳<EFBDB3> | 逶第而譌・蠢<EFBDA5> |
| **逕ィ謌キ驥榊、肴署莠、谺。謨ー** | 蟷ウ蝮<EFBDB3>3谺?| 蜃<>荵惹ク? | 逕ィ謌キ陦御クコ蛻<EFBDBA>梵 |
| **逕ィ謌キ貊。諢丞コ?* | 蝓コ郤ソ | 譏セ闡玲署蜊<E7BDB2> | 髣ョ蜊キ隹<EFBDB7>衍 |
### 10.3 鬪梧噺譬<E599BA>㊥<EFBFBD><E38AA5>2.0譖エ譁ー<EFBFBD>?
#### **Redis郛灘ュ倬ェ梧噺**
```bash
笨?謇譛臥シ灘ュ伜黒蜈<E9BB92>オ玖ッ暮夊ソ<E5A48A>
笨?HealthCheckService郛灘ュ伜多荳ュ豬玖ッ暮夊ソ<E5A48A>
笨?LLM12FieldsService郛灘ュ伜多荳ュ豬玖ッ暮夊ソ<E5A48A>
笨?螳樔セ矩㍾蜷ッ蜷守シ灘ュ倅サ榊ュ伜惠
笨?郛灘ュ伜多荳ュ邇?> 60%
笨?LLM API隹<49>畑谺。謨ー荳矩剄 > 40%
Redis髦溷<EFBFBD>鬪梧噺 <20>閥 *譁ー蠅槫<EFBFBD>髞ョ鬘?
笨?Redis髦溷<E9ABA6>蜊募<E89C8A>豬玖ッ暮夊ソ<E5A48A>
笨?髟ソ莉サ蜉。<E89C89><EFBDA1>2蟆乗慮<E4B997>画オ玖ッ暮夊ソ<E5A48A>
笨?螳樔セ矩㍾蜷ッ蜷惹ササ蜉。閾ェ蜉ィ諱「螟?笨?莉サ蜉。螟ア雍・閾ェ蜉ィ驥崎ッ包シ?谺。<E8B0BA><EFBDA1>
笨?1000遽<30>枚迪ョ遲幃画<C280>蜉溽紫 > 99%
笨?譌<>逕ィ謌キ謚戊ッ我ササ蜉。荳「螟?笨?霑帛コヲ螳樊慮譖エ譁ー豁」蟶ク
謨<EFBFBD>囿諱「螟肴オ玖ッ<EFBFBD> <20>閥 驥崎ヲ<EFBFBD>
笨?讓。諡溷ョ樔セ矩楳豈?竊?莉サ蜉。閾ェ蜉ィ諱「螟<EFBDA2>
笨?讓。諡欒edis螳墓惻 竊?邉サ扈滄剄郤ァ霑占。<E58DA0>
笨?讓。諡溽ス醍サ懷サカ霑<EFBDB6> 竊?莉サ蜉。豁」蟶ク螳梧<E89EB3>
笨?讓。諡溷ケカ蜿台ササ蜉。 竊?豁」遑ョ蛻<EFBDAE><E89BBB>螟<EFBFBD>炊
逕滉コァ邇ッ蠅<EFBFBD>ェ梧噺
笨?逕滉コァ邇ッ蠅<EFBDAF>ソ占。<E58DA0>48蟆乗慮譌<E685AE>髞呵ッ?笨?2荳ェ螳梧紛逧<E7B49B>1000遽<30>枚迪ョ遲幃我ササ蜉。謌仙<E8AC8C>?笨?逶第而謖<E8808C><E8AC96><EFBFBD>ュ」蟶ク<E89FB6>亥<EFBFBD>蟄倥∬ソ樊磁謨ー縲_PS<50>?笨?譌<>逕ィ謌キ謚戊ッ?```
---
## 11. FAQ
### Q1<51>壼ヲよ棡Redis蝨ィ蜊雁、懃ェ∫┯謖ゆコ<E38286>惹ケ亥萱<E4BAA5><E890B1>
**A1**<2A>夂ウサ扈滉シ夊<EFBDBC>蜉ィ髯咲コァ蛻ー蜀<EFBDB0>ュ倡シ灘ュ假シ悟コ皮畑扈ァ扈ュ霑占。後らャャ莠悟、ゥ霑千サエ譽譟・蟷カ諱「螟抗edis縲?
### Q2<51>?56MB螟溽畑蜷暦シ滉サ荵域慮蛟咎怙隕∝合驟搾シ<E690BE>
**A2**<2A>?- 蠖灘燕鬚<E78795>シー<EFBDBC>? 50% 菴ソ逕ィ邇?- 隗ヲ蜿大合驟堺ソ。蜿キ<E89CBF>? - 蜀<>ュ倅スソ逕ィ > 80%
- 鬚醍ケ∬ァヲ蜿銑RU鬩ア騾? - 逶第而蜻願ュヲ
- 蜊<><E89C8A>譁ケ蠑擾シ夐仭驥御コ第而蛻カ蜿ー荳髞ョ蜊<EFBDAE>コァ<EFBDBA>梧裏髴驥榊星
### Q3<51>啌edis莨壼スア蜩咲ウサ扈滓ァ閭ス蜷暦シ<E69AA6>
**A3**<2A>?- 譛ャ蝨ー蜀<EFBDB0>ュ假シ? 0.1ms
- 譛ャ蝨ーRedis<69>嘸1ms
- 髦ソ驥御コ然edis<69>亥酔蝨ー蝓滂シ会シ嘸2-5ms
- 蠖ア蜩榊庄蠢ス逡・<E980A1>御ク疲怏謇ケ驥乗桃菴應シ伜喧
### Q4<51>壼ヲよ棡蜿醍鴫Redis荳埼ょ粋<E38287>瑚<EFBFBD>蝗樣蛻ー蜀<EFBDB0>ュ倡シ灘ュ伜雛<E4BC9C>?**A4**<2A>壼庄莉・<E88E89>∽ソョ謾ケ `CACHE_TYPE=memory` 蜊ウ蜿ッ<E89CBF>御サ」遐∵髪謖∫Ο蛻<CE9F>困縲?
### Q5<51>夐怙隕∝ュヲ荵<EFBDA6>Redis蜻ス莉、蜷暦シ<E69AA6>
**A5**<2A>?- 蠑蜿托シ壻ク埼怙隕<E68099>シ御サ」遐∝キイ蟆∬」?- 霑千サエ<EFBDBB>壼サコ隶ョ蟄ヲ荵?荳ェ蝓コ遑蜻ス莉、<E88E89><EFBDA4>ET/SET/KEYS/TTL/INFO<46>?
---
## 12. 逶ク蜈ウ譁<EFBDB3>。」
- [莠大次逕溷シ蜿題ァ<EFBFBD>激](../04-蠑蜿題ァ<E9A18C><EFBDA7>?08-莠大次逕溷シ蜿題ァ<E9A18C><EFBDA7>?md)
- [SAE驛ィ鄂イ螳悟<EFBFBD>謖<EFBFBD>漉](./02-SAE驛ィ鄂イ螳悟<E89EB3>謖<EFBFBD>漉(莠ァ蜩∫サ冗炊迚?.md)
- [SAE邇ッ蠅<EFBFBD>序驥城<EFBFBD>鄂ョ謖<EFBFBD>漉](./03-SAE邇ッ蠅<EFBDAF>序驥城<E9A9A5>鄂ョ謖<EFBDAE>漉.md)
- [Redis螳俶婿譁<EFBFBD>。」](https://redis.io/docs/)
- [ioredis譁<EFBFBD>。」](https://github.com/redis/ioredis)
- [髦ソ驥御コ然edis譁<EFBFBD>。」](https://help.aliyun.com/product/26340.html)
---
## 13. 髯<>ス<EFBFBD>
### 髯<>ス柊<EFBDBD>壼ョ梧紛逧<E7B49B>.env驟咲スョ讓。譚ソ
```env
# ==================== 謨ー謐ョ蠎?====================
DATABASE_URL=postgresql://postgres:password@localhost:5432/ai_clinical_research
# ==================== Redis驟咲スョ ====================
CACHE_TYPE=redis
REDIS_HOST=localhost
REDIS_PORT=6379
REDIS_PASSWORD=
REDIS_DB=0
# 謌紋スソ逕ィ霑樊磁蟄礼ャヲ荳イ
# REDIS_URL=redis://localhost:6379
# ==================== 髦溷<E9ABA6>驟咲スョ ====================
QUEUE_TYPE=memory # 隨ャ荳髦カ谿オ逕ィmemory<72>檎ャャ莠碁亳谿オ謾ケ荳コredis
# ==================== JWT ====================
JWT_SECRET=your-secret-key-change-in-production
JWT_EXPIRES_IN=7d
# ==================== LLM API ====================
DEEPSEEK_API_KEY=sk-xxxxxxxxxxxxxx
DASHSCOPE_API_KEY=sk-xxxxxxxxxxxxxx
CLOSEAI_API_KEY=sk-xxxxxxxxxxxxxx
CLOSEAI_OPENAI_BASE_URL=https://api.openai-proxy.org/v1
CLOSEAI_CLAUDE_BASE_URL=https://api.openai-proxy.org/anthropic
# ==================== Dify ====================
DIFY_API_URL=http://localhost/v1
DIFY_API_KEY=dataset-xxxxxxxxxxxxxx
# ==================== Server ====================
PORT=3001
NODE_ENV=development
# ==================== 蟄伜お驟咲スョ ====================
STORAGE_TYPE=local
LOCAL_STORAGE_DIR=uploads
LOCAL_STORAGE_URL=http://localhost:3001/uploads
# ==================== CORS驟咲スョ ====================
CORS_ORIGIN=http://localhost:5173
# ==================== 譌・蠢鈴<E8A0A2>鄂ョ ====================
LOG_LEVEL=debug
髯<EFBFBD>ス稗<EFBFBD>啌edis蜀<EFBFBD>ュ倩ョ。邂怜<EFBFBD>?
蜊穂クェLLM扈捺棡郛灘ュ假シ嘸50KB
蜊穂クェ蛛・蠎キ譽譟・郛灘ュ假シ嘸5KB
鬚<>シー螳ケ驥擾シ?- 1000荳ェLLM扈捺棡 = 50MB
- 1000荳ェ蛛・蠎キ譽譟?= 5MB
- 邉サ扈溷シ髞 = 20MB
-----------------------------
諤サ隶。 = 75MB / 256MB = 29% 菴ソ逕ィ邇?```
### 髯<>ス匹<EFBDBD>壽腐髫懈シ皮サ<E79AAE><EFBDBB>譛?
```bash
#!/bin/bash
# 譁<>サカ<EFBDBB>喘ackend/scripts/disaster-recovery-drill.sh
echo "<22>圷 Redis謨<73>囿貍皮サ<E79AAE>シ蟋?.."
# 1. 蛛懈ュ「Redis
echo "1. 蛛懈ュ「Redis..."
docker stop ai-clinical-redis
sleep 2
# 2. 豬玖ッ募コ皮畑譏ッ蜷ヲ豁」蟶ク
echo "2. 豬玖ッ募コ皮畑蛛・蠎キ譽譟?.."
response=$(curl -s http://localhost:3001/api/health)
echo "蜩榊コ<E6A68A>: $response"
if [[ $response == *"memory"* ]]; then
echo "笨?髯咲コァ謌仙粥<E4BB99>御スソ逕ィ蜀<EFBDA8>ュ倡シ灘ュ?
else
echo "笶?髯咲コァ螟ア雍・"
exit 1
fi
# 3. 諱「螟抗edis
echo "3. 諱「螟抗edis..."
docker start ai-clinical-redis
sleep 5
# 4. 豬玖ッ紐edis諱「螟<EFBDA2>
echo "4. 豬玖ッ紐edis諱「螟<EFBDA2>..."
response=$(curl -s http://localhost:3001/api/health)
echo "蜩榊コ<E6A68A>: $response"
if [[ $response == *"redis"* ]]; then
echo "笨?Redis諱「螟肴<E89E9F>蜉<EFBFBD>"
else
echo "笞<><E7AC9E><EFBFBD> Redis譛ェ諱「螟搾シ御サ堺スソ逕ィ蜀<EFBDA8>ュ倡シ灘ュ?
fi
echo "<22>脂 謨<>囿貍皮サ<E79AAE>ョ梧<EFBDAE><E6A2A7>?
譁<EFBFBD>。」扈エ謚、閠<EFBFBD>シ<EFBFBD> 謚譛ッ蝗「髦?
譛蜷取峩譁ー<EFBFBD><EFBFBD> 2025-12-12
譁<EFBFBD>。」迥カ諤<EFBFBD>シ<EFBFBD> 笨?蠕<>ョ。譬?
*荳区ャ。譖エ譁ー<EFBFBD>? 謾ケ騾<EFBDB9>螳梧<E89EB3>蜷取サ扈鍋サ城ェ梧蕗隶ュ
笨?謾ケ騾<EFBDB9>螳梧<E89EB3>譽譟・貂<EFBDA5><E8B282>?
蝨ィ螳梧<EFBFBD>Redis謾ケ騾<EFBFBD>蜷趣シ瑚ッキ騾宣。ケ譽譟・<EFBFBD><EFBFBD>
莉」遐∝アる擇
ioredis蟾イ螳芽」?- [ ]RedisCacheAdapter蟾イ螳樒<E89EB3>?- [ ]CacheFactory蟾イ豺サ蜉<EFBDBB>髯咲コァ騾サ霎<EFBDBB>.env驟咲スョ蟾イ譖エ譁?- [ ] 謇譛我スソ逕?cache.set()逧<>慍譁ケ驛ス隶セ鄂ョ莠<EFBDAE>TL
豬玖ッ募アる擇
- 蜊募<EFBFBD>豬玖ッ募<EFBFBD>驛ィ騾夊ソ<EFBFBD>
- 髮<EFBFBD><EFBFBD>豬玖ッ募<EFBFBD>驛ィ騾夊ソ<EFBFBD>
- 蜴句鴨豬玖ッ戊セセ譬<EFBFBD>
- 謨<EFBFBD>囿讓。諡滓オ玖ッ暮夊ソ<EFBFBD>
驛ィ鄂イ螻る擇
- 髦ソ驥御コ然edis蟾イ雍ュ荵?- [ ] 逋ス蜷榊黒蟾イ驟咲スョ
- SAE邇ッ蠅<EFBFBD>序驥丞キイ驟咲ス?- [ ] 逕滉コァ邇ッ蠅<EFBDAF>キイ鬪瑚ッ?
譁<EFBFBD>。」螻る擇
- 謾ケ騾<EFBFBD>譁<EFBFBD>。」蟾イ譖エ譁ー
- 霑千サエ譁<EFBFBD>。」蟾イ陦・蜈?- [ ] 逶第而謖<E8808C><E8AC96><EFBFBD>キイ隶ー蠖?- [ ] 扈城ェ梧蕗隶ュ蟾イ諤サ扈<EFBDBB>
*逾晄隼騾<EFBFBD>鬘コ蛻ゥ<EFBFBD>∝ヲよ怏髣ョ鬚假シ瑚ッキ蜿頑慮豐滄壹? <20>噫