在处理微服务架构或分布式系统时,数据库连接的稳定性是一个关键问题。最近,我在使用NestJS框架和Milvus向量数据库时,遇到一个有趣的问题:当Milvus服务(通过Docker容器运行)停止时,尝试创建Milvus客户端的操作无法正确捕获异常。这篇博客将详细探讨这个问题,并提供一个实际解决方案。

问题描述

首先,让我们回顾一下最初的代码:

import { Injectable } from '@nestjs/common';
import { MilvusClient } from '@zilliz/milvus2-sdk-node';

@Injectable()
export class EmbeddingService {
  private client: MilvusClient;
  
  constructor() {
    try {
      this.client = new MilvusClient('localhost:19530');
    } catch (error) {
      console.error('Failed to connect to Milvus:', error);
    }
  }
}

当Milvus服务停止后,new MilvusClient抛出异常,但这个异常并未被catch块捕获,导致程序在初始化阶段就崩溃。

问题分析

根据评论中的建议,这并不是NestJS或Node.js的问题,而是Milvus客户端构造函数的设计缺陷。Milvus客户端的constructor没有提供错误处理的接口,这是一个明显的设计失误。

解决方案

为了解决这个问题,我们需要在实例化Milvus客户端之前进行预连接检查。这里提供一个改进的方案:

import { Injectable, OnModuleInit } from '@nestjs/common';
import { MilvusClient } from '@zilliz/milvus2-sdk-node';
import { setTimeout } from 'timers/promises';

@Injectable()
export class EmbeddingService implements OnModuleInit {
  private client: MilvusClient | null = null;
  
  async onModuleInit() {
    try {
      // 尝试连接到Milvus服务
      await this.tryConnect();
    } catch (error) {
      console.error('Failed to connect to Milvus:', error);
      // 根据具体需求,可以设置重试逻辑或直接退出
      // await this.retry();
      // or
      // process.exit(1);
    }
  }

  private async tryConnect(): Promise<void> {
    const timeout = 10000; // 10秒超时
    try {
      await setTimeout(timeout);
      this.client = new MilvusClient('localhost:19530');
    } catch (error) {
      throw new Error('Milvus service is not available');
    }
  }

  // 可选的重试逻辑
  private async retry() {
    const maxRetries = 3;
    for (let attempt = 1; attempt <= maxRetries; attempt++) {
      console.log(`Retry attempt ${attempt}...`);
      try {
        await this.tryConnect();
        console.log('Successfully connected after retry');
        return;
      } catch (error) {
        if (attempt === maxRetries) {
          throw error;
        }
        await setTimeout(5000); // 重试前等待5秒
      }
    }
  }

  // 其他方法可以使用this.client
}

解释

  1. 预连接检查: 使用setTimeout来等待一定时间,看看Milvus服务是否可用。
  2. 错误处理: 通过try-catch块来捕获超时或连接失败的错误。
  3. 重试机制: 如果第一次连接失败,可以设置一个重试逻辑,以尝试重新连接Milvus服务。

通过这种方式,我们不仅能够捕获Milvus服务不可用的异常,还可以提供一个更稳定的服务连接方案。

结论

处理分布式系统中的异常需要考虑到各种可能的情况。通过上述示例,我们不仅解决了Milvus客户端的初始化问题,还增强了系统的健壮性和可靠性。在实际应用中,根据具体需求,你可能需要调整超时时间、重试次数等参数,以达到最佳的平衡点。

Logo

火山引擎开发者社区是火山引擎打造的AI技术生态平台,聚焦Agent与大模型开发,提供豆包系列模型(图像/视频/视觉)、智能分析与会话工具,并配套评测集、动手实验室及行业案例库。社区通过技术沙龙、挑战赛等活动促进开发者成长,新用户可领50万Tokens权益,助力构建智能应用。

更多推荐