缓存微信accesstoken实现

一、背景

微信公众号调用接口的accesstoken,失效期为7200秒,所以我们需要把它缓存起来,不用每次都去获取新的。

二、方案

将accesstoken保存再redis中,设置失效时间,并在代码中检测是否有缓存值,没有则去获取并更新redis。但会出现一个问题,在多进程的模式下,会出现并发去获取accesstoken的场景,这样会导致前边的值失效,但前边的值已经缓存到redis中,后边的进程发现redis中有值,则不去更新redis,会造成之后的请求accesstoken一直失效的问题。解决方案采用的是redis的分布式锁来实现进程间并发的问题。


三、代码

package com.wuage.wechat.service.center.utils;

import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.stereotype.Component;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.support.WebApplicationObjectSupport;

import com.alibaba.fastjson.JSON;
import com.wuage.wechat.model.WechatConfig;
import com.wuage.wechat.model.WechatMessageResult;
import com.wuage.wechat.service.center.redis.MessageJedisPool;

import redis.clients.jedis.Jedis;

/**
 * 类SingleWechatAccessToken.java的实现描述:TODO 类实现描述
 * 
 * @author wangpeng 2017年1月18日 下午3:39:47
 */
@Component
public class WechatAccessToken extends WebApplicationObjectSupport implements InitializingBean {

    private Logger            logger                    = LoggerFactory.getLogger(WechatAccessToken.class);
    private MessageJedisPool  messageJedisPool;
    private volatile String   accessToken               = "";
    private final String      WECHAT_ACCESSTOKEN_KEY    = PropertiesUtil.getParameter("wechat.accesstoken.key");
    private final int         WECHAT_ACCESSTOKEN_EXPIRE = 6000;                                                  // key超时时间(秒)
    private static final long LOCK_TIMEOUT              = 3000;
    private static final long SLEEP_TIME                = 1000;
    private long              lock_timeout              = 0;
    private String            LOCK_KEY                  = PropertiesUtil.getParameter("wechat.accesstoken.lock");

    /**
     * @return the accessToken
     */
    public String getAccessToken() {
        checkAndResetAccessToken();
        return accessToken;
    }

    /**
     * @Description:检查并重新获取accesstoken
     * @Author:wangpeng
     * @Date:2017年1月19日
     * @Return:void
     */
    private void checkAndResetAccessToken() {
        Jedis jedis = messageJedisPool.getJedis();
        try {
            if (jedis != null) {
                String token = jedis.get(WECHAT_ACCESSTOKEN_KEY);
                if (StringUtils.isBlank(token)) {
                    if (getLock().longValue() == 1) {
                        resetAccessToken();
                        releaseLock();
                    }
                } else {
                    accessToken = token;
                }
            }
        } catch (Exception e) {
            logger.error("get wechat access token throw exception :{}", e);
        } finally {
            if (jedis != null) {
                jedis.close();
            }
        }
    }

    /**
     * @Description:重新获取accesstoken
     * @Author:wangpeng
     * @Date:2017年1月19日
     * @Return:void
     */
    public void resetAccessToken() {
        logger.info("get wechat access token before,token is " + accessToken);
        StringBuilder sb = new StringBuilder();
        sb.append(WechatConfig.ACCESS_TOKEN_URL);
        sb.append("&appid=");
        sb.append(PropertiesUtil.getParameter("WX_APPID"));
        sb.append("&secret=");
        sb.append(PropertiesUtil.getParameter("WX_APPSECRET"));
        // sb.append("546c1511fa8182c6c65db70197f2ebb6");
        String result = HttpClientUtil.HttpGetService(sb.toString());
        WechatMessageResult wmr = JSON.parseObject(result, WechatMessageResult.class);
        if (wmr != null) {
            accessToken = wmr.getAccess_token();
            setAccessTokenOnRedis(wmr.getAccess_token());
            logger.info("get wechat access token after,token is " + accessToken);
        }
    }

    /**
     * @Description:缓存accesstoken
     * @Author:wangpeng
     * @Date:2017年1月19日
     * @Return:void
     */
    private void setAccessTokenOnRedis(String token) {
        Jedis jedis = messageJedisPool.getJedis();
        try {
            if (jedis != null) {
                String state = jedis.set(WECHAT_ACCESSTOKEN_KEY, token);
                Long expire = jedis.expire(WECHAT_ACCESSTOKEN_KEY, WECHAT_ACCESSTOKEN_EXPIRE);
                logger.info("set access token state is " + state);
                logger.info("access token expire time is " + expire);
            }
        } catch (Exception e) {
            logger.error("set access token on redis throw excpetion :{}", e);
        } finally {
            if (jedis != null) {
                jedis.close();
            }
        }
    }

    /**
     * @Description:获取锁
     * @Author:wangpeng
     * @Date:2017年2月10日
     * @Return:Long
     */
    private Long getLock() {
        Long lock = 0L;
        Jedis jedis = messageJedisPool.getJedis();
        try {
            if (jedis != null) {
                while (lock != 1) {
                    long now = System.currentTimeMillis();
                    lock_timeout = now + LOCK_TIMEOUT + 1000;
                    lock = jedis.setnx(LOCK_KEY, lock_timeout + "");
                    if (lock.longValue() == 1 || (now > Long.parseLong(jedis.get(LOCK_KEY))
                                                  && now > Long.parseLong(jedis.getSet(LOCK_KEY, lock_timeout + "")))) {
                        break;
                    } else {
                        Thread.sleep(SLEEP_TIME);
                    }
                }
            }
        } catch (Exception e) {
            logger.error("getLock failed throw excpetion :{}", e);
        } finally {
            if (jedis != null) {
                jedis.close();
            }
        }
        return lock;
    }

    /**
     * @Description:释放锁
     * @Author:wangpeng
     * @Date:2017年2月10日
     * @Return:void
     */
    private void releaseLock() {
        Jedis jedis = messageJedisPool.getJedis();
        try {
            if (jedis != null) {
                long now = System.currentTimeMillis();
                if (now < lock_timeout) {
                    Long result = jedis.del(LOCK_KEY);
                    logger.info("release lock result:" + result);
                }
            }
        } catch (Exception e) {
            logger.error("releaseLock failed throw excpetion :{}", e);
        } finally {
            if (jedis != null) {
                jedis.close();
            }
        }
    }

    /**
     * @Description:
     * @Author:wangpeng
     * @Date:2017年1月22日
     */
    @Override
    public void afterPropertiesSet() throws Exception {
        WebApplicationContext wac = getWebApplicationContext();
        messageJedisPool = (MessageJedisPool) wac.getBean("messageJedisPool");
    }

}


WeixinSDK.net使用帮助1.使用对象微信公众平台:http://mp.weixin.qq.com/wiki/home/index.htmlDeepleo.Weixin.SDK是SDK源代码Deepleo.Web是一个asp.net mvc的demo2.核心思想运用Dynamic(.net 4.0以及以上版本支持)在程序中传递微信所需的结构化(xml,json)对象,减少大量代码,实现轻量级。 让您可以像Python一样书写代码。3.疑难问题1)API返回的Dynamic对象应该如何使用?答:如果您调用API,return的是 a.由DynamicJson.Parse转换而来,您可以用.[属性名称] 访问到该属性的值; 譬如 BasicAPI.cs中GetAccessToken: var token = DynamicJson.Parse(result.Content.ReadAsStringAsync().Result); 那么调用时:string newToken = BasicAPI.GetAccessToken(AppId, AppSecrect).access_token; b.如果是DynamicXml转换而来的,您可以用.[属性名称].Value 访问到该属性的值; 譬如 AcceptMessageAPI.cs中Parse: msg.Body = new DynamicXml(message); string msgType = msg.Body.MsgType.Value;2)遇到其他问题该如何解决?答:如果开发者遇到开发问题或者遇到SDK的bug,请到  a.官方QQ群:173564082   b.官方论坛:http://www.weixinsdk.net/  c.作者QQ:2586662969  d.作者微信号: deepleo (加好友注明weixinsdk)4.源代码唯一托管地址:https://github.com/night-king/weixinSDK5.Copyright and licenseCode and documentation copyright 2011-2015. Code released under the MIT license. Docs released under Creative Commons. 标签:微信sdk
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值