使用 Map + 定时器 定时存取微信 Token

来源:互联网 发布:java jsoup 爬虫实例 编辑:程序博客网 时间:2024/06/14 04:58

微信的 Token 可以存放在数据库里 , 需要的时候从数据库读取 . 但是这样日积月累 , 数据量会逐渐撑大 , 而且 token 我们用指定时间(两小时内)就会丢弃 , 以后也不会用到过期的 token , 所以将它保存在数据库是很浪费资源的 . 所以我写了个缓存类来定时获取微信的 token 到 map
首先 , 定义存 map 的方法

package com.newtec.weixin.manager;import java.util.HashMap;import java.util.Map;import java.util.concurrent.ScheduledThreadPoolExecutor;import java.util.concurrent.TimeUnit;import java.util.concurrent.locks.Lock;import java.util.concurrent.locks.ReentrantLock;import org.slf4j.Logger;import org.slf4j.LoggerFactory;import com.newtec.weixin.pojo.CacheConfModel;/** * 使用 map 作为一级缓存 , 必要时代替数据库存取token */public class CacheMgr {    private static Logger log = LoggerFactory.getLogger(CacheMgr.class);    public static Map cacheMap = new HashMap();//存放缓存    public static Map cacheConfMap = new HashMap();//存放配置信息    static final Lock lock = new ReentrantLock();    public static String key;    private static Object value;    private static CacheConfModel ccm;    //单例    private static CacheMgr cacheMgr = null;    private CacheMgr(){}    public static CacheMgr getIntance(){        if(cacheMgr == null){            cacheMgr = new CacheMgr();        }        return cacheMgr;    }    /**     * 增加缓存信息     * @param key     * @param value     * @param ccm     * @return     */    public class AddCache implements Runnable{        public AddCache(String key,Object value,CacheConfModel ccm) {            CacheMgr.key = key;            CacheMgr.value = value;            CacheMgr.ccm = ccm;        }        @Override        public void run() {            //定时线程处理的业务                log.info("开始清理缓存---  {}",key);                ClearCache.clearCache();//每次运行清除上次缓存  只有一个token缓存能够存在                log.info("开始缓存---  {}",key);                lock.lock();                try {                    cacheMap.put(key, value);                    cacheConfMap.put(key, ccm);                    log.info("增加缓存结束:key={},value={}",key,value);                    log.info("缓存大小为={}",cacheMap.size());                } catch (Exception e) {                    e.printStackTrace();                }finally{                    lock.unlock();                }        }    }    /**     * 获取缓存实体     * @param key  缓存键     * @return     */    public Object getValue(String key){        Object obj = cacheMap.get(key);        if(obj != null){            return obj;        }else{            return null;        }    }    /**     * 获取缓存总数     * @return 缓存总数     */    public int getSize(){        return cacheMap.size();    }    /**     * 删除缓存     * @param key      * @return     */    public boolean removeCache(String key){        boolean flag = false;        try {            cacheMap.remove(key);            cacheConfMap.remove(key);            flag = true;        } catch (Exception e) {            e.printStackTrace();        }        return flag;    }}

如果你没有 log 的 jar 包 , 可以选择不打印 log

由于我们每次只用到一个 token , 当最新的 token 生成时 ,以前的 token 都会失效 , 所以在增加 token缓存之前需要清理之前的缓存

public class ClearCache{    static final Lock lock = new ReentrantLock();    final Condition myLock = lock.newCondition();    public static void clearCache(){        while(true){            try {                lock.lock();//每次只允许一个线程清理缓存                Set tempSet = new HashSet();                Set set = CacheMgr.cacheMap.keySet();                Iterator it = set.iterator();                while(it.hasNext()){//迭代                    String key = (String) it.next();                    CacheConfModel ccm = (CacheConfModel)CacheMgr.cacheConfMap.get(key);                      tempSet.add(key);  //记录清除数据                }                //清除                Iterator tempIt = tempSet.iterator();                while(tempIt.hasNext()){                    String key = (String) tempIt.next();                    CacheMgr.cacheMap.remove(key);                    CacheMgr.cacheConfMap.remove(key);                }                if(CacheMgr.cacheMap.size()<=0 && CacheMgr.cacheConfMap.size()<=0){//如果缓存已被清空,跳出循环 , 否则继续清理 , 知道 map 中没有 token 为止                    break;                }            } catch (Exception e) {                e.printStackTrace();            }finally{                lock.unlock();            }        }    }}

然后我们需要一个 servlet 执行缓存存取过程

public class AutoLoadCacheServlet extends HttpServlet {    private static CacheMgr cacheMgr = CacheMgr.getIntance();    private static ScheduledThreadPoolExecutor stpe =  new ScheduledThreadPoolExecutor(1);    @Override    protected void doPost(HttpServletRequest req, HttpServletResponse resp)            throws ServletException, IOException {        CacheConfModel cModel = new CacheConfModel();        Date d = new Date();        cModel.setBeginTime(d.getTime());        cModel.setDurableTime(60);        cModel.setForever(true);        AddCache addCache = cacheMgr.new AddCache("TOKEN", CommonUtil.getToken(                CommonUtil.APPID, CommonUtil.APPSECRET).getAccessToken(), cModel);        //执行的回调方法,第一次执行时间,周期,时间类型                   stpe.scheduleAtFixedRate(addCache, 0, 7000,TimeUnit.SECONDS);                       //System.out.println(CacheMgr.cacheMap.get("TOKEN"));    }    }

缓存配置类 – CacheConfModel

public class CacheConfModel {    private long beginTime;// 缓存开始时间    private boolean isForever = false;// 是否持久保存    private int durableTime;// 持续时间    public long getBeginTime() {        return beginTime;    }    public void setBeginTime(long beginTime) {        this.beginTime = beginTime;    }    public boolean isForever() {        return isForever;    }    public void setForever(boolean isForever) {        this.isForever = isForever;    }    public int getDurableTime() {        return durableTime;    }    public void setDurableTime(int durableTime) {        this.durableTime = durableTime;    }}

最后在启动 web 项目时 , 自动装载这个 servelet
在 web.xml 配置

<servlet>        <servlet-name>autoLoadCacheServlet</servlet-name>        <servlet-class>com.newtec.weixin.servlet.AutoLoadCacheServlet        </servlet-class>        <load-on-startup>0</load-on-startup>    </servlet>

这样当 web 项目启动时 , 缓存就会通过定时器自动增加并清理
这里不使用 timer 的原因是
Timer 和 Timertask 是单线程的,而且没有处理异常,当程序超过指定时间没有运行的时候,程序就会异常终止 , 即使我们用线程锁使得缓存存取/清理同步了 , 仍然不能保证不会在处理缓存的时候跑出异常导致程序终止
所以需要多个线程或者需要额外的灵活性和功能时,推荐使用ScheduledThreadPoolExecutor

原创粉丝点击