关于微信公众号开发的“中控服务器”

access_token是公众号的全局唯一接口调用凭据,公众号调用各接口时都需使用access_token,现在微信提供了两个接口用于获取access_token:分别是GET https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=APPSECRET 和 POST https://api.weixin.qq.com/cgi-bin/stable_token ,以前只提供了前者。
官方建议公众号开发者使用中控服务器统一获取和刷新access_token,其他业务逻辑服务器所使用的access_token均来自于该中控服务器,不应该各自去刷新,否则容易造成冲突,导致access_token覆盖而影响业务。
获取 Stable Access token有两种调用模式: 1. 普通模式,access_token 有效期内重复调用该接口不会更新 access_token,绝大部分场景下使用该模式;2. 强制刷新模式,会导致上次获取的 access_token 失效,并返回新的 access_token;该接口调用频率限制为 1万次 每分钟,每天限制调用 50w 次。
是不是觉得Stable Access token就完全够用了,不用考虑什么中控服务器了,最佳实践是仍然建议构建中控服务器,这样一是可以减少对微信服务器的访问流量,减少网络意外的发生,二是可以突破访问频次的限制。
下面说下构建中控服务器的一些注意事项。
其实中控服务器不用设计得很复杂,主要是要实现以下几个功能:
1 对于授权用户提供access_token(其实还有ticket等)的缓存服务,缓存没有或者过期则需要从微信获取新的access_token,有效期内,直接返回缓存内容
2 考虑并发锁机制,缓存有效时没有影响,缓存无效时,要访问微信服务器去获取新的access_token,注意这时候一定要进行加锁操作,不然在极端情况下,会造成大量无效操作,甚至返回过期的错误的结果,第一个获得锁的可以访问微信去拿token,其他的得不到锁,等待一段时间后从缓存中获取更新内容。
关于加锁操作可以再稍微详细地讲下:以nodejs编程为例:假设这个中控服务器只跑了一个服务进程,那么可以不依赖redis,利用原子操作就可以进行加锁。

var wxifcache={
	updatetime:jargs.wxupdatetime,//最开始加载一个较早的时间,每次获取token时更新
	token:"",
	ticket:""
	}
var sab = new SharedArrayBuffer(1);
var wxlock = new Uint8Array(sab);
wxlock[0]=0;
...
if (!Atomics.add(ta, 0, 1)) {
	gettokenfromwx();
	Atomics.add(ta, 0, -1));
	res.json(wxifcache);
	};
else {
	Atomics.add(ta, 0, -1));
	setTimeout(() => { res.json(wxifcache); }, "5000");
	}; 

如果是跑在多个进程实例或者多个节点上,那么最好借助redis的分布式锁来进行加锁操作。单节点redis利用setnx加锁就够用了。

//setnx代码示例
const Redis = require('ioredis');
const redis = new Redis();

redis.setnx('wxlock',5, '1', (err, result) => {
	if (err) { console.error(err);} 
	else {
    	if (result) { 
    		gettokenfromwx();
    		res.json(wxifcache());
    		}
    	else {
    		setTimeout(() => { res.json(wxifcache()); }, "5000");
    		}
  }
  redis.quit();

多节点redis可以用redlock加锁。

//redlock代码示例
import Client from "ioredis";
import Redlock from "redlock";

const redisA = new Client({ host: "a.redis.example.com" });
const redisB = new Client({ host: "b.redis.example.com" });
const redisC = new Client({ host: "c.redis.example.com" });

const redlock = new Redlock(
  // You should have one client for each independent redis node
  // or cluster.
  [redisA, redisB, redisC],
  {
    // The expected clock drift; for more details see:
    // http://redis.io/topics/distlock
    driftFactor: 0.01, // multiplied by lock ttl to determine drift time

    // The max number of times Redlock will attempt to lock a resource
    // before erroring.
    retryCount: 0,//不尝试retry

    // the time in ms between attempts
    retryDelay: 200, // time in ms

    // the max time in ms randomly added to retries
    // to improve performance under high contention
    // see https://www.awsarchitectureblog.com/2015/03/backoff.html
    retryJitter: 200, // time in ms

    // The minimum remaining time on a lock before an extension is automatically
    // attempted with the `using` API.
    automaticExtensionThreshold: 500, // time in ms
  }
);
...
redlock.lock("wxlock", 5000, function (err, lock) {
  // we failed to lock the resource
  if (err) {
    // ...
    setTimeout(() => { res.json(wxifcache()); }, "5000");
  }

  // we have the lock
  else {
    // ...do something here...
        gettokenfromwx();
    	res.json(wxifcache());
    // unlock your resource when you are done
    lock.unlock(function (err) {
      // we weren't able to reach redis; your lock will eventually
      // expire, but you probably want to log this error
      console.error(err);
    });
  }
});

注意后面两段代码中的wxifcache()不是从内存中获得,而是从redis中获得,redis中的值由gettokenfromwx()设置。

  • 9
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值