缓存微信accesstoken实现

夏知
2023-12-01

一、背景

微信公众号调用接口的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");
    }

}


 类似资料: