zoukankan      html  css  js  c++  java
  • 防缓存穿透设计

    这里首先要弄清楚什么是缓存穿透,缓存雪崩,缓存击穿,简单说就是缓存里面查询不到,db里面也查询不到,耗干服务器资源或缓存失效了,外面的请求这时直接访问的是db不是cache,那么流量一加大,就会把数据库顶爆

    如果还不清楚,请参考这里  缓存穿透,缓存击穿,缓存雪崩解决方案分析

    我这里拿之前写过的java-redis缓存jsp页面做例子,

    限流可用的技术有

    a,Guava官方文档-RateLimiter类,

    b,Semaphore,

    c,netflix的hystrix

    这里用到的是java 里面的 Semaphore做限制

    import com.xxxxxx.platform.common.redis.RedisUtil;
    import org.apache.commons.lang.StringUtils;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import javax.servlet.*;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import java.io.IOException;
    import java.util.concurrent.Semaphore;
    
    public class RedisPageCacheFilter implements Filter {
    
        private static final Logger log = LoggerFactory.getLogger(RedisPageCacheFilter.class);
        private final static String FILTER_URL_PATTERNS = "patterns";
        private static String[] cacheURLs;
        private static int timeOut;
        //加入信号量防止缓存穿透,缓存失效时只能同时1个用户访问数据库,防止数据库被顶爆
        private static final Semaphore semp = new Semaphore(1);
    
        @Override
        public void init(FilterConfig config) throws ServletException {
            String patterns = config.getInitParameter(FILTER_URL_PATTERNS);
            timeOut = Integer.parseInt(config.getInitParameter("expireTime"));
            cacheURLs = StringUtils.split(patterns, ",");
        }
    
        @Override
        public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
            HttpServletResponse resp = (HttpServletResponse) servletResponse;
            HttpServletRequest request = (HttpServletRequest) servletRequest;
            String url = request.getRequestURI();
    
            boolean flag = false;
            if (cacheURLs != null && cacheURLs.length > 0) {
                for (String cacheURL : cacheURLs) {
                    if (url.contains(cacheURL.trim()) || url.matches(cacheURL)) {
                        flag = true;
                        break;
                    }
                }
            }
    
            // 如果包含我们要缓存的url 就缓存该页面,否则执行正常的页面转向
            if (flag) {
    
                String query = request.getQueryString();
                if (query != null) {
                    query = "?" + query;
                }
    
                final String key = "REDISCACHE:" + url + query;
                log.info("当前请求缓存为:" + url + query);
    
                // 从缓存中得到主页html
                String html = getHtmlFromCache(key);
                if (null == html) {
                    try {
                        semp.acquire();
    //这里做第二道redis读缓存操作,因为同时等待不只一个请求,第一个请求读完db,就把数据写到redis里面了,那么第二个进来就不用读db了
    html
    = getHtmlFromCache(key); if(StringUtils.isEmpty(html)) { // 截取生成的html并放入缓存 log.info("缓存不存在,生成缓存1"); ResponseWrapper wrapper = new ResponseWrapper(resp); filterChain.doFilter(servletRequest, wrapper); // 放入缓存 html = wrapper.getResult(); putIntoCache(key, html); } else{ log.info("缓存已经生成,直接用缓存2"); } } catch (InterruptedException e) { e.printStackTrace(); } finally { // 访问完后,释放 semp.release(); } } // 返回响应 resp.setContentType("text/html; charset=utf-8"); resp.getWriter().print(html); } else { filterChain.doFilter(servletRequest, resp); return; } } @Override public void destroy() { } private String getHtmlFromCache(String redisKey) { return RedisUtil.get(redisKey); } private void putIntoCache(String redisKey, String html) { RedisUtil.set(redisKey, html, timeOut * 60); } }

     如有不足之处,请诸位大神指出

  • 相关阅读:
    Ansible 和 Playbook 暂存
    nginx 和keepalived的使用
    关于 群晖 docker 百度云盘下载的使用心得
    文件夹共享
    转:轻松把玩HttpClient之封装HttpClient工具类(一)(现有网上分享中的最强大的工具类)
    Maven学习笔记一
    转:Maven常用命令
    转:MySQL下载安装、配置与使用(win7x64)
    转:SQL SERVER 2014 安装图解(含 SQL SERVER 2014 安装程序共享)
    转:java 解析excel,带合并单元的excel
  • 原文地址:https://www.cnblogs.com/fangyuan303687320/p/8288088.html
Copyright © 2011-2022 走看看