NestJS Redis客户端模块始终回退到localhost,不读取配置
NestJS Redis发布服务连接拒绝问题排查与解决
问题描述
使用NestJS开发服务,创建了包含缓存和发布功能的Redis客户端模块,但调用发布服务时始终出现连接拒绝错误,错误日志显示客户端尝试连接127.0.0.1:6379,但.env中已配置REDIS_HOST为Docker容器地址redis-service-1。
现有代码实现
1. Redis客户端模块
import { CacheModule, Module } from "@nestjs/common"; import { ConfigService } from "@nestjs/config"; import * as redisStore from "cache-manager-redis-store"; import { ClientsModule, Transport } from "@nestjs/microservices"; import { RedisCacheService } from "./redis-cache.service"; import { RedisPublishService } from "./redis-publish.service"; @Module({ imports: [ CacheModule.register({ inject: [ConfigService], useFactory: async (configService: ConfigService) => ({ store: redisStore, host: configService.get<string>("REDIS_HOST"), port: configService.get<string>("REDIS_PORT"), password: configService.get<string>("REDIS_PASSWORD"), }), }), ClientsModule.registerAsync([ { name:"PUBLISH_SERVICE", useFactory: async (configService: ConfigService) => { const url = `redis://${configService.get<string>( "REDIS_HOST" )}:${configService.get<string>("REDIS_PORT")}`; return { transport: Transport.REDIS, options: { url: url, password: configService.get<string>("REDIS_PASSWORD"), }, } }, inject: [ConfigService], }, ]), ], providers: [RedisCacheService, RedisPublishService], exports: [RedisCacheService, RedisPublishService], }) export class RedisClientModule {}
2. Redis缓存服务
import { CACHE_MANAGER, Inject, Injectable } from "@nestjs/common"; import { Cache } from "cache-manager"; @Injectable() export class RedisCacheService { constructor(@Inject(CACHE_MANAGER) private readonly cache: Cache) {} async get<T>(key: string): Promise<T | undefined>{ console.log("getting data from redis with key: " + key) return this.cache.get(key); } async set<T>(key:string, value:T):Promise<T> { return this.cache.set(key, value); } del(key: string): Promise<any>{ return this.cache.del(key); } async setTtl<T>(key: string, value: T, ttl: number): Promise<T>{ return this.cache.set(key,value,ttl); } }
3. Redis发布服务
import { Inject, Injectable } from "@nestjs/common"; import { ClientProxy } from "@nestjs/microservices"; import { Observable } from "rxjs"; @Injectable() export class RedisPublishService { constructor(@Inject('PUBLISH_SERVICE') private client: ClientProxy) {} emit<TResult = any, TInput = any>(pattern: any, data: TInput): Observable<TResult>{ return this.client.emit(pattern,data); } send<TResult = any, TInput = any>(pattern: any, data: TInput): Observable<TResult>{ return this.client.send(pattern,data); } }
调用模块与服务
ExampleModule
@Module({ imports:[RedisClientModule], providers: [ExampleService], controllers: [ExampleController] }) export class ExampleModule {}
ExampleService
@Injectable() export class ExampleService { constructor( private readonly cacheService:RedisCacheService, private readonly publishService:RedisPublishService, ) {} async getExample() { return this.cacheService.get("example"); } async setExample(value:string){ await this.publishService.emit("example:set",value); return this.cacheService.set("example",value); } }
错误信息
[admin-service] error 2022-09-03 17:36:15 connect ECONNREFUSED 127.0.0.1:6379 - {"stack":["ClientProxy"],"errno":-111,"code":"ECONNREFUSED","syscall":"connect","address":"127.0.0.1","port":6379} +18s 2022-09-03T10:36:15.377101300Z [admin-service] error 2022-09-03 17:36:15 connect ECONNREFUSED 127.0.0.1:6379 - {"stack":["ClientProxy"],"errno":-111,"code":"ECONNREFUSED","syscall":"connect","address":"127.0.0.1","port":6379} +9ms 2022-09-03T10:36:15.378503300Z [admin-service] error 2022-09-03 17:36:15 Retry time exhausted - {"stack":["ClientProxy"]} +2ms 2022-09-03T10:36:15.381020100Z [admin-service] error 2022-09-03 17:36:15 Retry time exhausted - {"stack":["ClientProxy"]} +2ms
.env配置
REDIS_HOST=redis-service-1 REDIS_PORT=6379 REDIS_PASSWORD=abc123yes
问题原因分析
- Redis微服务客户端配置冲突:
ClientsModule.registerAsync中同时使用url和独立password参数时,NestJS Redis客户端无法正确合并配置,导致自定义地址未生效,客户端回退到默认的localhost连接。 - ConfigService全局有效性缺失:若根模块未全局导入
ConfigModule,其他模块的ConfigService无法读取.env变量,客户端只能使用默认配置。
解决步骤
步骤1:确保ConfigModule全局导入
在根模块(如AppModule)中全局导入ConfigModule,保证所有模块能读取环境变量:
import { Module } from '@nestjs/common'; import { ConfigModule } from '@nestjs/config'; import { RedisClientModule } from './redis-client/redis-client.module'; import { ExampleModule } from './example/example.module'; @Module({ imports: [ ConfigModule.forRoot({ isGlobal: true, envFilePath: '.env', }), RedisClientModule, ExampleModule, ], }) export class AppModule {}
步骤2:修正Redis发布客户端配置
将密码嵌入到Redis URL中,避免独立参数导致的配置失效:
ClientsModule.registerAsync([ { name:"PUBLISH_SERVICE", useFactory: async (configService: ConfigService) => { const host = configService.get<string>("REDIS_HOST"); const port = configService.get<string>("REDIS_PORT"); const password = configService.get<string>("REDIS_PASSWORD"); // 格式:redis://[密码@]主机:端口 const url = `redis://${password ? `${password}@` : ''}${host}:${port}`; return { transport: Transport.REDIS, options: { url: url, }, } }, inject: [ConfigService], }, ]),
步骤3:验证环境变量读取(可选)
在useFactory中添加日志,确认环境变量是否正确加载:
useFactory: async (configService: ConfigService) => { const host = configService.get<string>("REDIS_HOST"); console.log('加载的REDIS_HOST:', host); // 应输出redis-service-1 // 其他配置代码 }
步骤4:统一缓存服务配置(可选)
将缓存服务也改为URL格式,保持配置一致性:
CacheModule.register({ inject: [ConfigService], useFactory: async (configService: ConfigService) => { const host = configService.get<string>("REDIS_HOST"); const port = configService.get<string>("REDIS_PORT"); const password = configService.get<string>("REDIS_PASSWORD"); const url = `redis://${password ? `${password}@` : ''}${host}:${port}`; return { store: redisStore, url: url, }; }, }),
额外排查点
- 验证Docker网络连通性:在应用容器内执行
ping redis-service-1,确认能解析并连通Redis容器。 - 检查Redis容器配置:确保Redis容器未绑定
127.0.0.1(注释配置文件中bind 127.0.0.1行),且6379端口在Docker网络中开放。
内容的提问来源于stack exchange,提问作者rizesky
相关产品推荐
相关产品推荐

