Git Product home page Git Product logo

Comments (16)

Eric-Guo avatar Eric-Guo commented on June 14, 2024

首先恭喜站点流量那么大,然后我改了一下刷新token的算法,现在不是在同一个时间刷access_token了,而是会在过期前3分钟到过期前30秒的一个随机时间,我觉得这样就应该不会常发生这样的情况了,不过这种情况也只有靠你试验了。:smile:

还有现在master版本刚刚从Rest-Client迁移到http,要不你也帮忙测一下?

from wechat.

Eric-Guo avatar Eric-Guo commented on June 14, 2024

还有我在计划将token的存取从文件移到redis或者环境变量中,这样就可以支持Heroku或者你的这种大流量情况了。@jasper-fu

from wechat.

teddy1004 avatar teddy1004 commented on June 14, 2024

赞,那我直接升级到最新的去 👏

from wechat.

teddy1004 avatar teddy1004 commented on June 14, 2024

流量并不大,只是刚好那时一堆人在一起测试 😢

from wechat.

teddy1004 avatar teddy1004 commented on June 14, 2024

发现这样似乎并不能解决我说到的问题。我看了一下代码,不知道是不是理解的问题,发现那个主动检查 token 有效期的代码只会在 initialize 的时候检查,然后就不会调用到了。

lib/wechat/api.rb 里,有下面这段

module Wechat
  class Api < ApiBase
    API_BASE = 'https://api.weixin.qq.com/cgi-bin/'
    OAUTH2_BASE = 'https://api.weixin.qq.com/sns/oauth2/'

    def initialize(appid, secret, token_file, timeout, skip_verify_ssl, jsapi_ticket_file)
      @client = Client.new(API_BASE, timeout, skip_verify_ssl)
      @access_token = AccessToken.new(@client, appid, secret, token_file)
      @jsapi_ticket = JsapiTicket.new(@client, @access_token, jsapi_ticket_file)
    end

    # ...
  end
end

所以后面调用 Wechat.api.xxx 接口时候读到的 access_token 都是这里的那个实例变量,一个 worker 调用比如 media 这个 api 的时候发现 invalid token 的错误了,回去刷新 access_token。然后另一个 worker 调用会继续刷新 access_token,我理解的没错的话,多个 worker 的情况下,由于进程间是独立的,会导致 access_token 一直被刷新,糟糕的情况就是四个 worker 同时在工作,这时一起刷新 access_token 就出现我开始说的问题了。

from wechat.

Eric-Guo avatar Eric-Guo commented on June 14, 2024

不是在initialize里面检查,是在取token的时候,token在文件里面如果是对的就不会发生这个问题了,但是如果四个worker同时取,而且初始的时候token还是错的,那么是有可能死锁的,建议随便执行一条wechat命令预先刷新对就好了。

这部分的确应该写的更好一点。。

access_token.rb

def token
  begin
    @token_data ||= JSON.parse(File.read(token_file))
    created_at = token_data['created_at'].to_i
    expires_in = token_data['expires_in'].to_i
    fail 'token_data may be expired' if Time.now.to_i - created_at >= expires_in - @random_generator.rand(30..3 * 60)
  rescue
    refresh
  end
  valid_token(@token_data)
end

from wechat.

teddy1004 avatar teddy1004 commented on June 14, 2024

@Eric-Guo 问题在 lib/wechat.rb 这里

module Wechat
  def self.api
    @wechat_api ||= ApiLoader.with({})
  end
end

调用 Wechat.api 的时候,第一次会 initialize,然后就到内存里了,每个 worker 有一份 copy,后面调用 Wechat.api 读的内存里的 @wechat_api,所以后面的取 token 的时候也不会调用到 lib/wechat/access_token.rb 的 token 方法了。

我刚才测试了一下,手动修改 wechat_access_token,再去 console 里看 Wechat.api.access_token.token 数据还是修改之前的,就是 @wechat_api ||= ApiLoader.with({}) 后面都是读的内存里的数据,不会再从文件里读了。所以导致后面检查更新 token 的都不会被触发,token 被更新都是因为调用微信的 api 出现 invalid token 才更新了的。

我现在 fork 了一份用比较粗糙的方法改成

def self.api
  ApiLoader.with({})
end

每次都去读一次 initialize 一次

from wechat.

Eric-Guo avatar Eric-Guo commented on June 14, 2024

你是对的,我今晚看看怎么改比较好

from wechat.

Eric-Guo avatar Eric-Guo commented on June 14, 2024

想了想应该保持其他配置项缓存,token应该还是随用随取,如果是文件中,每次都应该读文件,因为token不是配置啊!

from wechat.

teddy1004 avatar teddy1004 commented on June 14, 2024

嗯,我也觉得应该是随用随取,不然不管是文件还是存 db 都会有上面这种问题。

from wechat.

jasper-fu avatar jasper-fu commented on June 14, 2024

@Eric-Guo 放到redis是靠谱的做法,我在自己的fork里面已经这么做了,非常稳定;

不过我现在用的是redis全局变量。。。没时间做重构呢~~

from wechat.

Eric-Guo avatar Eric-Guo commented on June 14, 2024

我重写了这部分代码,但是感觉每次api调用都读一次文件有点low,目前的实现是没有初始化才读,应该足够了?

def token
  read_token_from_file if access_token.blank?
  refresh if remain_life_seconds < @random_generator.rand(30..3 * 60)
  access_token
end

from wechat.

teddy1004 avatar teddy1004 commented on June 14, 2024

我觉得还是有问题啊,问题不是出在这部分的文件这里,我改的之所以每次都要读文件是考虑到这种情况:

A / B worker 的内存中各有一个不一样的 token,假设 B worker 的是正确的。
这时两个 worker 同时接收到请求,然后如果 A worker 快一步然后请求不对,刷新了 token,这时 B worker 用自己的 token 去请求也得刷新 token,这时如果 A 又拿着自己的新 token 去请求就又会刷新 token,B 那边的又用不了了。。。我们昨天测试就是碰到这种情况,因为我们的公众号会和用户的互动比较频繁,所以用到 token 也比较频繁,这种情况触发的概率还是蛮大的,尤其是 worker 增加到更多的情况。

而上面的这部分改动我觉得并没有避免这种情况,还是会出现每个 worker 拿着自己内存里有可能是错误的 token 去请求。

from wechat.

Eric-Guo avatar Eric-Guo commented on June 14, 2024

恩,你说的没错,的确只能每次读文件,我写了注释。

from wechat.

teddy1004 avatar teddy1004 commented on June 14, 2024

坐等支持配置 access_token 存储的版本 🙊

from wechat.

Eric-Guo avatar Eric-Guo commented on June 14, 2024

由于http的版本不能upload files,暂时先发布一个0.6.9的版本,包含了这个token refresh bug的修正,这样0.6系列也算跑在最新版本的代码上(除了移除了http支持)。

from wechat.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.