UNPKG

ts-api-core

Version:

Nodejs api framework core

226 lines 8.85 kB
"use strict"; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; Object.defineProperty(exports, "__esModule", { value: true }); exports.MysqlDistributedLock = void 0; const base_object_1 = require("../context/base.object"); const db_script_1 = require("../database/db.script"); const utils_1 = require("./utils"); var LockStateValue; (function (LockStateValue) { /** 未加锁 */ LockStateValue[LockStateValue["unlock"] = 0] = "unlock"; /** 已加锁 */ LockStateValue[LockStateValue["locked"] = 1] = "locked"; })(LockStateValue || (LockStateValue = {})); class LockState { constructor(data) { var _a; this.last = 0; // 最后更新锁时间 this.state = LockStateValue.unlock; // 0 未加锁, 1 已加锁 this.owner = ''; // 所有者 if (data) { try { const e = JSON.parse(data); this.last = e.last && !Number.isNaN(e.last) ? Number(e.last) : 0; this.state = utils_1.Utils.parseInt(String(e.state), LockStateValue.unlock); this.owner = (_a = e.owner) !== null && _a !== void 0 ? _a : ''; } catch (e) { } } } } class LockDataEntity { constructor() { this.key = ''; this.value = ''; } } class LockedState { constructor() { this.unlocked = false; } initTimer(doUpdateState) { if (this.timer) { return; } // 加锁成功, 开一个定时器定时更新锁,防止丢失 this.timer = setInterval(() => { if (!this.unlocked) { this.v.last = utils_1.Utils.currentTimeMillis(); doUpdateState(this.key, this.v); } }, 5000); } } /** * MySQL 分布式锁 */ class MysqlDistributedLock extends base_object_1.BaseObject { /** * MySQL 分布式锁 * @param context 上下文对象 * @param table 表名 * @param keyFieldName key 字段名称 * @param valueFieldName value 字段名称 * @param keyName 加锁的 key 名称 * @param timeout 锁超时 秒 (默认 60) * @param prefix 锁的前缀标识 */ // eslint-disable-next-line max-params constructor(context, table, keyFieldName, valueFieldName, timeout, prefix, flag) { var _a; super(context); this.table = table; this.keyFieldName = keyFieldName; this.valueFieldName = valueFieldName; this.state = {}; this.timeout = (timeout !== null && timeout !== void 0 ? timeout : 60) * 1000; this.prefixStr = prefix ? prefix + ':' : ''; this.owner = `${this.prefixStr}${(_a = utils_1.Utils.getIPAddress()) !== null && _a !== void 0 ? _a : '0.0.0.0'}${flag ? (':' + flag) : ''}`; } getStateValue(key) { const stateValue = this.state[key]; if (!stateValue) { const data = new LockedState(); data.key = key; this.state[key] = data; return data; } return stateValue; } initTimer(v, key) { const stateValue = this.getStateValue(key); stateValue.unlocked = false; stateValue.v = v; stateValue.initTimer((_key, _v) => { void this.setState(_key, _v); }); } /** * 加锁 * @param key KEY * @param waitTime 等待确认时间,默认 `500`ms * @returns */ lock(key, waitTime) { return __awaiter(this, void 0, void 0, function* () { try { let v = yield this.getState(key); if (v.state === LockStateValue.locked && utils_1.Utils.currentTimeMillis() - v.last <= this.timeout) { // 已经锁定了 if (v.owner === this.owner) { this.initTimer(v, key); return true; } return false; } v.owner = this.owner; v.state = LockStateValue.locked; v.last = utils_1.Utils.currentTimeMillis(); yield this.setState(key, v); yield utils_1.Utils.sleep(waitTime && waitTime >= 0 ? waitTime : 500); // 重新获取锁状态,防止并发冲突,确认是否加锁成功 v = yield this.getState(key); const isLocked = (v.owner === this.owner && v.state === LockStateValue.locked); if (isLocked) { this.initTimer(v, key); } return isLocked; } catch (e) { // eslint-disable-next-line @typescript-eslint/restrict-template-expressions this.logE(`lock err: ${e.message} (key: ${key})`); return false; } }); } /** 释放分布式锁 */ unlock(key, deleteLock) { return __awaiter(this, void 0, void 0, function* () { try { const stateValue = this.state[key]; if (stateValue) { stateValue.unlocked = true; if (stateValue.timer) { clearInterval(stateValue.timer); stateValue.timer = undefined; } delete this.state[key]; } const v = yield this.getState(key); if (v.state === LockStateValue.locked && utils_1.Utils.currentTimeMillis() - v.last < (this.timeout - 1000) && v.owner === this.owner) { if (deleteLock === true) { yield this.deleteLock(key); } else { v.state = 0; v.owner = ''; yield this.setState(key, v); } } return true; } catch (e) { return false; } }); } /** 判断是否已经被锁定 */ isLocked(key) { return __awaiter(this, void 0, void 0, function* () { try { const v = yield this.getState(key); return v.state === LockStateValue.locked && utils_1.Utils.currentTimeMillis() - v.last <= this.timeout; } catch (e) { return false; } }); } /** 获取锁状态 */ getState(key) { return __awaiter(this, void 0, void 0, function* () { const sql = `SELECT \`${this.keyFieldName}\`,\`${this.valueFieldName}\` FROM \`${this.table}\` WHERE \`${this.keyFieldName}\`=?`; const data = yield this.queryData(LockDataEntity, sql, [this.prefixStr + key]); return new LockState(data === null || data === void 0 ? void 0 : data.value); }); } /** 更新锁状态 */ setState(key, state) { return __awaiter(this, void 0, void 0, function* () { const value = new LockDataEntity(); value.key = this.prefixStr + key; value.value = JSON.stringify(state); const script = db_script_1.DBScript.insertUpdate(LockDataEntity, value, { table: this.table }); if (script) { try { yield this.executeSql(script.sql, script.params); return true; } catch (e) { this.logE(e.message); } } return false; }); } /** 删除锁 */ deleteLock(key) { return __awaiter(this, void 0, void 0, function* () { const sql = db_script_1.DBScript.deleteByIds(this.table, this.keyFieldName, this.prefixStr + key); return sql ? (yield this.executeSql(sql)) > 0 : false; }); } } exports.MysqlDistributedLock = MysqlDistributedLock; //# sourceMappingURL=mysql.distributed.lock.js.map