Google App Engine性能优化策略:页面性能优化

GAE提供了简单实用的API和开发工具,结合已有的开发框架,Java开发人员可以很容易开发出自己的业
首页 新闻资讯 行业资讯 Google App Engine性能优化策略:页面性能优化

GAE即Google App Engine,还不了解什么是Google App Engine的可以阅读一下它的介绍

GAE提供了简单实用的API和开发工具,结合已有的开发框架,Java开发人员可以很容易开发出自己的业务应用系统。本次先介绍页面部分的性能优化技巧,只需要进行简单的设置和少量的编码,即可获得不错的性能提高。文中提到的技巧已经在本博客取得验证,从后来的统计数据中可以看到,首页的处理时间从平均400ms减少到了平均26ms,性能提高了15倍!

App Engine性能优化:指定GAE的静态文件配置

在一般的httpd + tomcat的架构中,客户端对图片、css文件以及js文件等静态资源文件,会根据文件的lsat modified属性,尽量只会请求一次,只有当文件进行了更新之后,才会重新请求新的文件。

但是在GAE里面,如果你不进行静态文件的设置,默认情况下,是无法享受上面所提的好处的。下面来看看设置的文件/WEB-INF/appengine-web.xml:


复制

<?xml version="1.0" encoding="utf-8"?> <appengine-web-app xmlns="http://appengine.google.com/ns/1.0">     <application>yourappid</application>     <version>1</version>     <static-files>         <include path="/**.jpg"/>         <include path="/**.png"/>         <include path="/**.gif"/>         <include path="/**.ico"/>         <include path="/**.css"/>         <include path="/**.js"/>     </static-files> </appengine-web-app>
  • 1.

  • 2.

  • 3.

  • 4.

  • 5.

  • 6.

  • 7.

  • 8.

  • 9.

  • 10.

  • 11.

  • 12.

  • 13.

进行了上面的设置之后,你的应用可以得到较为明显的性能提升。

App Engine性能优化:利用Memcache服务进行页面缓存

GAE提供了Memcache服务,可以将经常使用到数据暂时存储在Memcache中,可以大大减少请求的处理时间,提高页面响应速度。下面提供几个代码例子,利用Servlet Filter技术,可以对经常访问的页面进行缓存操作。

CacheSingleton.java


复制

package hover.blog.servlet;   import javax.cache.Cache;  import javax.cache.CacheException;  import javax.cache.CacheFactory;  import javax.cache.CacheManager;  import javax.servlet.ServletException;  import java.util.Map;   /**   * @author Hover   * @version 1.0   */ public class CacheSingleton {      private static final CacheSingleton instance = new CacheSingleton();       private Cache cache;       private CacheSingleton() {      }       public static CacheSingleton getInstance() {          return instance;      }       public void init(Map props) throws ServletException {          try {              CacheFactory factory = CacheManager.getInstance().getCacheFactory();               cache = factory.createCache(props);          } catch (CacheException e) {              throw new ServletException("cache error: " + e.getMessage(), e);          }      }       public Cache getCache() {          return cache;      }       public void clear() {          if (cache != null) {              cache.clear();          }      }  }
  • 1.

  • 2.

  • 3.

  • 4.

  • 5.

  • 6.

  • 7.

  • 8.

  • 9.

  • 10.

  • 11.

  • 12.

  • 13.

  • 14.

  • 15.

  • 16.

  • 17.

  • 18.

  • 19.

  • 20.

  • 21.

  • 22.

  • 23.

  • 24.

  • 25.

  • 26.

  • 27.

  • 28.

  • 29.

  • 30.

  • 31.

  • 32.

  • 33.

  • 34.

  • 35.

  • 36.

  • 37.

  • 38.

  • 39.

  • 40.

  • 41.

  • 42.

  • 43.

  • 44.

  • 45.

  • 46.

因需要在多处地方访问Cache,因此这里使用了Singleton模式,可以在不同的Action中访问同一个Cache实例。

WebCacheFilter

WebCacheFilter.java


复制

package hover.blog.servlet;   import javax.cache.Cache;  import javax.servlet.*;  import javax.servlet.http.Cookie;  import javax.servlet.http.HttpServletRequest;  import javax.servlet.http.HttpServletResponse;  import java.io.BufferedOutputStream;  import java.io.ByteArrayOutputStream;  import java.io.IOException;  import java.util.Collections;  import java.util.List;   /**   * @author Hover   * @version 1.0   */ @SuppressWarnings("unchecked")  public class WebCacheFilter implements Filter {      public static final String PAGE_PREFIX = "/page";       public void init(FilterConfig config) throws ServletException {          CacheSingleton.getInstance().init(Collections.emptyMap());      }       public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse,                           FilterChain chain) throws IOException, ServletException {          HttpServletRequest request = (HttpServletRequest) servletRequest;          HttpServletResponse response = (HttpServletResponse) servletResponse;           Cache cache = CacheSingleton.getInstance().getCache();           if ("post".equalsIgnoreCase(request.getMethod()) || cache == null) {              chain.doFilter(servletRequest, servletResponse);          } else {              String requestPath = request.getRequestURI();              String queryString = request.getQueryString();              if (queryString != null && queryString.length() > 0) {                  requestPath += "?" + queryString;              }              String cachePath = PAGE_PREFIX + requestPath;               PageInfo page = null;               try {                  page = (PageInfo) cache.get(cachePath);              }              catch (Exception e) {                  // type mis-match              }               if (page == null) {  // on cache content                  ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();                  GenericResponseWrapper wrapper = new GenericResponseWrapper(response, byteArrayOutputStream);                  chain.doFilter(request, wrapper);                  wrapper.flush();                   page = new PageInfo(wrapper.getStatus(), wrapper.getContentType(), wrapper.getHeaders(),                          wrapper.getCookies(), byteArrayOutputStream.toByteArray());                   if (page.getStatus() == HttpServletResponse.SC_OK) {                      cache.put(cachePath, page);                  }              }               response.setStatus(page.getStatus());               String contentType = page.getContentType();              if (contentType != null && contentType.length() > 0) {                  response.setContentType(contentType);              }               for (Cookie cookie : (List) page.getCookies()) {                  response.addCookie(cookie);              }               for (String[] header : (List) page.getResponseHeaders()) {                  response.setHeader(header[0], header[1]);              }               response.setContentLength(page.getBody().length);              BufferedOutputStream out = new BufferedOutputStream(response.getOutputStream());              out.write(page.getBody());              out.flush();          }      }       public void destroy() {      }  }
  • 1.

  • 2.

  • 3.

  • 4.

  • 5.

  • 6.

  • 7.

  • 8.

  • 9.

  • 10.

  • 11.

  • 12.

  • 13.

  • 14.

  • 15.

  • 16.

  • 17.

  • 18.

  • 19.

  • 20.

  • 21.

  • 22.

  • 23.

  • 24.

  • 25.

  • 26.

  • 27.

  • 28.

  • 29.

  • 30.

  • 31.

  • 32.

  • 33.

  • 34.

  • 35.

  • 36.

  • 37.

  • 38.

  • 39.

  • 40.

  • 41.

  • 42.

  • 43.

  • 44.

  • 45.

  • 46.

  • 47.

  • 48.

  • 49.

  • 50.

  • 51.

  • 52.

  • 53.

  • 54.

  • 55.

  • 56.

  • 57.

  • 58.

  • 59.

  • 60.

  • 61.

  • 62.

  • 63.

  • 64.

  • 65.

  • 66.

  • 67.

  • 68.

  • 69.

  • 70.

  • 71.

  • 72.

  • 73.

  • 74.

  • 75.

  • 76.

  • 77.

  • 78.

  • 79.

  • 80.

  • 81.

  • 82.

  • 83.

  • 84.

  • 85.

  • 86.

  • 87.

  • 88.

  • 89.

  • 90.

  • 91.

在初始化的时候,调用CacheSingleton.init()方法,初始化Memecache的调用接口。

WebCacheFilter只处理HTTP GET请求,对后台数据的修改、删除、新增等操作,应该使用HTTP POST方式来提交数据。

下面将此Filter所用到的其他辅助类列在下面:

FilterServletOutputStream.java


复制

package hover.blog.servlet;   import javax.servlet.ServletOutputStream;  import java.io.IOException;  import java.io.OutputStream;   /**   * @author Hover   * @version 1.0   */ public class FilterServletOutputStream extends ServletOutputStream {       private OutputStream stream;       public FilterServletOutputStream(final OutputStream stream) {          this.stream = stream;      }       /**       * Writes to the stream.       */     public void write(final int b) throws IOException {          stream.write(b);      }       /**       * Writes to the stream.       */     public void write(final byte[] b) throws IOException {          stream.write(b);      }       /**       * Writes to the stream.       */     public void write(final byte[] b, final int off, final int len) throws IOException {          stream.write(b, off, len);      }  }  
GenericResponseWrapper.java  package hover.blog.servlet;   import javax.servlet.ServletOutputStream;  import javax.servlet.http.Cookie;  import javax.servlet.http.HttpServletResponse;  import javax.servlet.http.HttpServletResponseWrapper;  import java.io.*;  import java.util.ArrayList;  import java.util.Collection;  import java.util.List;  import java.util.logging.Logger;   /**   * @author Hover   * @version 1.0   */ @SuppressWarnings("unchecked")  public class GenericResponseWrapper extends HttpServletResponseWrapper implements Serializable {       private static final Logger LOG = Logger.getLogger(GenericResponseWrapper.class.getName());      private int statusCode = SC_OK;      private int contentLength;      private String contentType;      private final List headers = new ArrayList();      private final List cookies = new ArrayList();      private ServletOutputStream outstr;      private PrintWriter writer;       /**       * Creates a GenericResponseWrapper       */     public GenericResponseWrapper(final HttpServletResponse response, final OutputStream outstr) {          super(response);          this.outstr = new FilterServletOutputStream(outstr);      }       /**       * Gets the outputstream.       */     public ServletOutputStream getOutputStream() {          return outstr;      }       /**       * Sets the status code for this response.       */     public void setStatus(final int code) {          statusCode = code;          super.setStatus(code);      }       /**       * Send the error. If the response is not ok, most of the logic is bypassed and the error is sent raw       * Also, the content is not cached.       *       * @param i      the status code       * @param string the error message       * @throws IOException       */     public void sendError(int i, String string) throws IOException {          statusCode = i;          super.sendError(i, string);      }       /**       * Send the error. If the response is not ok, most of the logic is bypassed and the error is sent raw       * Also, the content is not cached.       *       * @param i the status code       * @throws IOException       */     public void sendError(int i) throws IOException {          statusCode = i;          super.sendError(i);      }       /**       * Send the redirect. If the response is not ok, most of the logic is bypassed and the error is sent raw.       * Also, the content is not cached.       *       * @param string the URL to redirect to       * @throws IOException       */     public void sendRedirect(String string) throws IOException {          statusCode = HttpServletResponse.SC_MOVED_TEMPORARILY;          super.sendRedirect(string);      }       /**       * Sets the status code for this response.       */     public void setStatus(final int code, final String msg) {          statusCode = code;          LOG.warning("Discarding message because this method is deprecated.");          super.setStatus(code);      }       /**       * Returns the status code for this response.       */     public int getStatus() {          return statusCode;      }       /**       * Sets the content length.       */     public void setContentLength(final int length) {          this.contentLength = length;          super.setContentLength(length);      }       /**       * Gets the content length.       */     public int getContentLength() {          return contentLength;      }       /**       * Sets the content type.       */     public void setContentType(final String type) {          this.contentType = type;          super.setContentType(type);      }       /**       * Gets the content type.       */     public String getContentType() {          return contentType;      }        /**       * Gets the print writer.       */     public PrintWriter getWriter() throws IOException {          if (writer == null) {              writer = new PrintWriter(new OutputStreamWriter(outstr, getCharacterEncoding()), true);          }          return writer;      }       /**       * Adds a header.       */     public void addHeader(final String name, final String value) {          final String[] header = new String[]{name, value};          headers.add(header);          super.addHeader(name, value);      }       /**       * @see #addHeader       */     public void setHeader(final String name, final String value) {          addHeader(name, value);      }       /**       * Gets the headers.       */     public List getHeaders() {          return headers;      }       /**       * Adds a cookie.       */     public void addCookie(final Cookie cookie) {          cookies.add(cookie);          super.addCookie(cookie);      }       /**       * Gets all the cookies.       */     public List getCookies() {          return cookies;      }       /**       * Flushes buffer and commits response to client.       */     public void flushBuffer() throws IOException {          flush();          super.flushBuffer();      }       /**       * Resets the response.       */     public void reset() {          super.reset();          cookies.clear();          headers.clear();          statusCode = SC_OK;          contentType = null;          contentLength = 0;      }       /**       * Resets the buffers.       */     public void resetBuffer() {          super.resetBuffer();      }       /**       * Flushes all the streams for this response.       */     public void flush() throws IOException {          if (writer != null) {              writer.flush();          }          outstr.flush();      }  }  
PageInfo.java  package hover.blog.servlet;   import java.io.Serializable;  import java.util.List;   /**   * @author Hover   * @version 1.0   */ public class PageInfo implements Serializable {      private int status;      private String contentType;      private List responseHeaders;      private List cookies;      private byte[] body;       public PageInfo() {      }       public PageInfo(int status, String contentType, List responseHeaders, List cookies, byte[] body) {          this.status = status;          this.contentType = contentType;          this.responseHeaders = responseHeaders;          this.cookies = cookies;          this.body = body;      }       public int getStatus() {          return status;      }       public void setStatus(int status) {          this.status = status;      }       public String getContentType() {          return contentType;      }       public void setContentType(String contentType) {          this.contentType = contentType;      }       public List getResponseHeaders() {          return responseHeaders;      }       public void setResponseHeaders(List responseHeaders) {          this.responseHeaders = responseHeaders;      }       public List getCookies() {          return cookies;      }       public void setCookies(List cookies) {          this.cookies = cookies;      }       public byte[] getBody() {          return body;      }       public void setBody(byte[] body) {          this.body = body;      }  }   
  • 1.

  • 2.

  • 3.

  • 4.

  • 5.

  • 6.

  • 7.

  • 8.

  • 9.

  • 10.

  • 11.

  • 12.

  • 13.

  • 14.

  • 15.

  • 16.

  • 17.

  • 18.

  • 19.

  • 20.

  • 21.

  • 22.

  • 23.

  • 24.

  • 25.

  • 26.

  • 27.

  • 28.

  • 29.

  • 30.

  • 31.

  • 32.

  • 33.

  • 34.

  • 35.

  • 36.

  • 37.

  • 38.

  • 39.

  • 40.

  • 41.

  • 42.

  • 43.

  • 44.

  • 45.

  • 46.

  • 47.

  • 48.

  • 49.

  • 50.

  • 51.

  • 52.

  • 53.

  • 54.

  • 55.

  • 56.

  • 57.

  • 58.

  • 59.

  • 60.

  • 61.

  • 62.

  • 63.

  • 64.

  • 65.

  • 66.

  • 67.

  • 68.

  • 69.

  • 70.

  • 71.

  • 72.

  • 73.

  • 74.

  • 75.

  • 76.

  • 77.

  • 78.

  • 79.

  • 80.

  • 81.

  • 82.

  • 83.

  • 84.

  • 85.

  • 86.

  • 87.

  • 88.

  • 89.

  • 90.

  • 91.

  • 92.

  • 93.

  • 94.

  • 95.

  • 96.

  • 97.

  • 98.

  • 99.

  • 100.

  • 101.

  • 102.

  • 103.

  • 104.

  • 105.

  • 106.

  • 107.

  • 108.

  • 109.

  • 110.

  • 111.

  • 112.

  • 113.

  • 114.

  • 115.

  • 116.

  • 117.

  • 118.

  • 119.

  • 120.

  • 121.

  • 122.

  • 123.

  • 124.

  • 125.

  • 126.

  • 127.

  • 128.

  • 129.

  • 130.

  • 131.

  • 132.

  • 133.

  • 134.

  • 135.

  • 136.

  • 137.

  • 138.

  • 139.

  • 140.

  • 141.

  • 142.

  • 143.

  • 144.

  • 145.

  • 146.

  • 147.

  • 148.

  • 149.

  • 150.

  • 151.

  • 152.

  • 153.

  • 154.

  • 155.

  • 156.

  • 157.

  • 158.

  • 159.

  • 160.

  • 161.

  • 162.

  • 163.

  • 164.

  • 165.

  • 166.

  • 167.

  • 168.

  • 169.

  • 170.

  • 171.

  • 172.

  • 173.

  • 174.

  • 175.

  • 176.

  • 177.

  • 178.

  • 179.

  • 180.

  • 181.

  • 182.

  • 183.

  • 184.

  • 185.

  • 186.

  • 187.

  • 188.

  • 189.

  • 190.

  • 191.

  • 192.

  • 193.

  • 194.

  • 195.

  • 196.

  • 197.

  • 198.

  • 199.

  • 200.

  • 201.

  • 202.

  • 203.

  • 204.

  • 205.

  • 206.

  • 207.

  • 208.

  • 209.

  • 210.

  • 211.

  • 212.

  • 213.

  • 214.

  • 215.

  • 216.

  • 217.

  • 218.

  • 219.

  • 220.

  • 221.

  • 222.

  • 223.

  • 224.

  • 225.

  • 226.

  • 227.

  • 228.

  • 229.

  • 230.

  • 231.

  • 232.

  • 233.

  • 234.

  • 235.

  • 236.

  • 237.

  • 238.

  • 239.

  • 240.

  • 241.

  • 242.

  • 243.

  • 244.

  • 245.

  • 246.

  • 247.

  • 248.

  • 249.

  • 250.

  • 251.

  • 252.

  • 253.

  • 254.

  • 255.

  • 256.

  • 257.

  • 258.

  • 259.

  • 260.

  • 261.

  • 262.

  • 263.

  • 264.

  • 265.

  • 266.

  • 267.

  • 268.

  • 269.

  • 270.

  • 271.

  • 272.

  • 273.

  • 274.

  • 275.

  • 276.

  • 277.

  • 278.

  • 279.

  • 280.

  • 281.

  • 282.

  • 283.

  • 284.

  • 285.

  • 286.

  • 287.

  • 288.

  • 289.

  • 290.

  • 291.

  • 292.

  • 293.

  • 294.

  • 295.

  • 296.

  • 297.

  • 298.

  • 299.

  • 300.

  • 301.

  • 302.

  • 303.

  • 304.

  • 305.

  • 306.

  • 307.

  • 308.

  • 309.

  • 310.

  • 311.

  • 312.

  • 313.

  • 314.

  • 315.

  • 316.

  • 317.

  • 318.

  • 319.

  • 320.

  • 321.

  • 322.

  • 323.

  • 324.

  • 325.

  • 326.

  • 327.

  • 328.

  • 329.

  • 330.

  • 331.

App Engine性能优化:在web.xml中配置WebCacheFilter

在web.xml中,配置WebCacheFilter,对经常访问的页面进行缓存。下面是我的博客的配置:


复制

<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns="http://java.sun.com/xml/ns/javaee"          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"          xsi:schemaLocation="http://java.sun.com/xml/ns/javaee      http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"           version="2.5">     <filter>         <filter-name>webCache</filter-name>         <filter-class>hover.blog.servlet.WebCacheFilter</filter-class>     </filter>      <filter-mapping>         <filter-name>webCache</filter-name>         <url-pattern>/main</url-pattern>     </filter-mapping>      <filter-mapping>         <filter-name>webCache</filter-name>         <url-pattern>/blog</url-pattern>     </filter-mapping>      <filter-mapping>         <filter-name>webCache</filter-name>         <url-pattern>/category</url-pattern>     </filter-mapping> </web-app>
  • 1.

  • 2.

  • 3.

  • 4.

  • 5.

  • 6.

  • 7.

  • 8.

  • 9.

  • 10.

  • 11.

  • 12.

  • 13.

  • 14.

  • 15.

  • 16.

  • 17.

  • 18.

  • 19.

  • 20.

  • 21.

  • 22.

  • 23.

  • 24.

  • 25.

  • 26.

  • 27.

App Engine性能优化:页面缓存的使用限制

WebCacheFilter会缓存整个页面的全部元素,如果页面中存在用户相关的代码,例如根据用户的身份不同而现实不同的内容的话,可能会出现不希望出现的后果。

假设你的页面中,判断如果是管理员的话,显示编辑链接:

jsp文件:

复制

<s:if test="admin">   <a href="edit-blog?key=<s:property value="key"/>">     <s:text name="edit"/>   </a> </s:if>
  • 1.

  • 2.

  • 3.

  • 4.

  • 5.


如果管理员先访问了页面,则缓存中保存的页面中,就包含了“编辑”的链接。当另外一个普通用户访问同一个url时,从页面缓存中获得了前面管理员所看到的页面,因为,普通用户也看到了“编辑”的链接。

因此,在利用WebCacheFilter进行缓存的页面中,尽量避免太多的动态属性显示。数据的编辑、维护工作应该在专门的页面中进行。

本文来自JavaEye博客:《Google App Engine性能调优 - 页面性能优化》

【编辑推荐】

  1. Google App Engine上的Scala+Lift初试

  2. 什么是GAE:Google App Engine介绍

  3. 手把手教你在Google App Engine上运行PHP

  4. 开始您的***个Google App Engine应用

  5. 在Google Java App Engine上实现文档存储和搜索

12    2009-09-08 09:45:23    App Engine性能优化