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

开发 开发工具
GAE提供了简单实用的API和开发工具,结合已有的开发框架,Java开发人员可以很容易开发出自己的业务应用系统。本文介绍如何对页面进行优化。

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:

  1. <?xml version="1.0" encoding="utf-8"?> 
  2. <appengine-web-app xmlns="http://appengine.google.com/ns/1.0"> 
  3.     <application>yourappid</application> 
  4.     <version>1</version> 
  5.     <static-files> 
  6.         <include path="/**.jpg"/> 
  7.         <include path="/**.png"/> 
  8.         <include path="/**.gif"/> 
  9.         <include path="/**.ico"/> 
  10.         <include path="/**.css"/> 
  11.         <include path="/**.js"/> 
  12.     </static-files> 
  13. </appengine-web-app> 

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

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

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

CacheSingleton.java

  1. package hover.blog.servlet;  
  2.  
  3. import javax.cache.Cache;  
  4. import javax.cache.CacheException;  
  5. import javax.cache.CacheFactory;  
  6. import javax.cache.CacheManager;  
  7. import javax.servlet.ServletException;  
  8. import java.util.Map;  
  9.  
  10. /**  
  11.  * @author Hover  
  12.  * @version 1.0  
  13.  */ 
  14. public class CacheSingleton {  
  15.     private static final CacheSingleton instance = new CacheSingleton();  
  16.  
  17.     private Cache cache;  
  18.  
  19.     private CacheSingleton() {  
  20.     }  
  21.  
  22.     public static CacheSingleton getInstance() {  
  23.         return instance;  
  24.     }  
  25.  
  26.     public void init(Map props) throws ServletException {  
  27.         try {  
  28.             CacheFactory factory = CacheManager.getInstance().getCacheFactory();  
  29.  
  30.             cache = factory.createCache(props);  
  31.         } catch (CacheException e) {  
  32.             throw new ServletException("cache error: " + e.getMessage(), e);  
  33.         }  
  34.     }  
  35.  
  36.     public Cache getCache() {  
  37.         return cache;  
  38.     }  
  39.  
  40.     public void clear() {  
  41.         if (cache != null) {  
  42.             cache.clear();  
  43.         }  
  44.     }  
  45. }  
  46.  

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

WebCacheFilter

WebCacheFilter.java

  1. package hover.blog.servlet;  
  2.  
  3. import javax.cache.Cache;  
  4. import javax.servlet.*;  
  5. import javax.servlet.http.Cookie;  
  6. import javax.servlet.http.HttpServletRequest;  
  7. import javax.servlet.http.HttpServletResponse;  
  8. import java.io.BufferedOutputStream;  
  9. import java.io.ByteArrayOutputStream;  
  10. import java.io.IOException;  
  11. import java.util.Collections;  
  12. import java.util.List;  
  13.  
  14. /**  
  15.  * @author Hover  
  16.  * @version 1.0  
  17.  */ 
  18. @SuppressWarnings("unchecked")  
  19. public class WebCacheFilter implements Filter {  
  20.     public static final String PAGE_PREFIX = "/page";  
  21.  
  22.     public void init(FilterConfig config) throws ServletException {  
  23.         CacheSingleton.getInstance().init(Collections.emptyMap());  
  24.     }  
  25.  
  26.     public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse,  
  27.                          FilterChain chain) throws IOException, ServletException {  
  28.         HttpServletRequest request = (HttpServletRequest) servletRequest;  
  29.         HttpServletResponse response = (HttpServletResponse) servletResponse;  
  30.  
  31.         Cache cache = CacheSingleton.getInstance().getCache();  
  32.  
  33.         if ("post".equalsIgnoreCase(request.getMethod()) || cache == null) {  
  34.             chain.doFilter(servletRequest, servletResponse);  
  35.         } else {  
  36.             String requestPath = request.getRequestURI();  
  37.             String queryString = request.getQueryString();  
  38.             if (queryString != null && queryString.length() > 0) {  
  39.                 requestPath += "?" + queryString;  
  40.             }  
  41.             String cachePath = PAGE_PREFIX + requestPath;  
  42.  
  43.             PageInfo page = null;  
  44.  
  45.             try {  
  46.                 page = (PageInfo) cache.get(cachePath);  
  47.             }  
  48.             catch (Exception e) {  
  49.                 // type mis-match  
  50.             }  
  51.  
  52.             if (page == null) {  // on cache content  
  53.                 ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();  
  54.                 GenericResponseWrapper wrapper = new GenericResponseWrapper(response, byteArrayOutputStream);  
  55.                 chain.doFilter(request, wrapper);  
  56.                 wrapper.flush();  
  57.  
  58.                 page = new PageInfo(wrapper.getStatus(), wrapper.getContentType(), wrapper.getHeaders(),  
  59.                         wrapper.getCookies(), byteArrayOutputStream.toByteArray());  
  60.  
  61.                 if (page.getStatus() == HttpServletResponse.SC_OK) {  
  62.                     cache.put(cachePath, page);  
  63.                 }  
  64.             }  
  65.  
  66.             response.setStatus(page.getStatus());  
  67.  
  68.             String contentType = page.getContentType();  
  69.             if (contentType != null && contentType.length() > 0) {  
  70.                 response.setContentType(contentType);  
  71.             }  
  72.  
  73.             for (Cookie cookie : (List) page.getCookies()) {  
  74.                 response.addCookie(cookie);  
  75.             }  
  76.  
  77.             for (String[] header : (List) page.getResponseHeaders()) {  
  78.                 response.setHeader(header[0], header[1]);  
  79.             }  
  80.  
  81.             response.setContentLength(page.getBody().length);  
  82.             BufferedOutputStream out = new BufferedOutputStream(response.getOutputStream());  
  83.             out.write(page.getBody());  
  84.             out.flush();  
  85.         }  
  86.     }  
  87.  
  88.     public void destroy() {  
  89.     }  
  90. }  
  91.  

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

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

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

FilterServletOutputStream.java

  1. package hover.blog.servlet;  
  2.  
  3. import javax.servlet.ServletOutputStream;  
  4. import java.io.IOException;  
  5. import java.io.OutputStream;  
  6.  
  7. /**  
  8.  * @author Hover  
  9.  * @version 1.0  
  10.  */ 
  11. public class FilterServletOutputStream extends ServletOutputStream {  
  12.  
  13.     private OutputStream stream;  
  14.  
  15.     public FilterServletOutputStream(final OutputStream stream) {  
  16.         this.stream = stream;  
  17.     }  
  18.  
  19.     /**  
  20.      * Writes to the stream.  
  21.      */ 
  22.     public void write(final int b) throws IOException {  
  23.         stream.write(b);  
  24.     }  
  25.  
  26.     /**  
  27.      * Writes to the stream.  
  28.      */ 
  29.     public void write(final byte[] b) throws IOException {  
  30.         stream.write(b);  
  31.     }  
  32.  
  33.     /**  
  34.      * Writes to the stream.  
  35.      */ 
  36.     public void write(final byte[] b, final int off, final int len) throws IOException {  
  37.         stream.write(b, off, len);  
  38.     }  
  39. }  

  40. GenericResponseWrapper.java  
  41. package hover.blog.servlet;  
  42.  
  43. import javax.servlet.ServletOutputStream;  
  44. import javax.servlet.http.Cookie;  
  45. import javax.servlet.http.HttpServletResponse;  
  46. import javax.servlet.http.HttpServletResponseWrapper;  
  47. import java.io.*;  
  48. import java.util.ArrayList;  
  49. import java.util.Collection;  
  50. import java.util.List;  
  51. import java.util.logging.Logger;  
  52.  
  53. /**  
  54.  * @author Hover  
  55.  * @version 1.0  
  56.  */ 
  57. @SuppressWarnings("unchecked")  
  58. public class GenericResponseWrapper extends HttpServletResponseWrapper implements Serializable {  
  59.  
  60.     private static final Logger LOG = Logger.getLogger(GenericResponseWrapper.class.getName());  
  61.     private int statusCode = SC_OK;  
  62.     private int contentLength;  
  63.     private String contentType;  
  64.     private final List headers = new ArrayList();  
  65.     private final List cookies = new ArrayList();  
  66.     private ServletOutputStream outstr;  
  67.     private PrintWriter writer;  
  68.  
  69.     /**  
  70.      * Creates a GenericResponseWrapper  
  71.      */ 
  72.     public GenericResponseWrapper(final HttpServletResponse response, final OutputStream outstr) {  
  73.         super(response);  
  74.         this.outstr = new FilterServletOutputStream(outstr);  
  75.     }  
  76.  
  77.     /**  
  78.      * Gets the outputstream.  
  79.      */ 
  80.     public ServletOutputStream getOutputStream() {  
  81.         return outstr;  
  82.     }  
  83.  
  84.     /**  
  85.      * Sets the status code for this response.  
  86.      */ 
  87.     public void setStatus(final int code) {  
  88.         statusCode = code;  
  89.         super.setStatus(code);  
  90.     }  
  91.  
  92.     /**  
  93.      * Send the error. If the response is not ok, most of the logic is bypassed and the error is sent raw  
  94.      * Also, the content is not cached.  
  95.      *  
  96.      * @param i      the status code  
  97.      * @param string the error message  
  98.      * @throws IOException  
  99.      */ 
  100.     public void sendError(int i, String string) throws IOException {  
  101.         statusCode = i;  
  102.         super.sendError(i, string);  
  103.     }  
  104.  
  105.     /**  
  106.      * Send the error. If the response is not ok, most of the logic is bypassed and the error is sent raw  
  107.      * Also, the content is not cached.  
  108.      *  
  109.      * @param i the status code  
  110.      * @throws IOException  
  111.      */ 
  112.     public void sendError(int i) throws IOException {  
  113.         statusCode = i;  
  114.         super.sendError(i);  
  115.     }  
  116.  
  117.     /**  
  118.      * Send the redirect. If the response is not ok, most of the logic is bypassed and the error is sent raw.  
  119.      * Also, the content is not cached.  
  120.      *  
  121.      * @param string the URL to redirect to  
  122.      * @throws IOException  
  123.      */ 
  124.     public void sendRedirect(String string) throws IOException {  
  125.         statusCode = HttpServletResponse.SC_MOVED_TEMPORARILY;  
  126.         super.sendRedirect(string);  
  127.     }  
  128.  
  129.     /**  
  130.      * Sets the status code for this response.  
  131.      */ 
  132.     public void setStatus(final int code, final String msg) {  
  133.         statusCode = code;  
  134.         LOG.warning("Discarding message because this method is deprecated.");  
  135.         super.setStatus(code);  
  136.     }  
  137.  
  138.     /**  
  139.      * Returns the status code for this response.  
  140.      */ 
  141.     public int getStatus() {  
  142.         return statusCode;  
  143.     }  
  144.  
  145.     /**  
  146.      * Sets the content length.  
  147.      */ 
  148.     public void setContentLength(final int length) {  
  149.         this.contentLength = length;  
  150.         super.setContentLength(length);  
  151.     }  
  152.  
  153.     /**  
  154.      * Gets the content length.  
  155.      */ 
  156.     public int getContentLength() {  
  157.         return contentLength;  
  158.     }  
  159.  
  160.     /**  
  161.      * Sets the content type.  
  162.      */ 
  163.     public void setContentType(final String type) {  
  164.         this.contentType = type;  
  165.         super.setContentType(type);  
  166.     }  
  167.  
  168.     /**  
  169.      * Gets the content type.  
  170.      */ 
  171.     public String getContentType() {  
  172.         return contentType;  
  173.     }  
  174.  
  175.  
  176.     /**  
  177.      * Gets the print writer.  
  178.      */ 
  179.     public PrintWriter getWriter() throws IOException {  
  180.         if (writer == null) {  
  181.             writer = new PrintWriter(new OutputStreamWriter(outstr, getCharacterEncoding()), true);  
  182.         }  
  183.         return writer;  
  184.     }  
  185.  
  186.     /**  
  187.      * Adds a header.  
  188.      */ 
  189.     public void addHeader(final String name, final String value) {  
  190.         final String[] header = new String[]{name, value};  
  191.         headers.add(header);  
  192.         super.addHeader(name, value);  
  193.     }  
  194.  
  195.     /**  
  196.      * @see #addHeader  
  197.      */ 
  198.     public void setHeader(final String name, final String value) {  
  199.         addHeader(name, value);  
  200.     }  
  201.  
  202.     /**  
  203.      * Gets the headers.  
  204.      */ 
  205.     public List getHeaders() {  
  206.         return headers;  
  207.     }  
  208.  
  209.     /**  
  210.      * Adds a cookie.  
  211.      */ 
  212.     public void addCookie(final Cookie cookie) {  
  213.         cookies.add(cookie);  
  214.         super.addCookie(cookie);  
  215.     }  
  216.  
  217.     /**  
  218.      * Gets all the cookies.  
  219.      */ 
  220.     public List getCookies() {  
  221.         return cookies;  
  222.     }  
  223.  
  224.     /**  
  225.      * Flushes buffer and commits response to client.  
  226.      */ 
  227.     public void flushBuffer() throws IOException {  
  228.         flush();  
  229.         super.flushBuffer();  
  230.     }  
  231.  
  232.     /**  
  233.      * Resets the response.  
  234.      */ 
  235.     public void reset() {  
  236.         super.reset();  
  237.         cookies.clear();  
  238.         headers.clear();  
  239.         statusCode = SC_OK;  
  240.         contentType = null;  
  241.         contentLength = 0;  
  242.     }  
  243.  
  244.     /**  
  245.      * Resets the buffers.  
  246.      */ 
  247.     public void resetBuffer() {  
  248.         super.resetBuffer();  
  249.     }  
  250.  
  251.     /**  
  252.      * Flushes all the streams for this response.  
  253.      */ 
  254.     public void flush() throws IOException {  
  255.         if (writer != null) {  
  256.             writer.flush();  
  257.         }  
  258.         outstr.flush();  
  259.     }  
  260. }  

  261. PageInfo.java  
  262. package hover.blog.servlet;  
  263.  
  264. import java.io.Serializable;  
  265. import java.util.List;  
  266.  
  267. /**  
  268.  * @author Hover  
  269.  * @version 1.0  
  270.  */ 
  271. public class PageInfo implements Serializable {  
  272.     private int status;  
  273.     private String contentType;  
  274.     private List responseHeaders;  
  275.     private List cookies;  
  276.     private byte[] body;  
  277.  
  278.     public PageInfo() {  
  279.     }  
  280.  
  281.     public PageInfo(int status, String contentType, List responseHeaders, List cookies, byte[] body) {  
  282.         this.status = status;  
  283.         this.contentType = contentType;  
  284.         this.responseHeaders = responseHeaders;  
  285.         this.cookies = cookies;  
  286.         this.body = body;  
  287.     }  
  288.  
  289.     public int getStatus() {  
  290.         return status;  
  291.     }  
  292.  
  293.     public void setStatus(int status) {  
  294.         this.status = status;  
  295.     }  
  296.  
  297.     public String getContentType() {  
  298.         return contentType;  
  299.     }  
  300.  
  301.     public void setContentType(String contentType) {  
  302.         this.contentType = contentType;  
  303.     }  
  304.  
  305.     public List getResponseHeaders() {  
  306.         return responseHeaders;  
  307.     }  
  308.  
  309.     public void setResponseHeaders(List responseHeaders) {  
  310.         this.responseHeaders = responseHeaders;  
  311.     }  
  312.  
  313.     public List getCookies() {  
  314.         return cookies;  
  315.     }  
  316.  
  317.     public void setCookies(List cookies) {  
  318.         this.cookies = cookies;  
  319.     }  
  320.  
  321.     public byte[] getBody() {  
  322.         return body;  
  323.     }  
  324.  
  325.     public void setBody(byte[] body) {  
  326.         this.body = body;  
  327.     }  
  328. }  
  329.  

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

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

  1. <?xml version="1.0" encoding="UTF-8"?> 
  2. <web-app xmlns="http://java.sun.com/xml/ns/javaee" 
  3.          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
  4.          xsi:schemaLocation="http://java.sun.com/xml/ns/javaee  
  5.     http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"  
  6.          version="2.5"> 
  7.     <filter> 
  8.         <filter-name>webCache</filter-name> 
  9.         <filter-class>hover.blog.servlet.WebCacheFilter</filter-class> 
  10.     </filter> 
  11.  
  12.     <filter-mapping> 
  13.         <filter-name>webCache</filter-name> 
  14.         <url-pattern>/main</url-pattern> 
  15.     </filter-mapping> 
  16.  
  17.     <filter-mapping> 
  18.         <filter-name>webCache</filter-name> 
  19.         <url-pattern>/blog</url-pattern> 
  20.     </filter-mapping> 
  21.  
  22.     <filter-mapping> 
  23.         <filter-name>webCache</filter-name> 
  24.         <url-pattern>/category</url-pattern> 
  25.     </filter-mapping> 
  26. </web-app> 
  27.  

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

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

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

jsp文件:

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

如果管理员先访问了页面,则缓存中保存的页面中,就包含了“编辑”的链接。当另外一个普通用户访问同一个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上实现文档存储和搜索
责任编辑:yangsai 来源: JavaEye博客
相关推荐

2023-05-10 10:30:02

性能优化Tomcat

2021-07-16 23:01:03

SQL索引性能

2015-03-04 09:29:53

GoogleAndroid

2022-07-21 18:51:13

性能优化

2021-07-26 18:23:23

SQL策略优化

2017-01-19 19:07:28

iOS进阶性能优化

2015-05-30 10:04:24

线下公开课51CTO沙龙MDSA

2016-11-17 09:00:46

HBase优化策略

2017-03-01 20:53:56

HBase实践

2016-08-12 10:23:28

javascriptChrome前端

2018-06-27 08:21:31

前端Web渲染

2014-12-10 10:12:02

Web

2010-05-05 11:48:27

Oracle设计开发阶

2017-08-14 09:05:50

SIOC存储负载

2024-03-01 12:19:00

接口性能优化

2022-02-16 14:10:51

服务器性能优化Linux

2021-11-29 11:13:45

服务器网络性能

2011-08-03 16:51:01

jQuery

2009-06-16 16:10:59

Hibernate性能

2013-06-09 15:31:35

jQueryjQuery优化性能优化
点赞
收藏

51CTO技术栈公众号