在NestJs中使用TypeORM优雅地关闭数据库连接

3

在深入讨论问题之前,让我先向您解释一下我的应用程序的基本情况。

我的应用程序连接了DB(TypeOrm)Kafka(kafkajs)

我的应用程序是1个主题的消费者,它执行以下操作:

  1. 在回调处理程序中获取一些数据,并使用TypeORM实体将该数据放入一个表中
  2. 在某个类的单例实例中维护全局映射(来自点1的数据)

应用程序关闭时,我的任务是:

  1. 断开与Kafka连接的所有主题的消费者
  2. 遍历全局映射(点2)并将消息重新发送到某个主题
  3. 使用close方法断开DB连接

以下是一些代码片段,这可能有助于您了解我如何在NestJs中为服务器添加生命周期事件。

system.server.life.cycle.events.ts

@Injectable()
export class SystemServerLifeCycleEventsShared implements BeforeApplicationShutdown {
    constructor(@Inject(WINSTON_MODULE_PROVIDER) private readonly logger: Logger, private readonly someService: SomeService) {}

    async beforeApplicationShutdown(signal: string) {
        const [err] = await this.someService.handleAbruptEnding();

        if (err) this.logger.info(`beforeApplicationShutdown, error::: ${JSON.stringify(err)}`);
        this.logger.info(`beforeApplicationShutdown, signal ${signal}`);
    }
}

some.service.ts

export class SomeService {
    constructor(private readonly kafkaConnector: KafkaConnector, private readonly postgresConnector: PostgresConnector) {}
    
    public async handleAbruptEnding(): Promise<any> {
        
        await this.kafkaConnector.disconnectAllConsumers();
        for(READ_FROM_GLOBAL_STORE) { 
             await this.kafkaConnector.function.call.to.repark.the.message();
        }
        
        await this.postgresConnector.disconnectAllConnections();

        return true;
    }
}

postgres.connector.ts

export class PostgresConnector {
    private connectionManager: ConnectionManager; 
    constructor () {
        this.connectionManager = getConnectionManager();
    }

    public async disconnectAllConnections(): Promise<void[]> {
        const connectionClosePromises: Promise<void> = [];
        connectionManager.connections?.forEach((connection) => {
            if (connection.isConnected) connectionClosePromises.push(connection.close());
        });

        return Promise.all(connectionClosePromises);
    }
}

从TypeORM模块导入ConnectionManager& getConnectionManager()。

现在我遇到了一些异常/行为:

  1. 断开所有连接会抛出异常/错误,如引用所示:

    ERROR [TypeOrmModule] Cannot execute operation on "default" connection because connection is not yet established. 如果连接还没有建立,那么我的isConnected怎么会在if中为真。我无法得知这是如何可能的。在TypeORM中如何优雅地关闭连接。

  2. 我们是否真的需要在TypeORM中处理连接的关闭或者它在内部处理。

  3. 即使TypeORM在内部处理连接关闭,我们如何显式实现它。

  4. 是否有回调函数可以在连接正确断开时触发,以便我确信断开连接实际上发生在数据库中。

  5. 一些消息是在我按下CTRL + C后(模拟我的服务器进程的突然关闭)出现的,控制权回到终端。这意味着,在句柄返回到我的终端之后,某个线程正在返回(不知道如何处理此问题,因为如果您看一下,我的handleAbruptHandling是awaited的,并且我交叉检查了所有承诺都被正确地等待了。)

需要知道的一些事情:

  1. 我已正确添加了模块以创建服务器生命周期事件的钩子。
  2. 已正确注入几乎所有类中的对象。
  3. 没有从NEST获得任何DI问题,服务器正在正确启动。

请指点迷津,并告诉我如何在NestJs中使用typeorm api优雅地断开与数据库的连接以处理突然关闭的情况。

提前感谢您的帮助,祝编码愉快 :)

4个回答

3
有点晚了,但可能对某些人有帮助。
TypeOrmModuleOptions 中,您缺少参数 keepConnectionAlive,需要将其设置为 true,因为 TypeOrm 默认不会保持连接活动状态。我将 keepConnectionAlive 设置为 false,如果一个事务保持连接打开状态,我会关闭连接(typeorm 等待事务或其他进程完成后再关闭连接),这是我的实现方法。
import { Logger, Injectable, OnApplicationShutdown } from '@nestjs/common';
import { getConnectionManager } from 'typeorm';

@Injectable()
export class LifecyclesService implements OnApplicationShutdown {
  private readonly logger = new Logger();

  onApplicationShutdown(signal: string) {
    this.logger.warn('SIGNTERM: ', signal);
    this.closeDBConnection();
  }

  closeDBConnection() {
    const conn = getConnectionManager().get();

    if (conn.isConnected) {
      conn
        .close()
        .then(() => {
          this.logger.log('DB conn closed');
        })
        .catch((err: any) => {
          this.logger.error('Error clossing conn to DB, ', err);
        });
    } else {
      this.logger.log('DB conn already closed.');
    }
  }
}

如果方便和适用的话,这可以简化为await getConnectionManager().get()?.close(); - Umur Karagöz
2
已过时 - Ankur Verma
现在的解决方案是什么,@AnkurVerma? - Buddybear
根据我的研究,社区建议使用Typeorm与Datasource,并使用destroy方法关闭连接。以下是一些参考资料:https://github.com/typeorm/typeorm/issues/194和https://orkhan.gitbook.io/typeorm/docs/data-source-api。 - Ankur Verma
getConnectionManager() 已经被弃用。 - soroush

0
我发现了一些TypeORM文档,其中说“当调用close时,将断开连接(关闭池中的所有连接)”。
这里是链接:https://typeorm.biunav.com/en/connection.html#what-is-connection 我尝试使用export const AppDataSource = new DataSource({ // details })并导入它,然后执行。
import { AppDataSource } from "../../src/db/data-source";

function closeConnection() {
    console.log("Closing connection to db");
    // AppDataSource.close(); // said "deprecated - use destroy() instead"
    AppDataSource.destroy(); // hence I did this
}

export default closeConnection;

也许这会为某人节省一些时间


1
这与我们遵循的依赖注入流程相违背,如果您自己创建对象,则测试模块和模拟将会非常麻烦。 - Ankur Verma
@AnkurVerma 没错,我正在苦苦挣扎于 Nest 根模块中的 Typeform 模块应该如何导出! - Jora

0

由于 close() 方法已被弃用,您必须像这样使用 destroy() 方法

if(connection.isInitialized){
 //connection must be instance of Typeorm connection or DataSource
  await connection.destroy();
}

0
这个 @nestjs/typeorm 的实现可能会对你有所帮助。

https://github.com/nestjs/typeorm/blob/9453f64ed52493d73ccc7dc82e7e417546964f55/lib/typeorm-core.module.ts#L141

@Global()
@Module({})
export class TypeOrmCoreModule implements OnApplicationShutdown {

  async onApplicationShutdown(): Promise<void> {
    const dataSource = this.moduleRef.get<DataSource>(
      getDataSourceToken(this.options as DataSourceOptions) as Type<DataSource>,
    );
    try {
      if (dataSource && dataSource.isInitialized) {
        await dataSource.destroy();
      }
    } catch (e) {
      this.logger.error(e?.message);
    }
  }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接