GAE Java 应用性能优化
转载请保留作者信息:
作者:88250
日期:2011 年 2 月 8 日
本文是作者开发 GAE 应用的性能优化经验谈,主要从框架、缓存、异步调用等方面介绍了如何进行高性能 GAE 应用的设计及优化。
ToC
使用 Memcache 进行缓存
使用缓存主要是为了提高响应速度。
HTML 页面
页面可以根据功能来划分进行缓存,请参考:应用 memcached 提升站点性能——减少读自数据库和数据源。
当然,也可以缓存整个页面,简化缓存及模板处理逻辑。
数据查询结果
如果你是直接使用 GAE DatastoreService 来进行数据查询,那么有必要缓存查询结果:
- 单一实体
根据唯一标识进行查询的实体。 - 集合实体
根据组合查询条件查询的实体,例如分页结果。
Memcache 使用注意
- 调用 GAE Memcache 也是会消耗一定 CPU 的(需要进行序列化/反序列化)
- 使用 GAE MemcaheService#clearAll() 方法会清除所有命名空间的缓存
减少内存使用
减少内存使用主要是为了充分利用有限的 Memcache 服务,降低实例启动时间。
尽量避免使用框架
虽然目前一些流行的 Java Web 框架(Spring、Struts、Play!、etc)是可以运行在 GAE 上的,但如果你想尽量免费地使用 GAE,
那最好还是不要使用现有框架,因为,大多数框架:
- 不是专门为 GAE 设计的
- 比较消耗内存
- 将延长启动时间
另外,关于 JSP 与其他模板引擎的优劣取舍问题这里不讨论。目前经测试无页面缓冲情况下,FreeMarker 与 JSP 性能非常接近。
无状态设计
有状态的设计是比较吃内存的,服务端最好少使用有状态的模型,除非要处理复杂的业务逻辑(例如多步表单、高级搜索)。
异步 APIs
GAE 提供了数据存取、HTTP 请求等 APIs 的异步版本。但需要注意:
- 目前 GAE/J(1.4.0)中的 Query 是没有异步 APIs 的
AsyncDatastoreService 或者 DatastoreService 上使用 PreparedQuery.asIterable() 与 PreparedQuery.asIterator()
效果一样,都是调用了就返回,迭代时才真正去获取数据 - 阻塞(同步)点
异步 APIs 调用后的代码块中要注意在哪个点进行同步,过早进行同步将降低异步 APIs 带来的优势。 - 调用异步 APIs 与调用同步 APIs 消耗同样的配额
其他
静态资源
仔细配置 appengine-web.xml 中 <static-files> 元素,这些资源将从单独的 Google 服务器、缓存获取,并不占用
应用服务器配额。
实体组
实体组是具有一定逻辑关系的、保存在同一 GAE 云存储区域的实体集。事务操作只能针对同一实体组的实体操作。
实体组对性能有一定影响(目前尚未实践证明影响有多大),但请尽量保持实体组的最小化。
域名解析
GAE 送的二级域名(*.appspot.com)在国内访问非常不稳定,所以最好绑定绑定自己的域名。
但绑定域名时需要配置 GHS IP,目前在国内已经没有 IP 可用。进一步,需要配置反向代理来进行请求代理。
在服务端性能优化后需要选择一个速度快、稳定的反向代理。不然,辛辛苦苦在服务端优化降低了几百 ms 的处理时间,
结果全耗在路由、丢包上了 :-(
如果免费的反向代理实在不能够满足性能需要,只能自己打个付费的了,或者等到 GHS 可用。。。。
实现逻辑
另外,目前 GAE 给的免费 CPU 配额比较少,所以优化实现逻辑以减少 CPU、APIs 使用也是非常关键的。
写代码时应该时刻注意代码的逻辑是否足够简洁,能一次性获取的参数就别重复获取,获取后以方法参数的形式进行传递。
参考资料
- App Engine Java Overview
- Best practices for writing scalable applications
- GAE SDK Javadoc
- GAE 数据存储——事务
- GAE/Java 应用框架——B3log Latke