Поскольку вы читаете эту статью, я предполагаю, что у вас есть некоторые знания о многопоточности и асинхронных процессах. Я не буду вдаваться в подробности с ними. Их много, и я почти уверен, что найдутся гораздо лучшие объяснения, чем мои собственные.
Сегодня я покажу свою реализацию мьютекса в 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
});
//...
Вот и все. Это метод, который я использовал в своем коде, и он работал хорошо, как и ожидалось. Это плохая практика? Я не уверен. Если вы знаете лучшие способы или предложения, поделитесь ими в комментариях, чтобы все могли их увидеть.
Если вам, ребята, интересно, я поделюсь способом создания динамической блокировки мьютекса, где есть несколько блокировок мьютекса для разных процессов или одного и того же процесса с разными атрибутами.