美文网首页
优化并发获取微信token

优化并发获取微信token

作者: linjiajiam | 来源:发表于2018-12-06 17:46 被阅读0次
背景:
  • 最近项目使用到了微信开发,获取微信token是必须可少的步骤。Redis是存储token的最佳方案,所以把获取token的方法写在了一个公用的方法内,如下:
public class WeChatUtil {

    public static String APPID;

    public static String SECRET;

    private static String TOKEN_URL = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=%s&secret=%s";

    @Value("${wechat.appid}")
    public void setAPPID(String APPID) {
        WeChatUtil.APPID = APPID;
    }

    @Value("${wechat.secret}")
    public void setSECRET(String SECRET) {
        WeChatUtil.SECRET = SECRET;
    }

    public static long EXPIRETIME = 7100;

   
    /**
     * 获取token
     * @return
     */
    public static String getWeixinToken(){
        //从redis中获取token
        String token = (String)RedisUtil.get("vbpWxToken");
        //token不存在或者已经过期,重新调用微信接口获取token
        if(StringUtils.isBlank(token)){
            try {
                String response = HttpUtil.requestGet(String.format(TOKEN_URL, APPID, SECRET), null);
                JSONObject jsonobject = JSONObject.parseObject(response);
                //如果取得token,那么就返回
                if(jsonobject.getString("access_token") != null){
                    token = jsonobject.getString("access_token");
                    //将token写入redis
                    RedisUtil.set("vbpWxToken", token, EXPIRETIME);
                }
                System.out.println(DateUtil.getCurrentFormatDate(null) +  ",获取新token:" + token);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        return token;
    }
}
  • 这个写法初看没问题,但是我们遇到一个BUG是当程序运行一段时间后,偶尔会出现redis中的token校验失败的问题。手工清除redis中的token后获取,可以正常运行一段时间,但是过一段时间问题又出现。思来想去,后来才发现有可能存在如下问题:
  • 举一种情况。假如token已经失效,在redis中已经不存在。此时两个线程1、2同时先后调用这个方法,线程1先调用了微信接口获取token,在线程1还没写入redis的时候,线程2也调用了微信的接口获取redis,然后会出现以下情况。


    image.png
解决方案:
1. 模拟单例双重检查
  • 这个解决方式模拟了单例中的双重检查,似乎没有问题。但是在分布式负载均衡的部署中不能用。因为synchronized是内存同步,分布式环境中每台机器的内存都是独立的。
  • 但是如果是在单机模式下,这个是方法是能用的。
    public static String getWeixinToken(){
        String token = (String)RedisUtil.get("vbpWxToken");
        //第一重检查
        if(StringUtils.isBlank(token)){
            //加锁
            synchronized (token){
                //第二重检查
                if(StringUtils.isBlank(token)){
                    try {
                        String response = HttpUtil.requestGet(String.format(TOKEN_URL, APPID, SECRET), null);
                        JSONObject jsonobject = JSONObject.parseObject(response);
                        //如果取得token,那么就返回
                        if(jsonobject.getString("access_token") != null){
                            token = jsonobject.getString("access_token");
                            RedisUtil.set("vbpWxToken", token, EXPIRETIME);
                        }
                        System.out.println(DateUtil.getCurrentFormatDate(null) +  ",获取新token:" + token);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            }
        }

        return token;
    }
2. 独立定时JOB刷新token
  • 我们最后采用的是一个独立的定时任务去刷新token,这种方法最简单,也最高效了。
    定时任务代码如下:
@EnableScheduling
@Component
public class WeChatSchedule {

    private static String TOKEN_URL = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=%s&secret=%s";

    /**
     * 刷新微信token
     * 第一次延迟1秒执行,当执行完后6500秒再执行
     */
    @Scheduled(initialDelay = 1000, fixedDelay = 6500*1000 )
    public void setWeixinToken(){
            try {
                String response = HttpUtil.requestGet(String.format(TOKEN_URL, APPID, SECRET), null);
                JSONObject jsonobject = JSONObject.parseObject(response);
                //如果取得token,那么就写入redis
                if (jsonobject.getString("access_token") != null) {
                    String token = jsonobject.getString("access_token");
                    RedisUtil.set("vbpWxToken", token, EXPIRETIME);
                    System.out.println(DateUtil.getCurrentFormatDate("yyyy-MM-dd HH:mm:ss") + "=====获取token成功");
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
    }
}
  • 获取token的方法变为单纯的读取redis
    public static String getWeixinToken(){
        String token = (String)RedisUtil.get("vbpWxToken");
        return token;
    }
  • 注意这个定时任务是写在一个单独的job工程中,单机模式部署。

相关文章

网友评论

      本文标题:优化并发获取微信token

      本文链接:https://www.haomeiwen.com/subject/jxgocqtx.html