Chinaunix首页 | 论坛 | 博客
  • 博客访问: 231032
  • 博文数量: 19
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 1182
  • 用 户 组: 普通用户
  • 注册时间: 2013-02-20 23:47
个人简介

如果人生是一条隐含马尔科夫模型,那维特比算法告诉我已经偏离最优路线了,但如果起点是现在呢?

文章分类

全部博文(19)

文章存档

2020年(2)

2014年(3)

2013年(14)

分类: 架构设计与优化

2013-12-09 00:41:53

普通青年用etag:后置拦截

点击(此处)折叠或打开

  1. public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
  2.      HttpServletRequest servletRequest = (HttpServletRequest) req;
  3.      HttpServletResponse servletResponse = (HttpServletResponse) res;

  4.       ByteArrayOutputStream baos = new ByteArrayOutputStream();
  5.      ETagResponseWrapper wrappedResponse = new ETagResponseWrapper(servletResponse, baos);
  6.      chain.doFilter(servletRequest, wrappedResponse);

  7.       byte[] bytes = baos.toByteArray();

  8.       String token = '"' + ETagComputeUtils.getMd5Digest(bytes) + '"';//对已经生成的视图内容计算md5值,消耗CPU
  9.      servletResponse.setHeader("ETag", token); // always store the ETag in the header

  10.       String previousToken = servletRequest.getHeader("If-None-Match");
  11.      if (previousToken != null && previousToken.equals(token)) { // 比较提交上来的且是上次从服务器获取的etags,相等则返回304,节省带宽
  12.          logger.debug("ETag match: returning 304 Not Modified");
  13.          servletResponse.sendError(HttpServletResponse.SC_NOT_MODIFIED);
  14.          // use the same date we sent when we created the ETag the first time through
  15.          servletResponse.setHeader("Last-Modified", servletRequest.getHeader("If-Modified-Since"));
  16.      } else {         // 赔了夫人又折兵,但是为了某些不支持1.1的浏览器还是要返回LM
  17.          Calendar cal = Calendar.getInstance();
  18.          cal.set(Calendar.MILLISECOND, 0);
  19.          Date lastModified = cal.getTime();
  20.          servletResponse.setDateHeader("Last-Modified", lastModified.getTime());

  21.           logger.debug("Writing body content");
  22.          servletResponse.setContentLength(bytes.length);
  23.          ServletOutputStream sos = servletResponse.getOutputStream();
  24.          sos.write(bytes);
  25.          sos.flush();
  26.          sos.close();
  27.      }
  28.  }


总结:简而言之就是牺牲性能换带宽,将已经经过底层操作(数据库操or外部接口之类的)获得的视图对象计算md5值,跟提交上来的etag进行比较,决定是发送304还是原本的web内容;缺点是对变更频率比较大的web内容没有良好的容忍性



文艺青年用etag:前置拦截

getToken方法从一个全局变量里取当前请求uri的计数器


点击(此处)折叠或打开

  1. public final boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws ServletException, IOException {
  2.      String method = request.getMethod();
  3.      if (!"GET".equals(method))
  4.          return true;

  5.       String previousToken = request.getHeader("If-None-Match");
  6.      String token = getTokenFactory().getToken(request);//取得计数器

  7.       // compare previous token with current one
  8.      if ((token != null) && (previousToken != null && previousToken.equals('"' + token + '"'))) {//
  9.          response.sendError(HttpServletResponse.SC_NOT_MODIFIED);
  10.          // re-use original last modified timestamp
  11.          response.setHeader("Last-Modified", request.getHeader("If-Modified-Since"))
  12.          return false; // 不需要走处理链,直接返回304
  13.      }

  14.       // set header for the next time the client calls
  15.      if (token != null) {
  16.          response.setHeader("ETag", '"' + token + '"');

  17.           // first time through - set last modified time to now
  18.          Calendar cal = Calendar.getInstance();
  19.          cal.set(Calendar.MILLISECOND, 0);
  20.          Date lastModified = cal.getTime();
  21.          response.setDateHeader("Last-Modified", lastModified.getTime());
  22.      }

  23.       return true;//没什么损失,继续走处理链
  24.  }

全局变量是一个key-value的结构

点击(此处)折叠或打开

  1. public String getToken(HttpServletRequest request) {
  2.      String view = request.getRequestURI();
  3.      Integer count = counts.get(view);
  4.      if (count == null) {
  5.          return null;
  6.      }

  7.       return count.toString();
  8.  }

其中这个K-V计数器的变更是绑定在底层数据库的写函数上的(以Hibernate 3 LocalSessionFactoryBean为例


点击(此处)折叠或打开

  1. public class DeleteHandler extends DefaultDeleteEventListener {
  2.      private ModifiedObjectTracker tracker;

  3.       public void onDelete(DeleteEvent event) throws HibernateException {
  4.          String viewname=eventname2view(event.getEntityName());//实体名转视图名字(比如说论坛某个帖子实质内容的ajax链接)
  5.         Integer count=counts.get(viewname);//获取计数器并自增
  6.         counts.put(view,count++);
  7.      }

  8.      public ModifiedObjectTracker getModifiedObjectTracker() {
  9.          return tracker;
  10.      }
  11.       public void setModifiedObjectTracker(ModifiedObjectTracker tracker) {
  12.          this.tracker = tracker;
  13.      }
  14.  }

总结:这么做不仅节省带宽,还节省CPU,更节省用户等待时间,有利于提高用户体验,可谓一举多得;需要注意的是,计数器那一块得设计好过期算法,那不然内存会不够你花



阅读(5327) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~