1.0.0 • Published 3 years ago

@qtk/key-mutex v1.0.0

Weekly downloads
2
License
ISC
Repository
github
Last release
3 years ago

@Qtk/Key-Mutex

@qtk/key-mutex基于process-key-mutex@qtk/tcp-framework封装,对同一个key绑定的函数执行进行加锁操作.支持进程级别网络级别的锁操作

  • 多个方法绑定同个key的话,同个时间只会执行一个,其他的按照绑定时间顺延执行.
  • 实现方式: Promise & 链表

使用方式:

//进程级别锁
const Mutex = require('@qtk/key-mutex');
const processMutex = new Mutex({ lockerType: "process" });
console.log(
    await processMutex.lock('mutexKey', async() => {
        await new Promise(resolve => setTimeout(() => resolve(), 1000));
        return 'hi';
    })
) 

//网络级别锁

//服务端
const Mutex = require('@qtk/key-mutex');
const server = new Mutex.NetworkServer({
    host: "127.0.0.1",
    port: 9999
});

//客户端
const Mutex = require('@qtk/key-mutex');
const networkMutex = new Mutex({ 
    lockerType: "network",
    serverHost: "127.0.0.1",
    serverPort: 9999
});
console.log(
    await networkMutex.lock('mutexKey', async() => {
        await new Promise(resolve => setTimeout(() => resolve(), 1000));
        return 'hi';
    })
) 
// hi

参数说明

构造函数

constructor({ lockerType, serverHost, serverPort, socketTimeout = 30 })
字段含义是否必填枚举值默认值
lockerType锁类型可选processnetworkprocess
serverHost网络锁服务端ip可选localhost
serverPort网络锁服务端portlockerType=network时必填
socketTimeout连接超时时间(单位秒)可选30

lock函数

lock(key, task, timeout)
字段含义是否必填枚举值默认值
key操作的key必填
task获得锁后的处理函数必填
timeout执行超时时间(含等待锁时间),单位秒可选30

网络锁服务端构造函数

constructor({ host, port, socketTimeout = 30, logDir })
字段含义是否必填枚举值默认值
host网络锁服务端ip可选localhost
port网络锁服务端portlockerType=network时必填
socketTimeout连接超时时间(单位秒)可选30
logDir日志目录可选默认为标准终端输出

测试样例:

const assert = require('assert');
let Locker = require('@qtk/key-mutex');
let mutex = undefined;
describe('#process', function () {
    before(function() {
        mutex = new Locker({
            lockerType: "process"
        })
    })

    it('[promise.all]', async function() {
        this.timeout(100000000);
        let [l1, l2] = await Promise.all([
            func('func1', 200),
            func('func2', 100),
        ])
        assert(l1 == 'func1' && l2 == 'func2', `promise.all failed`);
    });

    it('promise && setTimeout', async function() {
        this.timeout(100000000);
        let [l1, l2, l3, l4] = await new Promise(resolve => {
            let result = [];
            setTimeout(async () => {
                func('func3', 2000).then(l => result.push(l));
                func('func4', 1000).then(l => result.push(l));
            }, 1000)
            func('func1', 2000).then(l => result.push(l));
            func('func2', 1000).then(l => result.push(l));
            setInterval(() => {
                if (result.length == 4) resolve(result)
            }, 1000)
        })
        assert(l1 == 'func1' && l2 == 'func2' && l3 == 'func3' && l4 == 'func4', `promise && setTimeout failed`);
    });

    it('immediately && setTimeout', async function() {
        this.timeout(100000000);
        let [l1, l2] = await new Promise(async(resolve) => {
            let result = [];
            result.push(await func('func1', 4000));
            setTimeout(async () => {
                func('func2', 2000).then(l => result.push(l));
            }, 1000)
            setInterval(() => {
                if (result.length == 2) resolve(result)
            }, 1000)
        })
        assert(l1 == 'func1' && l2 == 'func2', `immediately && setTimeout failed`);
    });

    it('[one of throw error]', async function() {
        this.timeout(100000000);
        let [l1, l2] = await new Promise(resolve => {
            let result = [];
            func('func1', 2000).then(l => result.push(l));
            funcErr('func2', 1000).then(l => result.push(l)).catch(error => result.push(error));
            setInterval(() => {
                if (result.length == 2) resolve(result)
            }, 1000)
        })
        assert(l1 == 'func1' && l2 instanceof Error, `one of throw error failed`);
    });

    it('[promise.all and one of timeout]', async function () {
        this.timeout(100000000);

        let startAt = Date.now();
        let [l1, l2] = await Promise.all([
            (
                () => {
                    return new Promise(resolve => {
                        func('func1', 5000, 1)
                            .then(_ => resolve(_))
                            .catch(error => resolve(error))
                    })
                }
            )(),
            func('func2', 1000),
        ]);
        let endAt  = Date.now();
        assert(l1 instanceof Error && l2 == 'func2' && endAt - startAt < 2100, `promise.all and one of server timeout`);
    });
})

let func = async (label, delay, timeout) => {
    return mutex.lock('func', async () => {
        // console.log(`execute func: ${label}`);
        await new Promise(resolve => setTimeout(() => resolve(), delay));
        // console.log(`func: ${label} done`);
        return label;
    }, timeout)
}

let funcErr = async (label, delay) => {
    return mutex.lock('func', async() => {
        //console.log(`execute func: ${label}`);
        throw new Error('error');
    })
}