redLock与redis实现并发控制

需求背景:

多个并发,下一个请求依赖上一个请求的处理结果,因此需求让多个并发处于队列中,依次执行。

使用技术栈:

egg.js + nodejs包redlock + redis

封装方法:

 /**
     * 自动并发锁下执行任务
     * @param this
     * @param fn 执行任务方法
     * @param resources redis keys
     * @param mySetting redlock相关配置
     * @param duration 锁(redis key)过期时间,该方法会自动延长key过期时间,所以一般不用设置
     * @returns 任务执行结果
     */
    async runWithAutoLock(fn, resources, mySetting = {}, duration = 5000) {
        // const duration = 5000; // redis key 过期时间,调lock.release()会删除key,所以这个时间长一点也没关系
        let error;
        let lock;
        let timeout;
        let extension;
        resources = Array.isArray(resources) ? resources : [resources];
        const settings = Object.assign(Object.assign({}, this.app.redlock.settings), mySetting);
        if (typeof fn !== 'function') {
            throw new Error('INVARIANT: fn is not a function.');
        }
        if (settings.automaticExtensionThreshold > duration - 100) {
            throw new Error('A lock `duration` must be at least 100ms greater than the `automaticExtensionThreshold` setting.');
        }
        const queue = () => {
            timeout = setTimeout(() => (extension = extend()), lock.expiration - Date.now() - settings.automaticExtensionThreshold);
        };
        const extend = async () => {
            // timeout = undefined;
            try {
                lock = await lock.extend(duration);
                queue();
            }
            catch (err) {
                // 可能会触发死循环
                // if (lock.expiration > Date.now()) {
                //     return (extension = extend());
                // }
                this.logger.error(err);
            }
        };
        try {
            lock = await this.app.redlock.acquire(resources, duration, settings);
            queue();
            try {
                return await fn();
            }
            catch (err) {
                error = err;
            }
            finally {
                // Clean up the timer.
                if (timeout !== undefined && timeout !== null) {
                    clearTimeout(timeout);
                    timeout = undefined;
                }
                // Wait for an in-flight extension to finish.
                if (extension) {
                    await extension.catch(() => {
                        // An error here doesn't matter at all, because the routine has
                        // already completed, and a release will be attempted regardless. The
                        // only reason for waiting here is to prevent possible contention
                        // between the extension and release.
                    });
                }
                // 释放锁也可能会报错
                let i = 0;
                while (i++ < 5) {
                    // 最多重试5次
                    try {
                        await lock.release();
                        break;
                    }
                    catch (e) {
                        this.logger.error(`lock release failed, keys: ${JSON.stringify(resources)}, `, e);
                    }
                    await this.sleep(200);
                }
            }
        }
        catch (e) {
            // 获取锁失败会抛错
            // this.logger.warn('require lock failed:', e);
        }
        if (error !== undefined) {
            throw error;
        }
    }

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值