You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

问题原因分析

  1. Redis微服务客户端配置冲突:ClientsModule.registerAsync中同时使用url和独立password参数时,NestJS Redis客户端无法正确合并配置,导致自定义地址未生效,客户端回退到默认的localhost连接。
  2. 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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.20 09:54:33