node.js有没有类似给方法加锁的东西?
发布于 8 年前 作者 hvick 12641 次浏览 来自 问答

举个例子,A.js里有个a方法,B.js和C.js都调用A里面的a方法,单线程执行,假设现在B先调用a,但是a还没执行完,然后切换到C要开始调用a,如何保证只有在B执行完a后,C才能调用a

39 回复

你学java的吧,实际上,本质上,要锁的东西是资源,而不是方法

呃这个怎么会想到要加锁呢?明显应该是执行顺序流程控制的问题啊

@captainblue2013 对,我原来是java的,现在学node,在弄邮箱和微信,access_token想全局缓存,即第一次http请求获取后,在缓存失效前全部都用缓存,现在第一次还没请求完,第二次请求都发出去了,然后access_token被刷新了,直接地想到锁住获取access_token的方法,思维还没转过来,有什么好的办法?

@klausgao 做http请求的access_token全局缓存,习惯以前的同步思维了。

同一时刻b、c方法中的a一定不会都在执行吧?

@guojinlong B调用a执行到一半,然后线程切换到C调用执行a,时间上来说不可能同时执行,但是B和C都调用a了,想在直到B调用执行完a,B以外的js才能调用a

我理解一下,可能说的不对。欢迎指出。 你说的线程应该主线程吧?那么主线程调用的方法中如果你没有写yield、await。那么他们一定是等方法执行完了才会切换的。 如果你在a()中写了yield,那么是否是写法不恰当?

@guojinlong 严格来说不能叫切换,应该是执行下一个回调。

@guojinlong 没有用yield、await哈,只是普通代码,a方法就是一个判断缓存过期,过期就发起http请求更新缓存的方法,只是想要一个单例缓存

@hvick 那node是单线程执行,应该不会遇到你说的问题吧。。。

@guojinlong 因为a方法里有http请求,比如B调用a的时候,缓存过期了,然后发起http请求,还没执行回调,然后到C调用a,因为B的http回调还没执行,缓存还没更新,所以C也发起http请求,接着B的http请求回调执行了,把缓存更新了一次,【然后C的http请求回调又把缓存更新了】。我的目的是缓存过期只需更新一次。

我想到一个简单方法,就是你在你的c方法中http请求不要用回调方式。就是同步的写法。这样你就不用判断了。哈哈。。。

@hvick 或者你回调函数中,也先判断一下缓存是否有值,如果有值你就舍弃当前请求的值,或者你自己选择用哪个缓存。这样不知道行不行?

js本身就是单线程的,A方法没执行完,是不会执行B方法的,除非有异步ajax调用

@guojinlong 回调里判断,貌似可行,我试试,谢谢

@151263 目前就是因为a里有http的请求回调,所以有这个问题

@hvick B外面放一个变量,是否正在执行B方法isRunning, C检测只有isRunning变量为false时,才能继续执行,否则直接return

@151263 不建议使用判断方式。其实node提倡就是不要用加锁什么之类的判断。当然这种提倡是我自己觉着的。。。

@151263 将异步回调换成同步,或者使用纤程阻塞不知道行不行

单线程 不需要加锁呀。。。

echo 单线程 不需要加锁呀。。。

nodejs 是单线程的,多进程的,准确的说提供给coder的是只能单线程编程,但是nodejs内部的实现是多线程的。 其实应该多考虑使用回调或者多进程。

@yuanbu2 利用回调实现基于http请求的全局单例缓存,还没想到好的解决方法,看来只能参考下现在流行的node微信或者邮箱开发框架了,看看别人怎么解决的。

@hvick 恩 我也接触node不久 也就一年。 这方面有什么好的解决办法分享一下啊。

可以在结构中使用一个有限自动状态机,考虑loading,success,failed 完整请求过程中在这三个状态之间转换 From Noder

这个问题貌似java里也不好做吧?假如有多台服务器怎么锁?锁不住吧?我觉得应该吧accesstoken全局缓存,起服务去刷新accesstoken

@yuanbu2 我才几个月,才刚学习基础模块和玩了下express

@wssgcg1213 状态机得要能在多个js之间共享也是个问题

@xupeng086 java一台的话就好弄,写个单例就可以了,多台的话个人做法选一台出来专门提供这个全局缓存的服务。现在我用node弄全局缓存就是卡在缓存要刷新的时候要使用access_token的js(2个以上)发起http请求的回调把access_token多次刷新了。

@hvick 那就弄一个外部的缓存 比如放在redis里共享 From Noder

redis里的getset 可以做到

@wssgcg1213 用redis,那又得重新考虑了

@flex1988 控制redis更新缓存,感觉上也不能完全实现

没有代码,不好判断。个人估计:你有使用循环,而且还是把 IO操作的代码,直接放进循环了。这里建议你 使用JS的闭包 匿名函数 在 循环中调用。

一般io接口才是 异步callback形式的,控制流程就好了

@hvick 大家都被楼主你问的问题带到坑里了,其实你就是想问如何实现(微信)access token 的中控服务器吧。微信公众号开发者文档已经写得很清楚了:

1、为了保密appsecrect,第三方需要一个access_token获取和刷新的中控服务器。而其他业务逻辑服务器所使用的access_token均来自于该中控服务器,不应该各自去刷新,否则会造成access_token覆盖而影响业务; 2、目前access_token的有效期通过返回的expire_in来传达,目前是7200秒之内的值。中控服务器需要根据这个有效时间提前去刷新新access_token。在刷新过程中,中控服务器对外输出的依然是老access_token,此时公众平台后台会保证在刷新短时间内,新老access_token都可用,这保证了第三方业务的平滑过渡; 3、access_token的有效时间可能会在未来有调整,所以中控服务器不仅需要内部定时主动刷新,还需要提供被动刷新access_token的接口,这样便于业务服务器在API调用获知access_token已超时的情况下,可以触发access_token的刷新流程。

你可以设计一个全局的获取 access token 的方法,该方法异步返回 access token。该方法维护一个全局的 access token(一般需要持久化),在调用方请求 access token 时,按照微信开发文档,你需要提前判断 access token 是否快过期了,并且按需到微信获取新的 access token,更新本地全局的 access token。当然你也可以定时去获取而不仅仅在调用方请求时才判断,看你需要。最后,因为文档说了,微信会保留旧的 access token 有效一段时间,所以通常在发生更新 access token 流程时,一般不会产生问题,当然调用方还是需要处理 access token 过期无效的情况,比如再调一次获取 access token 的方法并重试。

function getAccessToken(callback) {
  ...
}

我们项目中用的,不知道对你有没有帮助:


getAccessToken = async function () {
    //判断access_token缓存是否存在
    let access_token = await S('ClientAccessToken');
    if(isEmpty(access_token)){
        //请求AuthCenter获取access_token
        let res = await requestp({uri: `${C('auth_center.url')}/OAuth/OAuth/token`, method: 'POST'}).form({
            client_id: C('auth_center.client_id'),
            client_secret: C('auth_center.client_secret')
        });
        let timeout = 3600;
        try{
            let rdata = JSON.parse(res);
            if(rdata.status == 1){
                access_token = rdata.data.access_token;
                timeout = Math.ceil(((parseInt(rdata.data.expire) || 0) - (Date.now()))/ 1000) || -1;
            } else {
                return E('request access_token error');
            }
        }catch (e){
            return E('request access_token error');
        }
        access_token && S('ClientAccessToken', access_token, timeout);
    }
    return access_token;
}

@finian 微信多次调用一般情况下是不会刷新access_token,这个只能解决微信的access_token。不过我的问题不是出现在微信开发上,而是腾讯企业邮箱开发上,腾讯企业邮箱的access_token不会保留旧的,每次刷新都不一样。

@richenlin 楼主新手async/await还没开始看哈,刚接触promise还没研究,先谢谢分享了~

回到顶部