Поскольку вы читаете эту статью, я предполагаю, что у вас есть некоторые знания о многопоточности и асинхронных процессах. Я не буду вдаваться в подробности с ними. Их много, и я почти уверен, что найдутся гораздо лучшие объяснения, чем мои собственные.

Сегодня я покажу свою реализацию мьютекса в NestJs, используя эту библиотеку async-mutex. Проверьте библиотеку, так как она дает отличное объяснение мьютекса. Вот обзор того, что такое мьютекс из библиотеки.

Термин «мьютекс» обычно относится к структуре данных, используемой для синхронизации параллельных процессов, выполняющихся в разных потоках.

Вы можете задаться вопросом, где я когда-либо буду их использовать. Раньше я задавался тем же вопросом, когда начинал, но довольно скоро я узнал. В моем случае это было нужно для проблемы состояния гонки. Что такое состояние гонки? Это серия действий, которые включают в себя «проверить и действовать». Проверьте значение и примите меры в зависимости от значения. Это кажется нормальным и безобидным, но представьте себе два потока или асинхронных процесса, поступающих одновременно. На какой из них код будет действовать в первую очередь? Например,

Электронный кошелек под названием «А», в котором 30 монет. Электронный кошелек под названием «B», в котором есть 5 монет. «А» хочет передать 20 монет «Б» и передать их. Под капотом запрос пришел, как обычно, наш код проверял, достаточно ли у него монет, т. е. проверял, извлекал их из кошелька «А», добавлял их в кошелек «Б» и обновлял балансы, т. е. действие. Достаточно просто. Но попутно по какой-то причине, такой как двойное касание или злонамеренная атака, например, одновременное выполнение большого количества запросов или слишком много запросов, это может вызвать проблемы.

Проблема: два потока или асинхронные процессы вошли одновременно (в течение миллисекунд).

Причина: когда оба процесса вошли одновременно (в течение миллисекунд). Они проходят проверку одновременно, и у «А» есть 30 монет для обоих процессов. Таким образом, оба предпринимают действия по извлечению, добавлению и обновлению баланса. Где второй процесс должен был быть отменен из-за нехватки монет.

Результат: «Б» получит 40 монет и извлечет только 20 монет из «А». (Может иметь другой результат, но это пример)

Как нам это остановить? Ну, вот тут-то и появляется мьютекс. Мьютекс помогает синхронизировать процессы, гарантируя, что что-то вроде приведенного выше примера не произойдет, блокируя первый процесс и освобождая его, когда это будет сделано, для входа второго.

Хватит болтать/объяснять и давайте реализовывать в NestJs.

npm install async-mutex

nest g module MutexModule
nest g service MutexService

В модуле мьютекса

import { Global, Module } from '@nestjs/common';
import { MutexService } from './mutex.service';

@Global() // to be able to use from every module
@Module({
  providers: [MutexService],
  exports: [MutexService],
})
export class MutexModule {}

В службе мьютекса

import { Injectable } from '@nestjs/common';
import { Mutex } from 'async-mutex';

@Injectable()
export class MutexService {
  private mutex = new Mutex();

  async runLocked<T>(callback: () => Promise<T>): Promise<T> {
    // acquire lock
    const release = await this.mutex.acquire();
    try {
      return await callback();
    } catch (e) {
      // handle exception
    } finally {
      // release the lock in the end
      release();
    }
  }
}

Использование службы мьютекса

//...
 private mutexService: MutexService, // Inject the service
//...
 await this.mutexService.runLocked(async () => {
   //... check and act
 });
//...

Вот и все. Это метод, который я использовал в своем коде, и он работал хорошо, как и ожидалось. Это плохая практика? Я не уверен. Если вы знаете лучшие способы или предложения, поделитесь ими в комментариях, чтобы все могли их увидеть.

Если вам, ребята, интересно, я поделюсь способом создания динамической блокировки мьютекса, где есть несколько блокировок мьютекса для разных процессов или одного и того же процесса с разными атрибутами.