ts-api-core
Version:
Nodejs api framework core
226 lines • 8.85 kB
JavaScript
"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