<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>润物无声 &#187; 云计算</title>
	<atom:link href="http://blog.zhourunsheng.com/category/%e8%bd%af%e4%bb%b6%e5%bc%80%e5%8f%91/%e4%ba%91%e8%ae%a1%e7%ae%97/feed/" rel="self" type="application/rss+xml" />
	<link>http://blog.zhourunsheng.com</link>
	<description>天空一朵雨做的云</description>
	<lastBuildDate>Sat, 08 May 2021 05:17:21 +0000</lastBuildDate>
	<language>zh-CN</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>https://wordpress.org/?v=4.1.41</generator>
	<item>
		<title>体验物联网</title>
		<link>http://blog.zhourunsheng.com/2013/09/%e4%bd%93%e9%aa%8c%e7%89%a9%e8%81%94%e7%bd%91/</link>
		<comments>http://blog.zhourunsheng.com/2013/09/%e4%bd%93%e9%aa%8c%e7%89%a9%e8%81%94%e7%bd%91/#comments</comments>
		<pubDate>Mon, 16 Sep 2013 01:47:15 +0000</pubDate>
		<dc:creator><![CDATA[润物无声]]></dc:creator>
				<category><![CDATA[云计算]]></category>
		<category><![CDATA[OpenSCADA]]></category>
		<category><![CDATA[Ubuntu]]></category>
		<category><![CDATA[Weixin]]></category>
		<category><![CDATA[物联网]]></category>

		<guid isPermaLink="false">http://blog.zhourunsheng.com/?p=1847</guid>
		<description><![CDATA[<p>上个星期初步接触了物联网，正好工作手头有这些基本的器件，可以打造一个最简单的物联网环境，体验一下。本文的目的是 [&#8230;]</p>
<p><a rel="nofollow" href="http://blog.zhourunsheng.com/2013/09/%e4%bd%93%e9%aa%8c%e7%89%a9%e8%81%94%e7%bd%91/">体验物联网</a>，首发于<a rel="nofollow" href="http://blog.zhourunsheng.com">润物无声</a>。</p>
]]></description>
				<content:encoded><![CDATA[<p>上个星期初步接触了物联网，正好工作手头有这些基本的器件，可以打造一个最简单的物联网环境，体验一下。本文的目的是展示一个基于温湿度数据采集的报警系统，当温度或者湿度达到一定的临界值，自动打开报警器，并且微信通知系统管理员，系统管理员也可以通过微信查询当前环境的实时温湿度数据。</p>
<p>【系统整体架构】</p>
<p><img class="alignnone" alt="" src="http://farm8.staticflickr.com/7443/9766709544_5761c66bd3_o.png" width="542" height="442" /><br />
1. PC端本地通过OpenScada系统采集温湿度的数据<br />
2. PC端本地将采集到的温湿度数据定时上报到云端，供PAD查询<br />
3. PC端本地检测温湿度的阀值，启动或者关闭报警设备<br />
4. PC端本地检测到报警信息，及时给云端报告告警信息，云端通过微信系统推送到系统管理员账号上<br />
5. 系统管理员通道PAD微信系统，查询当前环境的数据<span id="more-1847"></span></p>
<p>【硬件环境】<br />
1. 工作PC，ubuntu系统，运行openscada数据采集系统<br />
2. PAD一台，手机也可以，模拟器也行，能跑微信就行<br />
3. USB-485 转接器，PC上没有485端口，需要转换成USB通信协议<br />
3. FT-02RL开关量输入输出模块(MODBUS-RTU协议)，软件控制继电器开关<br />
4. 温湿度传感器，基于485 modbus的数据传输协议</p>
<p>【硬件布线】<br />
1. 注意一下电源的正负极，不要接反了<br />
2. 注意485的A B数据端口，不要接反了<br />
3. 其他的没啥问题了</p>
<p><img class="alignnone" alt="" src="http://farm4.staticflickr.com/3771/9766710004_edcff96ea4_z.jpg" width="480" height="640" /></p>
<p><img class="alignnone" alt="" src="http://farm4.staticflickr.com/3674/9766812174_e66c67d61c_z.jpg" width="640" height="480" /><br />
【软件环境】<br />
1. 本地PC运行Ubuntu 12.04 OS<br />
2. 本地实现OpenScada数据采集系统，可以采集Modbus协议的数据，和基于Modbus协议控制外设<br />
3. 远端的PHP服务器，采用的我博客（http://blog.zhourunsheng.com）的后台，基于wordpress + 插件<br />
4. 微信公众号的接入，像我下面的微信号是专门为博客服务的</p>
<p>【演示数据查询】<br />
1. 加入公众微信号，查询关键字“zhou_runsheng”，或者扫描如下的二维码，添加</p>
<p><img class="alignnone" alt="" src="http://blog.zhourunsheng.com/wp-content/uploads/2013/04/qrcode_runsheng_blog.jpg" width="430" height="430" /><br />
2. 发送查询指令，例如“查询温湿度”，返回当前环境温湿度数据<br />
&lt;&lt;&lt; 查询温湿度<br />
&gt;&gt;&gt; 温湿度数据：<br />
温度：33.5 度<br />
湿度：88.7%<br />
露点：18.7 度</p>
<p>3. 其他的指令还没有加入，如果输入其他的查询信息，默认会按照该关键词来搜索相关的博文展示</p>
<p><img class="alignnone" alt="" src="http://farm3.staticflickr.com/2809/9766669722_bb9fb18613_z.jpg" width="640" height="480" /></p>
<p>【演示报警】<br />
1. 加热温湿度采集器（O(∩_∩)O，用火烤烤，甭烧毁仪器就行）<br />
2. 加湿温湿度采集器（最简单的办法，对着仪器哈口气，湿度立马上升到99.99%）<br />
3. 当温湿度达到管理员配置的临界阀值（比如温度超过33摄氏度），产生报警<br />
4. 报警产生，报警器响铃，微信推送通知系统管理员</p>
<p><img class="alignnone" alt="" src="http://farm3.staticflickr.com/2814/9766609931_e7046c61bb_z.jpg" width="640" height="480" /></p>
<p>5. 湿度报警, 继电器开关打开,左数第一个亮起来的红灯</p>
<p><img class="alignnone" alt="" src="http://farm3.staticflickr.com/2870/9766709664_e8c524555d_z.jpg" width="640" height="480" /></p>
<p>【未来】<br />
目前仅仅实现了简单的数据采集+报警应急处理，推广到其他地方，比如智能家居，智能农田灌溉，智能监控系统等等，O(∩_∩)O哈哈~，先想想怎么样打造一套智能的家居吧~~~，把家里联网的东西全控制了。</p>
<p><a rel="nofollow" href="http://blog.zhourunsheng.com/2013/09/%e4%bd%93%e9%aa%8c%e7%89%a9%e8%81%94%e7%bd%91/">体验物联网</a>，首发于<a rel="nofollow" href="http://blog.zhourunsheng.com">润物无声</a>。</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.zhourunsheng.com/2013/09/%e4%bd%93%e9%aa%8c%e7%89%a9%e8%81%94%e7%bd%91/feed/</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
		<item>
		<title>Heroku 上的 Play Framework（Java）</title>
		<link>http://blog.zhourunsheng.com/2011/09/heroku-%e4%b8%8a%e7%9a%84-play-framework%ef%bc%88java%ef%bc%89/</link>
		<comments>http://blog.zhourunsheng.com/2011/09/heroku-%e4%b8%8a%e7%9a%84-play-framework%ef%bc%88java%ef%bc%89/#comments</comments>
		<pubDate>Sun, 04 Sep 2011 03:06:06 +0000</pubDate>
		<dc:creator><![CDATA[润物无声]]></dc:creator>
				<category><![CDATA[云计算]]></category>
		<category><![CDATA[Heroku]]></category>
		<category><![CDATA[Java]]></category>

		<guid isPermaLink="false">http://blog.zhourunsheng.com/?p=706</guid>
		<description><![CDATA[<p>上周Heroku开始支持Java程序设计，具体的请参见我的上一篇博文《Heroku 上的 Java 程序设计》 [&#8230;]</p>
<p><a rel="nofollow" href="http://blog.zhourunsheng.com/2011/09/heroku-%e4%b8%8a%e7%9a%84-play-framework%ef%bc%88java%ef%bc%89/">Heroku 上的 Play Framework（Java）</a>，首发于<a rel="nofollow" href="http://blog.zhourunsheng.com">润物无声</a>。</p>
]]></description>
				<content:encoded><![CDATA[<p>上周Heroku开始支持Java程序设计，具体的请参见我的上一篇博文《<a title="Heroku 上的 Java 程序设计" href="http://blog.zhourunsheng.com/2011/08/heroku-%e4%b8%8a%e7%9a%84-java-%e7%a8%8b%e5%ba%8f%e8%ae%be%e8%ae%a1/">Heroku 上的 Java 程序设计</a>》，本周Heroku开始支持Play框架，本文就带领大家熟悉一下基于play框架的程序开发流程。</p>
<h3>Play是什么</h3>
<p>Play是 <a href="http://www.infoq.com/news/2011/07/play-composition">一个Java Web框架</a> 。针对Web开发，Play采用了“净室”方法，不会强加约束，例如：</p>
<blockquote><p>兼容Servlet容器、支持JSP、兼容标准Java Web App布局、顺应Java和OO原则。</p></blockquote>
<p>Play遵循Ruby on Rails的“Built-and-Deploy”模型，而不是更传统的“Package-and-Distribute”模型。</p>
<blockquote><p>不需要公式化的类或XML配置文件。框架采用了全新的打包惯例，在适当的地方使用了静态代码。举例来说，控制器入口点是无状态的，它面向HTTP而非面向对象，因此可以用静态方法来实现。</p></blockquote>
<p>Play基于无容器的PaaS模型。Play应用可以运行于本地，也可以无缝地部署到生产环境。这样一来就可以简化部署工作流，消除由环境差异导致的问题。<br />
从架构角度来看，Play使用了<a href="http://www.jboss.org/netty">Netty</a>，这是一个由<a href="http://www.jboss.org/about.html">JBoss团队</a>构建的非阻塞I/O协议库，它使用基于Continuation的编程模型，可以支持请求的异步处理。Play还实现了<a href="http://www.infoq.com/presentations/Horizontal-Scalability">Share-Nothing</a>模型，可以很方便地通过添加节点对应于程序进行水平扩展，有状态会话是无法做到这点的。<span id="more-706"></span></p>
<h3>怎样使用Play</h3>
<ol>
<li>安装 heroku 客户端开发环境，参见  <a href="http://toolbelt.herokuapp.com/linux/readme">Linux</a>, <a href="http://toolbelt.herokuapp.com/osx/download">Mac</a>,  <a href="http://toolbelt.herokuapp.com/windows/download">Windows</a>.</li>
<li>安装 <a href="http://git-scm.com/">git</a>  客户端软件和配置 ssh key，参见 <a href="http://help.github.com/mac-key-setup/">Mac</a>，<a href="http://help.github.com/msysgit-key-setup/">Windows</a> 和 <a href="http://help.github.com/linux-key-setup/">Linux</a></li>
<li>安装 <a href="http://www.playframework.org/download">Play! version 1.2.3</a></li>
<li>登录 Heroku :
<pre>heroku auth:login</pre>
</li>
<li>创建一个 Play! app:
<pre>play new play_hello_carey
cd play_hello_carey</pre>
<p><img class="alignnone size-medium wp-image-708" title="new_play_hello_carey" src="http://blog.zhourunsheng.com/wp-content/uploads/2011/09/new_play_hello_carey-300x173.png" alt="" width="300" height="173" /></li>
<li>本地运行 app :
<pre>play run --%production</pre>
<p><img class="alignnone size-medium wp-image-712" title="play_run_production" src="http://blog.zhourunsheng.com/wp-content/uploads/2011/09/play_run_production-300x173.png" alt="" width="300" height="173" /></li>
<li>建立 git repo:
<pre>git init
git add app conf lib public test
git commit -m init</pre>
</li>
<li>在 Heroku 上建立一个新的 app :
<pre>heroku create -s cedar</pre>
<p><img class="alignnone size-medium wp-image-713" title="create_new_app" src="http://blog.zhourunsheng.com/wp-content/uploads/2011/09/create_new_app-300x42.png" alt="" width="300" height="42" /></li>
<li>上传 play_hello_carey app 到 Heroku:
<pre>git push heroku master</pre>
</li>
<li>运行 app，浏览器中访问app网址 :
<pre>heroku open</pre>
<p><img class="alignnone size-medium wp-image-714" title="run_app" src="http://blog.zhourunsheng.com/wp-content/uploads/2011/09/run_app-300x194.png" alt="" width="300" height="194" /></li>
</ol>
<p>通过这个流程，相信大家可以知道怎样在Heroku上面部署安装基于Play框架的Java程序了。</p>
<h4>参考文献</h4>
<ul>
<li><a href="http://www.jamesward.com/2011/08/29/getting-started-with-play-framework-on-heroku">getting-started-with-play-framework-on-heroku</a></li>
<li><a href="http://www.infoq.com/cn/news/2011/09/play-heroku">play-heroku</a></li>
</ul>
<p><a rel="nofollow" href="http://blog.zhourunsheng.com/2011/09/heroku-%e4%b8%8a%e7%9a%84-play-framework%ef%bc%88java%ef%bc%89/">Heroku 上的 Play Framework（Java）</a>，首发于<a rel="nofollow" href="http://blog.zhourunsheng.com">润物无声</a>。</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.zhourunsheng.com/2011/09/heroku-%e4%b8%8a%e7%9a%84-play-framework%ef%bc%88java%ef%bc%89/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Heroku 上的 Java 程序设计</title>
		<link>http://blog.zhourunsheng.com/2011/08/heroku-%e4%b8%8a%e7%9a%84-java-%e7%a8%8b%e5%ba%8f%e8%ae%be%e8%ae%a1/</link>
		<comments>http://blog.zhourunsheng.com/2011/08/heroku-%e4%b8%8a%e7%9a%84-java-%e7%a8%8b%e5%ba%8f%e8%ae%be%e8%ae%a1/#comments</comments>
		<pubDate>Sat, 27 Aug 2011 02:07:57 +0000</pubDate>
		<dc:creator><![CDATA[润物无声]]></dc:creator>
				<category><![CDATA[云计算]]></category>
		<category><![CDATA[Heroku]]></category>
		<category><![CDATA[Java]]></category>

		<guid isPermaLink="false">http://blog.zhourunsheng.com/?p=698</guid>
		<description><![CDATA[<p>Heroku简介 Heroku is a Polyglot Cloud Application Platfor [&#8230;]</p>
<p><a rel="nofollow" href="http://blog.zhourunsheng.com/2011/08/heroku-%e4%b8%8a%e7%9a%84-java-%e7%a8%8b%e5%ba%8f%e8%ae%be%e8%ae%a1/">Heroku 上的 Java 程序设计</a>，首发于<a rel="nofollow" href="http://blog.zhourunsheng.com">润物无声</a>。</p>
]]></description>
				<content:encoded><![CDATA[<h3>Heroku简介</h3>
<p>Heroku is a <a href="http://blog.heroku.com/archives/2011/8/3/polyglot_platform/">Polyglot Cloud Application Platform</a>. Heroku provides us a way to run Ruby, Node.js, Clojure, and Java applications on a managed, scalable, and multi-tenant system. Heroku also provides <a href="http://addons.heroku.com/">numerous add-ons</a> that help us make the <a href="http://www.jamesward.com/2011/07/12/architectural-evolution-from-middleware-to-the-cloud">shift from monolithic middleware to Cloud Components</a>. Another way to say it is:</p>
<blockquote><p>Heroku = Polyglot + Platform as a Service (PaaS) + Cloud Components</p></blockquote>
<p>Heroku是一个支持<a href="http://blog.heroku.com/archives/2011/8/3/polyglot_platform/">多语言的云应用平台</a>。 它为我们提供了一个支撑的环境来运行Ruby，Node.js，Clojure，和Java应用程序，并且是可扩展，多租户的系统。 Heroku还提供了众多的<a href="http://addons.heroku.com/">组件</a>，帮助我们从<a href="http://www.jamesward.com/2011/07/12/architectural-evolution-from-middleware-to-the-cloud">单片中间件转移到云组件</a>。另一种说法是：</p>
<blockquote><p>Heroku = 多语种 + 平台作为服务（PaaS）+ 云组件</p></blockquote>
<p><span id="more-698"></span></p>
<h3>Heroku 上运行Java程序</h3>
<p>Heroku can run any Java app that runs in OpenJDK 6. Today Heroku uses Maven to create a “<a href="http://devcenter.heroku.com/articles/slug-compiler">slug</a>” for Java apps. That slug can then be loaded onto one or more “<a href="http://devcenter.heroku.com/articles/dynos">dynos</a>“. You can tell a dyno to execute / start a Java app from the command line and you can also use a “<a href="http://devcenter.heroku.com/articles/procfile">Procfile</a>” to provide a command that will auto-start for each instance of a specific dyno type. Web dynos are able to listen on a port and will receive HTTP traffic through a load balancer that is automatically setup for each app. With that background knowledge, lets dive into code!</p>
<p>Heroku 的Java运行时环境为OpenJDK 6，通过用Maven来编译程序，然后将程序上传到云端，用命令行方式来启动执行Java应用程序，云端会根据HTTP流量自动调节负载平衡，并启动一个运行实例。</p>
<p>具体步骤如下：</p>
<ol>
<li>安装 heroku 客户端软件，参见  <a href="http://toolbelt.herokuapp.com/linux/readme">Linux</a>，<a href="http://toolbelt.herokuapp.com/osx/download">Mac</a> 和  <a href="http://toolbelt.herokuapp.com/windows/download">Windows</a></li>
<li>安装 <a href="http://git-scm.com/">git</a>  客户端软件和配置 ssh key，参见 <a href="http://help.github.com/mac-key-setup/">Mac</a>，<a href="http://help.github.com/msysgit-key-setup/">Windows</a> 和 <a href="http://help.github.com/linux-key-setup/">Linux</a></li>
<li>安装 <a href="http://maven.apache.org/">Maven</a></li>
<li>从命令行登入 Heroku :
<div>
<div>
<pre>heroku auth:login
依次输入Email和Password，如果是第一次登录的话会要求上传SSH key文件，选择yes就行，然后heroku会将这些信息保存在本地，以后登录就不需要再次输入了。</pre>
</div>
</div>
</li>
<li>建立一个新的工程目录:
<div>
<div>
<pre>mkdir herokuhellojava
cd herokuhellojava</pre>
</div>
</div>
</li>
<li>新建 Maven 配置文件 <code>pom.xml</code>:
<div>
<div>
<pre>&lt;?xml version="1.0" encoding="UTF-8"?&gt;
&lt;project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"&gt;
    &lt;modelVersion&gt;4.0.0&lt;/modelVersion&gt;
    &lt;groupId&gt;foo&lt;/groupId&gt;
    &lt;version&gt;1.0-SNAPSHOT&lt;/version&gt;
    &lt;name&gt;herokuhellojava&lt;/name&gt;
    &lt;artifactId&gt;herokuhellojava&lt;/artifactId&gt;
&lt;/project&gt;</pre>
</div>
</div>
</li>
<li>建立Java source 目录:
<div>
<div>
<pre>mkdir -p src/main/java</pre>
</div>
</div>
</li>
<li>在 <code>src/main/java</code> 目录中新建立一个Java 源文件 <code>Hello.java</code> :
<div>
<div>
<pre>public class Hello
{
  public static void main(String[] args)
  {
    System.out.println("hello, Carey");
  }
}</pre>
</div>
</div>
</li>
<li>编译工程项目:
<div>
<div>
<pre>mvn compile</pre>
</div>
</div>
</li>
<li>本地运行Java程序:
<div>
<div>
<pre>java -cp target/classes Hello
程序输出 hello, Carey</pre>
</div>
</div>
</li>
<li>建立git本地repo，然后将 <code>pom.xml</code> 和 <code>src</code> 文件夹加入进去:
<div>
<div>
<pre>git init
git add pom.xml src
git commit -m init</pre>
</div>
</div>
</li>
<li>利用cedar栈在Heroku上建立一个新的APP:
<div>
<div>
<pre>heroku create -s cedar
程序输出新建立的APP的信息，比如 stark-lightning-113</pre>
</div>
</div>
</li>
<li>把本地的java程序上传到Heroku:
<div>
<div>
<pre>git push heroku master</pre>
</div>
</div>
<p>Heroku 会自动建立一个 与此APP对应的 slug .</li>
<li>在 Heroku上远程运行java程序:
<div>
<div>
<pre>heroku run "java -cp target/classes Hello"</pre>
</div>
</div>
<p>Heroku 会新建立一个 dyno 环境，装载此APP的 slug，然后运行该APP</p>
<p><img title="herokuhellojava" src="http://blog.zhourunsheng.com/wp-content/uploads/2011/08/herokuhellojava-300x173.jpg" alt="" width="300" height="173" /></li>
</ol>
<p>到目前为止，你已经开始在云端运行Java程序了，虽然只是个简单的Demo程序，但是至少知道了整个部署和执行的过程，接下来要学习的东西还有很多很多。</p>
<h3>下一步工作</h3>
<ul>
<li>阅读书籍 <a href="http://github.com/heroku/java-workbook">Heroku for Java Workbook</a></li>
<li>阅读文章 <a href="http://devcenter.heroku.com/articles/java">Heroku Dev Center</a></li>
<li>问题讨论 <a href="http://stackoverflow.com/">StackOverflow</a></li>
</ul>
<h3>参考文献</h3>
<p><a href="http://www.jamesward.com/2011/08/25/heroku-adds-java-support">heroku-adds-java-support</a></p>
<p><a rel="nofollow" href="http://blog.zhourunsheng.com/2011/08/heroku-%e4%b8%8a%e7%9a%84-java-%e7%a8%8b%e5%ba%8f%e8%ae%be%e8%ae%a1/">Heroku 上的 Java 程序设计</a>，首发于<a rel="nofollow" href="http://blog.zhourunsheng.com">润物无声</a>。</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.zhourunsheng.com/2011/08/heroku-%e4%b8%8a%e7%9a%84-java-%e7%a8%8b%e5%ba%8f%e8%ae%be%e8%ae%a1/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>阿里云应用托管（ACE）搭建 wordpress 博客</title>
		<link>http://blog.zhourunsheng.com/2011/07/%e9%98%bf%e9%87%8c%e4%ba%91%e5%ba%94%e7%94%a8%e6%89%98%e7%ae%a1%ef%bc%88ace%ef%bc%89%e6%90%ad%e5%bb%ba-wordpress-%e5%8d%9a%e5%ae%a2/</link>
		<comments>http://blog.zhourunsheng.com/2011/07/%e9%98%bf%e9%87%8c%e4%ba%91%e5%ba%94%e7%94%a8%e6%89%98%e7%ae%a1%ef%bc%88ace%ef%bc%89%e6%90%ad%e5%bb%ba-wordpress-%e5%8d%9a%e5%ae%a2/#comments</comments>
		<pubDate>Sat, 30 Jul 2011 01:58:15 +0000</pubDate>
		<dc:creator><![CDATA[润物无声]]></dc:creator>
				<category><![CDATA[云计算]]></category>
		<category><![CDATA[ACE]]></category>
		<category><![CDATA[WordPress]]></category>

		<guid isPermaLink="false">http://blog.zhourunsheng.com/?p=575</guid>
		<description><![CDATA[<p>成功搭建ACE应用：http://carey.aliapp.com, 步骤总结如下： 访问 http://ac [&#8230;]</p>
<p><a rel="nofollow" href="http://blog.zhourunsheng.com/2011/07/%e9%98%bf%e9%87%8c%e4%ba%91%e5%ba%94%e7%94%a8%e6%89%98%e7%ae%a1%ef%bc%88ace%ef%bc%89%e6%90%ad%e5%bb%ba-wordpress-%e5%8d%9a%e5%ae%a2/">阿里云应用托管（ACE）搭建 wordpress 博客</a>，首发于<a rel="nofollow" href="http://blog.zhourunsheng.com">润物无声</a>。</p>
]]></description>
				<content:encoded><![CDATA[<p>成功搭建ACE应用：<a href="http://carey.aliapp.com">http://carey.aliapp.com</a>, 步骤总结如下：</p>
<ol>
<li>访问 <a href="http://ace.aliyun.com/">http://ace.aliyun.com/</a>，申请ACE的CloudID，并且启动域名解析，目前在内测阶段，需要邀请码才可以</li>
<li>下载中文版 wordpress，下载地址：<a href="http://cn.wordpress.org/wordpress-3.2.1-zh_CN.zip">http://cn.wordpress.org/</a></li>
<li>解压 wordpress压缩包，并且修改部分文件，具体如下：</li>
<p><strong>a. </strong> 重命名 wp-config-sample.php 为 wp-config.php<br />
<strong>b. </strong> 修改 wp-config.php 中的数据库信息，数据库的详细信息可以参照自己的阿里云邮箱里面的邮件：<br />
信息如下：<br />
云数据库(RDS)应用信息<br />
云数据库服务名称：XXXXX<br />
云数据库类型:共享型<br />
配置级别:MYSQL数据库1G空间<br />
<span style="color: #ff00ff;">链接地址:XXXXX.mysql.aliyun.com:3306</span><br />
<span style="color: #ff00ff;">数据库(DB)名:XXXXX</span><br />
<span style="color: #ff00ff;"> 数据库账号:XXXXX</span><br />
<span style="color: #ff00ff;"> 数据库密码:XXXXX</span><br />
数据库备份周期:周一,周二,周三,周四,周五,周六,周日<br />
备份保存天数:7天<br />
<strong>c. </strong>修改wp-includes/canonical.php 第58行，改成如下，即去掉行首的注释符，阻止301无限重定向<br />
<del>#$original['path'] = preg_replace(‘|/index.php$|’, ‘/’, $original['path']);</del><br />
$original['path'] = preg_replace(‘|/index.php$|’, ‘/’, $original['path']);</p>
<li>用Ftp软件将wordpress文件夹中的全部文件上传到ACE的FTP服务器，我用的是Flashfxp</li>
<p>ftp的服务器信息参见阿里云里面邮件的信息：<br />
网站应用部署信息<br />
网站应用名称:润物无声<br />
二级域名:carey.aliapp.com<br />
开发语言:php<br />
<span style="color: #ff00ff;">文件上传FTP地址:ftp://ftp.ace.aliyun.com:2222</span><br />
<span style="color: #ff00ff;"> FTP用户名:carey</span><br />
<span style="color: #ff00ff;"> FTP密码:XXXXX</span></p>
<li>访问http://carey(ACE域名).aliapp.com/wp-admin/install.php 完成后续的安装过程</li>
</ol>
<p><a rel="nofollow" href="http://blog.zhourunsheng.com/2011/07/%e9%98%bf%e9%87%8c%e4%ba%91%e5%ba%94%e7%94%a8%e6%89%98%e7%ae%a1%ef%bc%88ace%ef%bc%89%e6%90%ad%e5%bb%ba-wordpress-%e5%8d%9a%e5%ae%a2/">阿里云应用托管（ACE）搭建 wordpress 博客</a>，首发于<a rel="nofollow" href="http://blog.zhourunsheng.com">润物无声</a>。</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.zhourunsheng.com/2011/07/%e9%98%bf%e9%87%8c%e4%ba%91%e5%ba%94%e7%94%a8%e6%89%98%e7%ae%a1%ef%bc%88ace%ef%bc%89%e6%90%ad%e5%bb%ba-wordpress-%e5%8d%9a%e5%ae%a2/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>通过GAE平台自动发布文章到百度空间</title>
		<link>http://blog.zhourunsheng.com/2011/07/%e9%80%9a%e8%bf%87gae%e5%b9%b3%e5%8f%b0%e8%87%aa%e5%8a%a8%e5%8f%91%e5%b8%83%e6%96%87%e7%ab%a0%e5%88%b0%e7%99%be%e5%ba%a6%e7%a9%ba%e9%97%b4/</link>
		<comments>http://blog.zhourunsheng.com/2011/07/%e9%80%9a%e8%bf%87gae%e5%b9%b3%e5%8f%b0%e8%87%aa%e5%8a%a8%e5%8f%91%e5%b8%83%e6%96%87%e7%ab%a0%e5%88%b0%e7%99%be%e5%ba%a6%e7%a9%ba%e9%97%b4/#comments</comments>
		<pubDate>Sat, 02 Jul 2011 09:37:47 +0000</pubDate>
		<dc:creator><![CDATA[润物无声]]></dc:creator>
				<category><![CDATA[云计算]]></category>
		<category><![CDATA[Baidu]]></category>
		<category><![CDATA[GAE]]></category>

		<guid isPermaLink="false">http://blog.zhourunsheng.com/?p=278</guid>
		<description><![CDATA[<p>昨天写了一篇通过Java程序自动发送文章到百度空间，具体实现原理和代码请参照&#60;&#60;Java程序自动发 [&#8230;]</p>
<p><a rel="nofollow" href="http://blog.zhourunsheng.com/2011/07/%e9%80%9a%e8%bf%87gae%e5%b9%b3%e5%8f%b0%e8%87%aa%e5%8a%a8%e5%8f%91%e5%b8%83%e6%96%87%e7%ab%a0%e5%88%b0%e7%99%be%e5%ba%a6%e7%a9%ba%e9%97%b4/">通过GAE平台自动发布文章到百度空间</a>，首发于<a rel="nofollow" href="http://blog.zhourunsheng.com">润物无声</a>。</p>
]]></description>
				<content:encoded><![CDATA[<p>昨天写了一篇通过Java程序自动发送文章到百度空间，具体实现原理和代码请参照&lt;&lt;<a href="http://carey.sinaapp.com/?p=271">Java程序自动发布文章到百度空间</a>&gt;&gt;</p>
<p>今天把那版代码进行了更新，现在可以放置到GAE平台了，这样基于GAE平台环境搭建的个人博客就可以</p>
<p>很方便的将个人的博文同步到百度空间。</p>
<p>具体的实现原理现在不多说了，现在展示一下核心代码：</p>
<p><strong>1.</strong> Config.Java</p>
<p>配置个人百度空间的账户名和密码</p>
<pre>package com.carey.baidublog;  

public class Config {    
 public static final String USERNAME = "username";    
 public static final String PASSWORD = "password";
}</pre>
<p><span id="more-278"></span></p>
<p><strong>2. </strong>GAEPublishBaiduActicleServlet.java</p>
<p>用来处理GAE的servlet请求</p>
<pre>package com.carey.gae.baidu;  
import java.io.IOException;
import javax.servlet.http.*;  
import com.carey.baidublog.BDHttpClient;  

public class GAEPublishBaiduActicleServlet extends HttpServlet {    
 private static final long serialVersionUID = 1L;      

 public void doGet(HttpServletRequest req, HttpServletResponse resp)  throws IOException {        
  try {         
   BDHttpClient.publishBlog("gae-title", "gae-content", "gae-category");        
  } catch (Exception e) {            
   e.printStackTrace();        
  }          

  resp.setContentType("text/plain");        
  resp.getWriter().println("成功发布一篇博文到百度空间");    
 }
}</pre>
<p><strong>3. </strong>BDHttpClient.java</p>
<p>完成百度博文的发布</p>
<pre>package com.carey.baidublog;  
import java.net.URL;
import java.util.HashMap;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;  
import javax.servlet.http.HttpServletResponse;  
import com.google.appengine.api.urlfetch.HTTPHeader;
import com.google.appengine.api.urlfetch.HTTPMethod;
import com.google.appengine.api.urlfetch.HTTPRequest;
import com.google.appengine.api.urlfetch.HTTPResponse;
import com.google.appengine.api.urlfetch.URLFetchService;
import com.google.appengine.api.urlfetch.URLFetchServiceFactory;  

public class BDHttpClient {    
 private static final String LOGIN_URL = "&lt;a href="https://passport.baidu.com/?login"&gt;https://passport.baidu.com/?login&lt;/a&gt;";    
 private static final String CREATBLOG_URL = "&lt;a href="http://hi.baidu.com/"&gt;http://hi.baidu.com/&lt;/a&gt;" + Config.USERNAME + "/creat/blog";    
 private static final String MODIFYCATEGORY_URL = "&lt;a href="http://hi.baidu.com/"&gt;http://hi.baidu.com/&lt;/a&gt;" + Config.USERNAME + "/modify/category/0";    
 private static final String COMMITBLOG_URL = "&lt;a href="http://hi.baidu.com/"&gt;http://hi.baidu.com/&lt;/a&gt;" + Config.USERNAME + "/commit";      
 private static final URLFetchService urlFetchService = URLFetchServiceFactory.getURLFetchService();    
 private static final HashMap globalCookies = new HashMap();      

 public static void publishBlog(String title, String content, String category) throws Exception {        
  // step 1, login baidu and get cookies        
  String cookies = LoginBaidu();          

  // step 2, get Bdstoken        
  String bdstoken = getBdstoken(cookies);          

  // step 3, publish article        
  if (bdstoken != null) {            
   postBlog(cookies, bdstoken, title, content, category);        
  } else {            
   throw new Exception("bdstoken == null");        
  }    
 }      

 private static String LoginBaidu() {        
  HashMap params = new HashMap();        
  params.put("username", Config.USERNAME);        
  params.put("password", Config.PASSWORD);        
  params.put("pwd", "1");         

  try {            
   httpPost(null, LOGIN_URL, generateQueryString(params));        
  } catch (Exception e) {        
   e.printStackTrace();        
  }         

  return globalCookies.get("set-cookie");    
 }    

 private static String getBdstoken(String cookies) {     
  try {            
   String res = httpGet(cookies, CREATBLOG_URL, null);       
   Pattern p = Pattern.compile("bdstoken=([0-9a-z]+)\W");        
   Matcher m = p.matcher(res);           

   if (m.find()) {                
    return m.group(1);            
   }      
  } catch (Exception e) {          
   e.printStackTrace();    
  }        

  return null;   
 }    

 private static String createCategory(String cookies, String bdstoken, String category) throws Exception {       
  HashMap params = new HashMap();          

  // bdstoken        
  params.put("bdstoken", bdstoken);          

  // create category        
  params.put("ct", "2");        
  params.put("cm", "1");          

  // Article category, such as "Android", "Google", "默认分类"        
  params.put("spBlogCatName", category);        
  params.put("spRefURL", MODIFYCATEGORY_URL);          

  return httpPost(cookies, COMMITBLOG_URL, generateQueryString(params));    
 }      

 private static String postBlog(String cookies, String bdstoken, String title, String content, String category) throws Exception {  
  HashMap params = new HashMap();          

  // bdstoken        
  params.put("bdstoken", bdstoken);          

  // new blog        
  params.put("ct", "1");        
  params.put("cm", "1");          

  // add a new article        
  params.put("spBlogID", "");        
  params.put("edithid", "");        
  params.put("spBlogCatName_o", "");          

  // Article title        
  params.put("spBlogTitle", title);          

  // Article content        
  params.put("spBlogText", content);          

  // Article category, such as "Android", "Google", "默认分类"        
  params.put("spBlogCatName", category);          

  // Article view Permissions,        
  // 0 --&gt; every one 1 --&gt; only friend 3 --&gt; only oneself       
  params.put("spBlogPower", "0");         

  // 0: forbidden comment 1: allow comment       
  params.put("spIsCmtAllow", "1");         

  // 0: allow share 1: forbidden share       
  params.put("spShareNotAllow", "0");         

  // verify code       
  params.put("spVcode", "");    
  params.put("spVerifyKey", "");         

  // first create category      
  createCategory(cookies, bdstoken, category);        

  return httpPost(cookies, COMMITBLOG_URL, generateQueryString(params));    
 }      

 private static String generateQueryString(HashMap params) {     
  StringBuffer sb = new StringBuffer();         
  if (params != null &amp;&amp; !params.isEmpty()) {          
   Set keys = params.keySet();            
   for (String key : keys) {            
    sb.append(key);               
    sb.append("=");               
    sb.append(params.get(key));       
    sb.append("&amp;");        
   }              

   // remove last &amp;           
   sb.deleteCharAt(sb.length() - 1);          
  }       

  return sb.toString();   
 }      

 private static String httpGet(String cookies, String url, String queryString) throws Exception {     
  String ret = null;         
  if (queryString != null &amp;&amp; !queryString.equals("")) {  
   url += "?" + queryString;        
  }          

  final HTTPRequest request = new HTTPRequest(new URL(url));        
  if (cookies != null) {           
   request.setHeader(new HTTPHeader("Cookie", cookies));       
  }

  try {            
   final HTTPResponse response = urlFetchService.fetch(request);          
   if (HttpServletResponse.SC_OK != response.getResponseCode()) {      
    System.err.println("HttpGet Method failed: " + response.getResponseCode());           
   }    

   ret = new String(response.getContent(), "UTF-8");       
  } catch (Exception e) {            
   throw new Exception(e);        
  }          

  return ret;    
 }     

 private static String httpPost(String cookies, String url, String queryString) throws Exception {        
  String ret = null;          
  final HTTPRequest request = new HTTPRequest(new URL(url), HTTPMethod.POST);        
  request.setHeader(new HTTPHeader("Content-Type", "application/x-www-form-urlencoded"));        

  if (cookies != null) {            
   request.setHeader(new HTTPHeader("Cookie", cookies));        
  }          

  if (queryString != null &amp;&amp; !queryString.equals("")) {            
   request.setPayload(queryString.getBytes("UTF-8"));        
  }          

  try {           
   final HTTPResponse response = urlFetchService.fetch(request);           
   if (HttpServletResponse.SC_OK != response.getResponseCode()) {                
    System.err.println("HttpPost Method failed: "  + response.getResponseCode());            
   }             

   // Get site cookie            
   for (HTTPHeader header : response.getHeaders()) {                
    if (header.getName().equalsIgnoreCase("set-cookie")) {                   
     globalCookies.put("set-cookie", header.getValue());                
    }     
   }     

   ret = new String(response.getContent(), "UTF-8");        
  } catch (Exception e) {         
   throw new Exception(e);   
  }         

  return ret;    
 }
}</pre>
<p>以上程序在GAE环境测试通过，运行过程序后会自动在百度空间发布一篇文章，截图如下：</p>
<p><a href="http://blog.zhourunsheng.com/wp-content/uploads/2011/07/gae-baidu.jpg"><img class="alignnone size-full wp-image-279" title="gae-baidu" src="http://blog.zhourunsheng.com/wp-content/uploads/2011/07/gae-baidu.jpg" alt="" width="500" height="270" /></a></p>
<p><a rel="nofollow" href="http://blog.zhourunsheng.com/2011/07/%e9%80%9a%e8%bf%87gae%e5%b9%b3%e5%8f%b0%e8%87%aa%e5%8a%a8%e5%8f%91%e5%b8%83%e6%96%87%e7%ab%a0%e5%88%b0%e7%99%be%e5%ba%a6%e7%a9%ba%e9%97%b4/">通过GAE平台自动发布文章到百度空间</a>，首发于<a rel="nofollow" href="http://blog.zhourunsheng.com">润物无声</a>。</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.zhourunsheng.com/2011/07/%e9%80%9a%e8%bf%87gae%e5%b9%b3%e5%8f%b0%e8%87%aa%e5%8a%a8%e5%8f%91%e5%b8%83%e6%96%87%e7%ab%a0%e5%88%b0%e7%99%be%e5%ba%a6%e7%a9%ba%e9%97%b4/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>利用Appspot访问外部资源</title>
		<link>http://blog.zhourunsheng.com/2011/07/%e5%88%a9%e7%94%a8appspot%e8%ae%bf%e9%97%ae%e5%a4%96%e9%83%a8%e8%b5%84%e6%ba%90/</link>
		<comments>http://blog.zhourunsheng.com/2011/07/%e5%88%a9%e7%94%a8appspot%e8%ae%bf%e9%97%ae%e5%a4%96%e9%83%a8%e8%b5%84%e6%ba%90/#comments</comments>
		<pubDate>Sat, 02 Jul 2011 06:26:16 +0000</pubDate>
		<dc:creator><![CDATA[润物无声]]></dc:creator>
				<category><![CDATA[云计算]]></category>
		<category><![CDATA[GAE]]></category>
		<category><![CDATA[外面的世界]]></category>

		<guid isPermaLink="false">http://blog.zhourunsheng.com/?p=213</guid>
		<description><![CDATA[<p>网上翻墙的技术层出不群，本人自己也使用过很多，用过之后，发现还是利用appspot 最实在，最方便。 既然要利 [&#8230;]</p>
<p><a rel="nofollow" href="http://blog.zhourunsheng.com/2011/07/%e5%88%a9%e7%94%a8appspot%e8%ae%bf%e9%97%ae%e5%a4%96%e9%83%a8%e8%b5%84%e6%ba%90/">利用Appspot访问外部资源</a>，首发于<a rel="nofollow" href="http://blog.zhourunsheng.com">润物无声</a>。</p>
]]></description>
				<content:encoded><![CDATA[<p>网上翻墙的技术层出不群，本人自己也使用过很多，用过之后，发现还是利用appspot 最实在，最方便。</p>
<p>既然要利用appspot 进行翻墙，那么首先得解决访问*.appspot .com的问题, 因为它本身就是一个墙外的东西，真是灰常像先有鸡还是先有蛋的两难问题啊。。。</p>
<p>幸好这个问题比较容易解决，现将解决方法罗列如下：</p>
<p>【本文用的是win7，xp神马的类似】</p>
<p>1. 建立自己的appspot 代理 gappproxy, 在 google code 上面下载其源代码工程 gappproxy-read-only，</p>
<p>fetchserver是用来上传到appspot的，比如我自己的代理地址为：http://careyproxy.appspot.com/fetch.py</p>
<p><img class="alignnone size-full wp-image-214" title="gappproxy" src="http://blog.zhourunsheng.com/wp-content/uploads/2011/07/gappproxy.jpg" alt="gappproxy" width="500" height="270" /></p>
<p>2. 修改hosts文件，win7 在 C:Windows\System32\drivers\etc 下面，注意需要管理员的权限，修改如下：</p>
<pre>203.208.39.104 careyproxy.appspot.com
209.85.225.101 sites.google.com
209.85.225.101 docs.google.com
209.85.225.101 spreadsheets.google.com
209.85.225.101 picasaweb.google.com</pre>
<p>3. 修改 gappproxy-read-onlylocalproxyproxy.conf 配置文件</p>
<pre>listen_port = 8008
fetch_server = http://careyproxy.appspot.com/fetch.py</pre>
<p>4. 运行 gappproxy-read-onlylocalproxy proxy.py，命令行输出如下：</p>
<p><img class="alignnone size-full wp-image-215" title="localproxy" src="http://blog.zhourunsheng.com/wp-content/uploads/2011/07/localproxy.jpg" alt="localproxy" width="500" height="326" /></p>
<p>说明服务已经启动了</p>
<p>5. 修改本机浏览器的代理地址为127.0.0.1：8008，试试是不是已经能访问facebook和twitter啦</p>
<p><img class="alignnone size-full wp-image-216" title="facebook" src="http://blog.zhourunsheng.com/wp-content/uploads/2011/07/facebook.jpg" alt="facebook" width="500" height="270" /></p>
<p>现在开始慢慢欣赏墙外的世界吧 。。。</p>
<p><a rel="nofollow" href="http://blog.zhourunsheng.com/2011/07/%e5%88%a9%e7%94%a8appspot%e8%ae%bf%e9%97%ae%e5%a4%96%e9%83%a8%e8%b5%84%e6%ba%90/">利用Appspot访问外部资源</a>，首发于<a rel="nofollow" href="http://blog.zhourunsheng.com">润物无声</a>。</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.zhourunsheng.com/2011/07/%e5%88%a9%e7%94%a8appspot%e8%ae%bf%e9%97%ae%e5%a4%96%e9%83%a8%e8%b5%84%e6%ba%90/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>探索GAE背后的奥秘之参考文章推荐</title>
		<link>http://blog.zhourunsheng.com/2011/07/%e6%8e%a2%e7%b4%a2gae%e8%83%8c%e5%90%8e%e7%9a%84%e5%a5%a5%e7%a7%98%e4%b9%8b%e5%8f%82%e8%80%83%e6%96%87%e7%ab%a0%e6%8e%a8%e8%8d%90/</link>
		<comments>http://blog.zhourunsheng.com/2011/07/%e6%8e%a2%e7%b4%a2gae%e8%83%8c%e5%90%8e%e7%9a%84%e5%a5%a5%e7%a7%98%e4%b9%8b%e5%8f%82%e8%80%83%e6%96%87%e7%ab%a0%e6%8e%a8%e8%8d%90/#comments</comments>
		<pubDate>Sat, 02 Jul 2011 04:56:32 +0000</pubDate>
		<dc:creator><![CDATA[润物无声]]></dc:creator>
				<category><![CDATA[云计算]]></category>
		<category><![CDATA[GAE]]></category>

		<guid isPermaLink="false">http://blog.zhourunsheng.com/?p=177</guid>
		<description><![CDATA[<p>Google's Dr. Kai-Fu Lee on Cloud Computing The Cost of  [&#8230;]</p>
<p><a rel="nofollow" href="http://blog.zhourunsheng.com/2011/07/%e6%8e%a2%e7%b4%a2gae%e8%83%8c%e5%90%8e%e7%9a%84%e5%a5%a5%e7%a7%98%e4%b9%8b%e5%8f%82%e8%80%83%e6%96%87%e7%ab%a0%e6%8e%a8%e8%8d%90/">探索GAE背后的奥秘之参考文章推荐</a>，首发于<a rel="nofollow" href="http://blog.zhourunsheng.com">润物无声</a>。</p>
]]></description>
				<content:encoded><![CDATA[<li><a href="http://perspectives.mvdirona.com/2008/06/25/GooglesDrKaiFuLeeOnCloudComputing.aspx">Google's Dr. Kai-Fu Lee on Cloud Computing</a></li>
<li><a href="http://perspectives.mvdirona.com/2009/10/31/TheCostOfLatency.aspx">The Cost of Latency</a></li>
<li><a href="http://googleappengine.blogspot.com/" target="_blank">Google App Engine Blog</a></li>
<li>Bigtable: A Distributed Storage System for Structured Data</li>
<li><a href="http://code.google.com/events/io/sessions/FromSparkPlugToDriveTrain.html" target="_blank">From Spark Plug to Drive Train: Life of an App Engine Request</a></li>
<li><a href="http://perspectives.mvdirona.com/2008/07/10/GoogleMegastore.aspx" target="_blank">Google Megastore</a></li>
<li><a href="http://code.google.com/intl/zh-CN/appengine/docs/" target="_blank">Google App Engine官方文档</a></li>
<li><a href="http://highscalability.com/google-architecture" target="_blank">Google Architecture</a></li>
<li><a href="http://highscalability.com/google-appengine-first-look" target="_blank">Google App Engine - a first look</a></li>
<li><a href="http://www.infoq.com/news/2009/08/google-chose-jetty" target="_blank">Google Chose Jetty for App Engine</a></li>
<li><a href="http://www.readwriteweb.com/cloud/2010/02/google-app-engine-is-down---ba.php" target="_blank">Google App Engine is Down - Backup Data Center Having Problems</a></li>
<li><a href="http://www.ibm.com/developerworks/cn/opensource/os-cloud-virtual2/" target="_blank">面向虚拟基础设施的云服务，第 2 部分: Platform as a Service (PaaS) 和 AppScale</a></li>
<li><a href="http://www.google.org.cn/posts/google-working-on-new-filesystem.html" target="_blank">传Google正在开发新的服务器文件系统</a></li>
<li><a href="http://zh.wikipedia.org/zh-cn/Google%E6%AA%94%E6%A1%88%E7%B3%BB%E7%B5%B1" target="_blank">Google File System</a></li>
<li><a href="http://www.cs.cornell.edu/projects/ladis2009/talks/dean-keynote-ladis2009.pdf" target="_blank">Designs, Lessons and Advice from Building Large Distributed Systems</a></li>
<li>The Chubby lock service for loosely-coupled distributed systems</li>
<li><a href="http://perspectives.mvdirona.com/2009/01/01/GooglesWillPowerAndDataCenterEfficiency.aspx" target="_blank">Google's Will Power and Data Center Efficiency</a></li>
<li><a href="http://zh.wikipedia.org/zh-cn/MapReduce" target="_blank">MapReduce</a></li>
<li><a href="http://www.dbanotes.net/arch/labs.google.com/papers/mapreduce-osdi04.pdf%20-" target="_blank">MapReduce的论文</a></li>
<li><a href="http://kimilv.javaeye.com/blog/411092" target="_blank">Protocol Buffer 简介</a></li>
<li><a href="http://www.theregister.co.uk/2008/04/11/google_data_center_map/" target="_blank">Google data centers snub Africa, Oz, and anything near Wyoming Or do they?</a></li>
<li><a href="http://server.ctocio.com.cn/comment/367/8785867.shtml" target="_blank">Google揭秘服务器创新技术 内置电池替换UPS</a></li>
<li><a href="http://www.dbanotes.net/database/database_sharding.html" target="_blank">开源数据库 Sharding 技术（Share Nothing）</a></li>
<li><a href="http://www.theregister.co.uk/2009/08/12/google_file_system_part_deux/" target="_blank">Google File System II: Dawn of the Multiplying Master Nodes</a></li>
<li><a href="http://snarfed.org/space/transactions_across_datacenters_io.html" target="_blank">Transactions Across Datacenters</a></li>
<li><a href="http://server.51cto.com/IDC-81960.htm" target="_blank">探秘Google全球数据中心与中国机房</a></li>
<li><a href="http://server.51cto.com/NGDC-197298.htm" target="_blank">揭开Google数据中心五大神话</a></li>
<li><a href="http://tech.watchstor.com/storage-systems-112892.htm" target="_blank">俄勒冈州的Google数据中心耗电惊人</a></li>
<li><a href="http://tomuse.com/google-app-engine-java-microblog-development-review/#ixzz0nLlxnZQ2">Google App Engine For Java - Microblogging Case Study</a></li>
<li><a href="http://snarfed.org/space/datastore_talk.html">Under the covers of the App Engine Datastore</a></li>
<li><a href="http://perspectives.mvdirona.com/2008/07/10/GoogleMegastore.aspx">Google Megastore</a></li>
<li><a href="http://hi.baidu.com/knuthocean/blog/item/12bb9f3dea0e400abba1673c.html">Megastore/Bigtable Replication的文章</a></li>
<p><a rel="nofollow" href="http://blog.zhourunsheng.com/2011/07/%e6%8e%a2%e7%b4%a2gae%e8%83%8c%e5%90%8e%e7%9a%84%e5%a5%a5%e7%a7%98%e4%b9%8b%e5%8f%82%e8%80%83%e6%96%87%e7%ab%a0%e6%8e%a8%e8%8d%90/">探索GAE背后的奥秘之参考文章推荐</a>，首发于<a rel="nofollow" href="http://blog.zhourunsheng.com">润物无声</a>。</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.zhourunsheng.com/2011/07/%e6%8e%a2%e7%b4%a2gae%e8%83%8c%e5%90%8e%e7%9a%84%e5%a5%a5%e7%a7%98%e4%b9%8b%e5%8f%82%e8%80%83%e6%96%87%e7%ab%a0%e6%8e%a8%e8%8d%90/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>探索GAE背后的奥秘之Datastore设计</title>
		<link>http://blog.zhourunsheng.com/2011/07/%e6%8e%a2%e7%b4%a2gae%e8%83%8c%e5%90%8e%e7%9a%84%e5%a5%a5%e7%a7%98%e4%b9%8bdatastore%e8%ae%be%e8%ae%a1/</link>
		<comments>http://blog.zhourunsheng.com/2011/07/%e6%8e%a2%e7%b4%a2gae%e8%83%8c%e5%90%8e%e7%9a%84%e5%a5%a5%e7%a7%98%e4%b9%8bdatastore%e8%ae%be%e8%ae%a1/#comments</comments>
		<pubDate>Sat, 02 Jul 2011 04:50:42 +0000</pubDate>
		<dc:creator><![CDATA[润物无声]]></dc:creator>
				<category><![CDATA[云计算]]></category>
		<category><![CDATA[GAE]]></category>

		<guid isPermaLink="false">http://blog.zhourunsheng.com/?p=171</guid>
		<description><![CDATA[<p>本篇会首先会从程序员角度来介绍一下Datastore在使用方面的一些信息，之后会接着介绍Datastore是如 [&#8230;]</p>
<p><a rel="nofollow" href="http://blog.zhourunsheng.com/2011/07/%e6%8e%a2%e7%b4%a2gae%e8%83%8c%e5%90%8e%e7%9a%84%e5%a5%a5%e7%a7%98%e4%b9%8bdatastore%e8%ae%be%e8%ae%a1/">探索GAE背后的奥秘之Datastore设计</a>，首发于<a rel="nofollow" href="http://blog.zhourunsheng.com">润物无声</a>。</p>
]]></description>
				<content:encoded><![CDATA[<p>本篇会首先会从程序员角度来介绍一下Datastore在使用方面的一些信息，之后会接着介绍Datastore是如何构建的。</p>
<h1>使用方面</h1>
<p>首先，在编程方面，Datastore是基于"Entity（实体）"这个概念，而且Entity和"对象"这个概念比较类似，同时Entity可以包括多个Property（属性），Property的类别有整数，浮点和字符串等，比如，可以设计一个名为"Person"的Entity，它包含名为"Name"的字符串Property和名为"Age"的整数Property。由于Datastore是"Schema-less"的，所以数据的Schema都由应用维护，而且能非常方便地对一个Entity所包含的属性进行增删和修改。在存储方面，一个Entity的实例可以被认为是一个普通的"Row（行）"，而包含所有这种Entity的实例的Table被称为Kind，比如，所有通过"Person"这个Entity生成实例，比如小吴，小朱和小华等，它们都会存放在同一个名为"Person"的Kind中。在结构方面，虽然也能通过特定的方式在Datastore中实现关系型结构，但是Datastore在设计上是为层次（Hierarchical）性结构"度身定做"的，有Root Entity和Child Entity之分，比如，可以把"Person"作为Root Entity（父实体），"Address"作为"Person"的Child Entity，两者合在一起可以称为一个"Entity Group"。这样做的好处是能将这两个实体集中一个BigTable本地分区中，而且能对这两个实体进行本地事务。</p>
<p>接下来，将谈一下Datastore支持那些高级功能：其一是提供名为GQL（Google Query Language）的查询语言，GQL是SQL的一个非常小的子集，包括对"&gt;"，"&lt;"和"="等操作符。其二是App Engine会根据代码中查询语句来自动生成相应Index，但不支持对Composite Index生成。其三是虽然由于Datastore分布式的设计，所以在速度方面和传统的关系型数据库相比一定的差距，但是Google的架构师保证大部分对Datastore的操作能在200ms之内完成，同时也得益于它的分布式设计，使得它在扩展性方面特别出色。其四是Datastore也支持在实体之间创建关系，比如在Python版App Engine中可以使用ReferenceProperty在实体间构建一对多和多对多的关系。</p>
<p>下表为Datastore和传统的关系型数据库之间的比较：</p>
<div>
<table border="1" cellspacing="0" cellpadding="2" width="568" align="center">
<tbody>
<tr>
<td width="138" valign="top"> </td>
<td width="211" valign="top">Datastore</td>
<td width="217" valign="top">关系型数据库</td>
</tr>
<tr>
<td width="138" valign="top">SQL支持</td>
<td width="211" valign="top">只支持一些基本的查询</td>
<td width="217" valign="top">全部支持</td>
</tr>
<tr>
<td width="138" valign="top">主要结构</td>
<td width="211" valign="top">层次（Hierarchical）</td>
<td width="217" valign="top">关系</td>
</tr>
<tr>
<td width="138" valign="top">Index</td>
<td width="211" valign="top">部分可自动创建</td>
<td width="217" valign="top">手动创建</td>
</tr>
<tr>
<td width="138" valign="top">事务</td>
<td width="211" valign="top">只支持在一个Entity Group内执行</td>
<td width="217" valign="top">支持</td>
</tr>
<tr>
<td width="138" valign="top">平均执行速度（ms）</td>
<td width="211" valign="top">低于200</td>
<td width="217" valign="top">低于100</td>
</tr>
<tr>
<td width="138" valign="top">扩展型</td>
<td width="211" valign="top">非常好</td>
<td width="217" valign="top">很困难，而且需要进行大量的修改</td>
</tr>
</tbody>
</table>
</div>
<p style="text-align: center;">表1. Datastore和关系型数据库之间的比较</p>
<p>最后，在接口方面，Python版提供一套私有的API和框架，在基本功能方面，比较容易学习，但在部分高级功能方面，比如关系和事务等方面，学习难度很高；Java版的API是基于JDO和JPA这两套官方的ORM标准，但是和现在事实的标准Hibernate有一定的差异。</p>
<h1>实现方面</h1>
<p>在实现方面，Datastore是在BigTable的基础上构建的，所以本段会首先重新介绍一下BigTable，之后会介绍Datastore的两个组成部分：Entities Table和Index，最后会讲一下它在事务和备份这两方面所采用的机制。</p>
<p><strong>BigTable</strong></p>
<p>在本系列的第一篇已经按照Google的Paper对BigTable技术做了一定的介绍，但其实BigTable本身其实没有之前介绍的那样复杂，其实就是一个非常巨大的Table，这也是是它之所以名为"BigTable"的原因，而且结构就像图1那样非常简单，就是一个个ROW，每个ROW都有一个Name和一组Cloumn，但是为了支持海量的数据，它将这个大的Table进行分片（Sharding）处理，每台服务器存储一个海量的Table的一小部分，并且为了查询效率，会对这个Table进行排序。就像App Engine的创始人之一Ryan Barrett所说的那样"BigTable is a sharded, sorted array "。</p>
<p> <a href="http://blog.zhourunsheng.com/wp-content/uploads/2011/07/BigTable-Simple.png"><img class="alignnone size-full wp-image-172" title="BigTable-Simple" src="http://blog.zhourunsheng.com/wp-content/uploads/2011/07/BigTable-Simple.png" alt="" width="468" height="222" /></a></p>
<p>图1. BigTable简化版模型</p>
<p>在功能方面，首先，BigTable支持基本的CRUD操作，也就是增加(Create)，查询(Read)，更新(Update)和删除(Delete)。其次支持对Single-Row的事务与基于前缀和范围的扫描。</p>
<p><strong>Entities Table</strong></p>
<p>它是Datastore最核心的Table，是以BigTable的形式存在的，主要用于存储所有的Entity，而且是格式非常简单，每行都会有一个Row Name，也称为Entity Key（可认为它是一个Entity的Primary Key），而且只有唯一一个Column，主要用于存放被序列化的Entity。每个Entity的Key的生成是基于它的父Entity（如果有的话）和其父至上的Entity，直到其Root Entity。以下图为例，timmy的父Entity是jane，jane的父Entity兼Root Entity是Ethel，所以最后timmy的Entity Key是"/Grandparent:Ethel/Parent:Jane/Child:Timmy"。</p>
<p><a href="http://blog.zhourunsheng.com/wp-content/uploads/2011/07/entity-P20keys.png"><img class="alignnone size-full wp-image-173" title="entity-P20keys" src="http://blog.zhourunsheng.com/wp-content/uploads/2011/07/entity-P20keys.png" alt="" width="580" height="272" /></a></p>
<p>图2. Entity Key的例子</p>
<p><strong>Index</strong></p>
<p>Index主要是为方便和加速查询而生的，所以在切入Index之前，先介绍一下Datastore主要支持那些查询，主要有三类：其一是基于Kind的，其二是基于Property值的，其三是基于多个Property值的。</p>
<p>Index表也是以BigTable的形式存在，但是和上面的Entities Table是分离的，主要用来单独存放那些需要被Index的数据，而且由于怕Index表体积太大，所以不会有时将其放置在内存中以提升查询速度。</p>
<p>主要有下面这几种Index表：</p>
<ul>
<li>Kind Index：用于加速那些用于获取所有属于某个Kind的Entity的查询，比如把所有属于Person这个Kind的Entity，包括小吴，小朱和小华等提取出来，Kind Index表每行有Kind和Entity Key这两个列，此Index会有系统自动生成。</li>
<li>Single-property Index：用于加速那些基于单一属性值的查询，比如要找出所有Age在20之下的Person，Age就是所谓的那个单一属性值，Single-property Index表每行除了Kind和Entity Key之外，还有属性名和属性值这两个列，此Index也会有系统自动生成，还会根据升降序的不同，生成两个表。</li>
<li>Composite Index：用于加速那些基于对多个属性值的查询，Composite Index表基本和上面的Single-property Index表非常类似，但是每行包括多个属性名和属性值，而且由于此Index消耗资源非常多，所有由开发人自己确定是不是需要这个Index，系统不自动生成。</li>
</ul>
<p><strong>事务</strong></p>
<p>原则上所有对单一Entity的Write操作都是事务的，并基于上面提到的BigTable的Single-Row事务和Optimistic Concurrency Control这两个技术，下面是流程：首先，系统会读这个Entity的Committed Timestamp（提交时间戳），Write会以串行（Serialized）的形式写入到BigTable的日志中，之后，系统会将日志更新到BigTable的表中，如果成功的话，系统会更新这个Entity的Committed Timestamp，但如果系统发现在更新之前，Committed Timestamp发生了变化，也就是说另一个事务在这个事务执行过程中已经对这个Entity进行了操作，在这个时候，系统会重新执行这个事务。由于在整个事务过程采用Optimistic Concurrency Control，而不是Locking，所以在吞吐量方面表现不错。</p>
<p>如果要对多个Entity执行事务，那就需要将这几个Entity设为一个Entity Group，也就意味着将这几个Entity放在同一台物理机上。在执行的时候，会将以Root Entity的Committed Timestamp为准来对所有参与事务的Entity进行和上面差不多的事务操作。</p>
<p><strong>备份</strong></p>
<p>与BigTable基于Row级别的备份不同的是，Datastore是基于Enity Group级别，而且采用Paxos算法，所以Datastore的备份方法比BigTable的更安全。</p>
<p>总体而言，Datastore在设计理念上和传统的关系型数据库有很大的不同，所以其在反应速度和写数据方面不是最优的，但是现在Web应用以读为主，而且需要能通过简单的扩展就能支持其海量的数据，而这两点却是Datastore所擅长，所以Datastore非常适合支撑Web应用。</p>
<p>文章来源：<a href="http://www.dbanotes.net/arch/google_app_engine-datastore.html">http://www.dbanotes.net/arch/google_app_engine-datastore.html</a></p>
<p><a rel="nofollow" href="http://blog.zhourunsheng.com/2011/07/%e6%8e%a2%e7%b4%a2gae%e8%83%8c%e5%90%8e%e7%9a%84%e5%a5%a5%e7%a7%98%e4%b9%8bdatastore%e8%ae%be%e8%ae%a1/">探索GAE背后的奥秘之Datastore设计</a>，首发于<a rel="nofollow" href="http://blog.zhourunsheng.com">润物无声</a>。</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.zhourunsheng.com/2011/07/%e6%8e%a2%e7%b4%a2gae%e8%83%8c%e5%90%8e%e7%9a%84%e5%a5%a5%e7%a7%98%e4%b9%8bdatastore%e8%ae%be%e8%ae%a1/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>探索GAE背后的奥秘之GAE架构</title>
		<link>http://blog.zhourunsheng.com/2011/07/%e6%8e%a2%e7%b4%a2gae%e8%83%8c%e5%90%8e%e7%9a%84%e5%a5%a5%e7%a7%98%e4%b9%8bgae%e6%9e%b6%e6%9e%84/</link>
		<comments>http://blog.zhourunsheng.com/2011/07/%e6%8e%a2%e7%b4%a2gae%e8%83%8c%e5%90%8e%e7%9a%84%e5%a5%a5%e7%a7%98%e4%b9%8bgae%e6%9e%b6%e6%9e%84/#comments</comments>
		<pubDate>Sat, 02 Jul 2011 04:39:08 +0000</pubDate>
		<dc:creator><![CDATA[润物无声]]></dc:creator>
				<category><![CDATA[云计算]]></category>
		<category><![CDATA[GAE]]></category>

		<guid isPermaLink="false">http://blog.zhourunsheng.com/?p=164</guid>
		<description><![CDATA[<p>本篇将首先介绍App Engine的一些设计理念，接着将对App Engine的组成部分等进行介绍。 设计理念 [&#8230;]</p>
<p><a rel="nofollow" href="http://blog.zhourunsheng.com/2011/07/%e6%8e%a2%e7%b4%a2gae%e8%83%8c%e5%90%8e%e7%9a%84%e5%a5%a5%e7%a7%98%e4%b9%8bgae%e6%9e%b6%e6%9e%84/">探索GAE背后的奥秘之GAE架构</a>，首发于<a rel="nofollow" href="http://blog.zhourunsheng.com">润物无声</a>。</p>
]]></description>
				<content:encoded><![CDATA[<p>本篇将首先介绍App Engine的一些设计理念，接着将对App Engine的组成部分等进行介绍。</p>
<h1><strong>设计理念</strong></h1>
<p>App Engine在设计理念方面，主要可以总结为下面这五条：</p>
<ul>
<li>重用现有的Google技术：大家都知道，重用是软件工程的核心理念之一，因为通过重用不仅能减低开发成本，而且能简化架构。在App Engine开发的过程中，重用的思想也得到了非常好的体现，比如Datastore是基于Google的bigtable技术，Images服务是基于Picasa的，用户认证服务是利用Google Account的，Email服务是基于Gmail的等。</li>
<li>无状态：为了让更好地支持扩展，Google没有在应用服务器层存储任何重要的状态，而主要在datastore这层对数据进行持久化，这样当应用流量突然爆发时，可以通过为应用添加新的服务器来实现扩展。</li>
<li>硬限制：App Engine对运行在其之上的应用代码设置了很多硬性限制，比如无法创建Socket和Thread等有限的系统资源，这样能保证不让一些恶性的应用影响到与其临近应用的正常运行，同时也能保证在应用之间能做到一定的隔离。</li>
<li>利用Protocol Buffers技术来解决服务方面的异构性：应用服务器和很多服务相连，有可能会出现异构性的问题，比如应用服务器是用Java写的，而部分服务是用C++写的等。Google在这方面的解决方法是基于语言中立，平台中立和可扩展的Protocol Buffer，并且在App Engine平台上所有API的调用都需要在进行RPC（Remote Procedure Call，远程方面调用）之前被编译成Protocol Buffer的二进制格式。</li>
<li>分布式数据库：因为App Engine将支撑海量的网络应用，所以独立数据库的设计肯定是不可取的，而且很有可能将面对起伏不定的流量，所以需要一个分布式的数据库来支撑海量的数据和海量的查询。</li>
</ul>
<h1><strong>组成部分</strong></h1>
<p><a href="http://blog.zhourunsheng.com/wp-content/uploads/2011/07/GAE-P20ARCH.jpg"><img class="alignnone size-full wp-image-165" title="GAE-P20ARCH" src="http://blog.zhourunsheng.com/wp-content/uploads/2011/07/GAE-P20ARCH.jpg" alt="" width="580" height="434" /></a></p>
<p style="text-align: center;">图1. GAE的架构图</p>
<p>简单而言，其架构可以分为三个部分：前端，Datastore和服务群：</p>
<p><strong>前端</strong></p>
<p>共包括四个模块：</p>
<ul>
<li>Front End：既可以认为它是Load Balancer，也可以认为它是Proxy，它主要负责负载均衡和将请求转发给App Server（应用服务器）或者Static Files等工作。</li>
<li>Static Files：在概念上，比较类似于CDN（Content Delivery Network，内容分发网络），用于存储和传送那些应用附带的静态文件，比如图片，CSS和JS脚本等。</li>
<li>App Server：用于处理用户发来的请求，并根据请求的内容来调用后面的Datastore和服务群。</li>
<li>App Master：是在应用服务器间调度应用，并将调度之后的情况通知Front End。</li>
</ul>
<p><strong>Datastore</strong></p>
<p>它是基于BigTable技术的分布式数据库，虽然其也可以被理解成为一个服务，但是由于其是整个App Engine唯一存储持久化数据的地方，所以其是App Engine中一个非常核心的模块。其具体细节将在下篇和大家讨论。</p>
<p><strong>服务群</strong></p>
<p>整个服务群包括很多服务供App Server调用，比如Memcache，图形，用户，URL抓取和任务队列等。</p>
<h1>Python版和Java版App Engine在实现方面的区别</h1>
<p>因为大多数服务都可以被这两个版本共享，所以两者之间的区别主要集中在App Server端，Python版App Server应该是经过Google修改的Python Runtime，版本号应该是2.5.2，而Java版App Server是基于Jetty 6的，因为它的体积和最常用的Tomcat相比更娇小，这样能使得一台服务器支持更多的应用，而且其应该经过Google的一定的修改。</p>
<h1>流程</h1>
<p>在这里举一个普通的HTTP请求的处理流程为例：</p>
<ul>
<li>用户发送一个HTTP请求。</li>
<li>Front End接受这个请求，并将这个请求转发给一个空闲的App Server。</li>
<li>App Server会处理这个请求。</li>
<li>检查用于处理这个请求的Handler是不是已经被初始化了，如果没有的话，需要对这个Handler进行初始化。</li>
<li>调用服务群的用户认证服务来对用户进行认证，如果失败的话，需要终止整个请求的处理工作，并返回用户无法被认证的信息。</li>
<li>查看这个请求所需的数据是否已经缓存在Memcahe中，如果没有的话，将对Datastore发出查询请求来得到数据。</li>
<li>通过整合上步得到数据来生成相关的HTML，并返回给用户。</li>
<li>由于HTML里面会包含对一些静态文件的引用，比如图片和CSS等，所以当用户收到HTML之后，还会通过Front End对Static Files里面存储的静态文件进行读取。</li>
</ul>
<p>文章来源：<a href="http://www.dbanotes.net/arch/google_app_engine-arch_intro.html">http://www.dbanotes.net/arch/google_app_engine-arch_intro.html</a></p>
<p><a rel="nofollow" href="http://blog.zhourunsheng.com/2011/07/%e6%8e%a2%e7%b4%a2gae%e8%83%8c%e5%90%8e%e7%9a%84%e5%a5%a5%e7%a7%98%e4%b9%8bgae%e6%9e%b6%e6%9e%84/">探索GAE背后的奥秘之GAE架构</a>，首发于<a rel="nofollow" href="http://blog.zhourunsheng.com">润物无声</a>。</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.zhourunsheng.com/2011/07/%e6%8e%a2%e7%b4%a2gae%e8%83%8c%e5%90%8e%e7%9a%84%e5%a5%a5%e7%a7%98%e4%b9%8bgae%e6%9e%b6%e6%9e%84/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>探索GAE背后的奥秘之GAE简介</title>
		<link>http://blog.zhourunsheng.com/2011/07/%e6%8e%a2%e7%b4%a2gae%e8%83%8c%e5%90%8e%e7%9a%84%e5%a5%a5%e7%a7%98%e4%b9%8bgae%e7%ae%80%e4%bb%8b/</link>
		<comments>http://blog.zhourunsheng.com/2011/07/%e6%8e%a2%e7%b4%a2gae%e8%83%8c%e5%90%8e%e7%9a%84%e5%a5%a5%e7%a7%98%e4%b9%8bgae%e7%ae%80%e4%bb%8b/#comments</comments>
		<pubDate>Sat, 02 Jul 2011 04:30:44 +0000</pubDate>
		<dc:creator><![CDATA[润物无声]]></dc:creator>
				<category><![CDATA[云计算]]></category>
		<category><![CDATA[GAE]]></category>

		<guid isPermaLink="false">http://blog.zhourunsheng.com/?p=157</guid>
		<description><![CDATA[<p>通过前面两篇介绍，大家应该对Google强大的基础设施有一定的了解。本篇开始介绍构筑在这强大基础设施之上的Go [&#8230;]</p>
<p><a rel="nofollow" href="http://blog.zhourunsheng.com/2011/07/%e6%8e%a2%e7%b4%a2gae%e8%83%8c%e5%90%8e%e7%9a%84%e5%a5%a5%e7%a7%98%e4%b9%8bgae%e7%ae%80%e4%bb%8b/">探索GAE背后的奥秘之GAE简介</a>，首发于<a rel="nofollow" href="http://blog.zhourunsheng.com">润物无声</a>。</p>
]]></description>
				<content:encoded><![CDATA[<p>通过前面两篇介绍，大家应该对Google强大的基础设施有一定的了解。本篇开始介绍构筑在这强大基础设施之上的Google App Engine。</p>
<h1>Google App Engine的介绍</h1>
<p>由于发布S3和EC2这两个优秀的云服务，使得Amazon已经率先在云计算市场站稳了脚跟，而身为云计算这个浪潮的发起者之一的Google肯定不甘示弱，并在2008年四月份推出了Google App Engine这项PaaS服务，虽然现在无法称其为一个革命性的产品，但肯定是现在市面上最成熟，并且功能最全面的PaaS平台。</p>
<p>Google App Engine 提供一整套开发组件来让用户轻松地在本地构建和调试网络应用，之后能让用户在Google强大的基础设施上部署和运行网络应用程序，并自动根据应用所承受的负载来对应用进行扩展，并免去用户对应用和服务器等的维护工作。同时提供大量的免费额度和灵活的资费标准。在开发语言方面，现支持Java和Python这两种语言，并为这两种语言提供基本相同的功能和API。</p>
<p>目前已经支持google开发的第三种语言Go语言平台的开发了。</p>
<p><strong>功能</strong></p>
<p>在功能上，主要有六个方面：</p>
<ul>
<li>动态网络服务，并提供对常用网络技术的支持，比如SSL等 。</li>
<li>持久存储空间，并支持简单的查询和本地事务。</li>
<li>能对应用进行自动扩展和负载平衡。</li>
<li>一套功能完整的本地开发环境，可以让用户在本机上对App Engine进行开发和调试。</li>
<li>支持包括Email和用户认证等多种服务。</li>
<li>提供能在指定时间和定期触发事件的计划任务和能实现后台处理的任务队列。</li>
</ul>
<p><strong>使用流程</strong></p>
<p>整个使用流程主要包括五个步骤：</p>
<ul>
<li>下载SDK和IDE，并在本地搭建开发环境。</li>
<li>在本地对应用进行开发和调试。</li>
<li>使用GAE自带上传工具来将应用部署到平台上。</li>
<li>在管理界面中启动这个应用。</li>
<li>利用管理界面来监控整个应用的运行状态和资费。</li>
</ul>
<p>由于本系列是专注于GAE的实现和设计两方面，所以不会对GAE的使用有非常深入地介绍，如果希望大家对GAE的使用方面有更深的理解，具体可以参看一下GAE的<a href="http://code.google.com/intl/zh-CN/appengine/docs/whatisgoogleappengine.html">官方文档</a>。</p>
<h1>Google App Engine的主要组成部分</h1>
<p>主要可分为五部分：</p>
<ul>
<li>应用服务器：主要是用于接收来自于外部的Web请求。</li>
<li>Datastore：主要用于对信息进行持久化，并基于Google著名的BigTable技术。</li>
<li>服务：除了必备的应用服务器和Datastore之外，GAE还自带很多服务来帮助开发者，比如：Memcache，邮件，网页抓取，任务队列，XMPP等。</li>
<li>管理界面：主要用于管理应用并监控应用的运行状态，比如，消耗了多少资源，发送了多少邮件和应用运行的日志等。</li>
<li>本地开发环境：主要是帮助用户在本地开发和调试基于GAE的应用，包括用于安全调试的沙盒，SDK和IDE插件等工具。</li>
</ul>
<h1><strong>应用服务器</strong></h1>
<p>应用服务器依据其支持语言的不同而有不同的实现。</p>
<p><strong>Python的实现</strong></p>
<p>Python版应用服务器的基础就是普通的Python 2.5.2版的Runtime，并考虑在在未来版本中添加对Python 3的支持，但是因为Python 3对Python而言，就好比Java2之于Java1，跨度非常大，所以引入Python3的难度很大。在Web技术方面，支持诸如Django，CherryPy，Pylons和Web2py等Python Web框架，并自带名为"WSGI"的CGI框架。虽然Python版应用服务器是基于标准的Python Runtime，但是为了安全并更好地适应App Engine的整体架构，对运行在应用服务器内的代码设置了很多方面的限制，比如不能加载用C编写Python模块和无法创建Socket等。</p>
<p><strong>Java的实现</strong></p>
<p>在实现方面，Java版应用服务器和Python版基本一致，也是基于标准的Java Web容器，而且选用了轻量级的Jetty技术，并跑在Java 6上。通过这个Web容器不仅能运行常见的Java Web 技术，包括Servlet，JSP，JSTL和GWT等，而且还能跑大多数常用的Java API（App Engine有一个<a href="http://code.google.com/appengine/docs/java/jrewhitelist.html">The JRE Class White List</a>来定义那些Java API能在App Engine的环境中被使用）和一些基于JVM的脚本语言，例如JavaScript，Ruby或Scala等，但同样无法创建Socket和Thread，或者对文件进行读写，也不支持一些比较高阶的API和框架，包括JDBC，JSF，Struts 2，RMI，JAX-RPC和Hibernate等。</p>
<h1><strong>Datastore</strong></h1>
<p>Datastore提供了一整套强大的分布式数据存储和查询服务，并能通过水平扩展来支撑海量的数据。但Datastore并不是传统的关系型数据库，它主要以"Entity"的形式存储数据，一个Entity包括一个Kind（在概念上和数据库的Table比较类似）和一系列属性。</p>
<p>Datastore提供强一致性和乐观（optimistic）同步控制，而在事务方面，则支持本地事务，也就是在只能同一个Entity Group内执行事务。</p>
<p>在接口方面，Python版提供了非常丰富的接口，而且还包括名为GQL的查询语言，而Java版则提供了标准的JDO和JPA这两套API。</p>
<p>而且Google已经在今年的Google I/O大会上宣布将在未来的App Engine for Business套件中包含标准的SQL数据库服务，但现在还不确定这个SQL数据库的实现方式，是基于开源的MySQL技术，还是基于其私有的实现，这是一个问题。</p>
<h1><strong>服务</strong></h1>
<p><strong>Memcache</strong></p>
<p>Memcache是大中型网站所备的服务，主要用来在内存中存储常用的数据，而App Engine也包含了这个服务。有趣的是App Engine的Memcache也是由Brad Fitzpatrick开发。</p>
<p><strong>URL抓取（Fetch）</strong></p>
<p>App Engine的应用可以通过URL抓取这个服务抓取网上的资源，并可以这个服务来与其他主机进行通信。这样避免了应用在Python和Java环境中无法使用Socket的尴尬。</p>
<p><strong>Email</strong></p>
<p>App Engine应用使用这个服务来利用Gmail的基础设施来发送电子邮件。</p>
<p><strong>计划任务（Cron）</strong></p>
<p>计划服务允许应用在指定时间或按指定间隔执行其设定的任务。这些任务通常称为Cron job。</p>
<p><strong>图形</strong></p>
<p>App Engine 提供了使用专用图像服务来操作图像数据的功能。图像服务可以调整图像大小，旋转、翻转和裁剪图像。它还能够使用预先定义的算法提升图片的质量。</p>
<p><strong>用户认证</strong></p>
<p>App Engine的应用可以依赖Google帐户系统来验证用户。App Engine还将支持OAuth。</p>
<p><strong>XMPP</strong></p>
<p>在App Engine上运行的程序能利用XMPP服务和其他兼容XMPP的IM服务（比如Google Talk）进行通信。</p>
<p><strong>任务队列（Task Queue）</strong></p>
<p>App Engine应用能通过在一个队列插入任务（以Web Hook的形式）来实现后台处理，而且App Engine会根据调度方面的设置来安排这个队列里面的任务执行。</p>
<p><strong>Blobstore</strong></p>
<p>因为Datastore最多支持存储1MB大小的数据对象，所以App Engine推出了Blobstore服务来存储和调用那些大于1MB但小于2G的二进制数据对象。</p>
<p><strong>Mapper</strong></p>
<p>Mapper可以认为就是"Map Reduce"中的Map，也就是能通过Mapper API对大规模的数据进行平行的处理，这些数据可以存储在Datastore或者Blobstore，但这个功能还处于内部开发阶段。</p>
<p><strong>Channel</strong></p>
<p>其实Channel就是我们常说的"Comet"，通过Channel API能让应用将内容直接推至用户的浏览器，而不需常见的轮询。</p>
<p>除了Java版的Memcache，Email和URL抓取都是采用标准的API之外，其他服务无论是Java版还是Python版，其API都是私有的，但是提供了丰富和细致的文档来帮助用户使用。</p>
<h1>管理界面</h1>
<p>用了让用户更好地管理应用，Google提供了一整套完善的管理界面，地址是http://appengine.google.com/ ，而且只需用户的Google帐户就能登录和使用。下图为其截屏：</p>
<p><a href="http://blog.zhourunsheng.com/wp-content/uploads/2011/07/gae-Dashboard.png"><img class="alignnone size-full wp-image-158" title="gae-Dashboard" src="http://blog.zhourunsheng.com/wp-content/uploads/2011/07/gae-Dashboard.png" alt="" width="580" height="261" /></a></p>
<p>图1. 管理界面</p>
<p>使用这个管理界面可执行许多操作，包括创建新的应用程序，为这个应用设置域名，查看与访问数据和错误相关的日志，观察主要资源的使用状况。</p>
<h1><strong>本地开发环境</strong></h1>
<p>为了安全起见，本地开发环境采用了沙箱（Sandbox）模式，基本上和上面提到的应用服务器的限制差不多，比如无法创建Socket和Thread，也无法对文件进行读写。Python版App Engine SDK是以普通的应用程序的形式发布，本地需要安装相应的Python Runtime，通过命令行方式启动Python版的Sandbox，同时也可以在安装有PyDev插件的Eclipse上启动。Java版App Engine SDK是以Eclispe Plugin形式发布，只要用户在他的Eclipse上安装这个Plugin，用户就能启动本地Java沙箱来开发和调试应用。</p>
<h1><strong>编程模型</strong></h1>
<p>因为App Engine主要为了支撑Web应用而存在，所以Web层编程模型对于App Engine也是最关键的。App Engine主要使用的Web模型是CGI，CGI全称为"Common Gateway Interface"，它的意思非常简单，就是收到一个请求，起一个进程或者线程来处理这个请求，当处理结束后这个进程或者线程自动关闭，之后是不断地重复这个流程。由于CGI这种方式每次处理的时候，都要重新起一个新的进程或者线程，可以说在资源消耗方面还是很厉害的，虽然有线程池（Thread Pool）这样的优化技术。但是由于CGI在架构上的简单性使其成为GAE首选的编程模型，同时由于CGI支持无状态模式，所以也在伸缩性方面非常有优势。而且App Engine的两个语言版本都自带一个CGI框架：在Python平台为WSGI。在Java平台则为经典的Servlet。最近，由于App Engine引入了计划任务和任务队列这两个特性，所以App Engine已经支持计划任务和后台进程这两种编程模型。</p>
<h1><strong>限制和资费</strong></h1>
<p>首先，谈一下App Engine的使用限制，具体请看下表：</p>
<div>
<table border="1" cellspacing="0" cellpadding="2" width="276" align="center">
<tbody>
<tr>
<td width="215" valign="top">类别</td>
<td width="59" valign="top">限制</td>
</tr>
<tr>
<td width="215" valign="top">每个开发者所拥有的项目</td>
<td width="59" valign="top">10个</td>
</tr>
<tr>
<td width="215" valign="top">每个项目的文件数</td>
<td width="59" valign="top">1000个</td>
</tr>
<tr>
<td width="215" valign="top">每个项目代码的大小</td>
<td width="59" valign="top">150MB</td>
</tr>
<tr>
<td width="215" valign="top">每个请求最多执行时间</td>
<td width="59" valign="top">30秒</td>
</tr>
<tr>
<td width="215" valign="top">Blobstore（二进制存储）的大小</td>
<td width="59" valign="top">1GB</td>
</tr>
<tr>
<td width="215" valign="top"><acronym title="HyperText Transfer Protocol ">HTTP</acronym> Response的大小</td>
<td width="59" valign="top">10MB</td>
</tr>
<tr>
<td width="215" valign="top">Datastore中每个对象的大小</td>
<td width="59" valign="top">1MB</td>
</tr>
</tbody>
</table>
</div>
<p style="text-align: center;">表1. App Engine的使用限制</p>
<p>虽然这些限制对开发者是一种障碍，但对App Engine这样的多租户环境而且却是非常重要的，因为如果一个租户的应用消耗过多的资源的话，将会影响到在临近应用的正常使用，而App Engine上面这些限制就是为了是运行在其平台上面应用能安全地运行着想，避免了一个吞噬资源或恶性的应用影响到临近应用的情况。除了安全的方面考虑之后，还有伸缩的原因，也就是说，当一个应用的所占空间（footprint）处于比较低的状态，比如少于1000个文件和大小低于150MB等，那么能够非常方便地通过复制应用来实现伸缩。</p>
<p>接着，谈一下资费情况，App Engine的资费情况主要有两个特点：其一是免费额度高，现有免费的额度能支撑一个中型网站的运行，且不需付任何费用。其二是资费项目非常细粒度，普通IaaS服务资费，主要就是CPU，内存，硬盘和网络带宽这四项，而App Engine则除了常见的CPU和网络带宽这两项之外，还包括很多应用级别的项目，比如：Datastore API和邮件API的调用次数等。具体资费的机制是这样的：如果用户的应用每天消费的各种资源都低于这个额度，那们用户无需支付任何费用，但是当免费额度被超过的时候，用户就需要为超过的部分付费。因为App Engine整套资费标准比较复杂，所以在这里就主要介绍一下它的免费额度，具体请看下表：</p>
<div>
<table border="1" cellspacing="0" cellpadding="2" width="355" align="center">
<tbody>
<tr>
<td width="230" valign="top">类型</td>
<td width="123" valign="top">数量（每天）</td>
</tr>
<tr>
<td width="230" valign="top">邮件API调用</td>
<td width="123" valign="top">7000次</td>
</tr>
<tr>
<td width="230" valign="top">传出（outbound）带宽</td>
<td width="123" valign="top">10G</td>
</tr>
<tr>
<td width="230" valign="top">传入（inbound）带宽</td>
<td width="123" valign="top">10G</td>
</tr>
<tr>
<td width="230" valign="top">CPU时间</td>
<td width="123" valign="top">46个小时</td>
</tr>
<tr>
<td width="230" valign="top">HTTP请求</td>
<td width="123" valign="top">130万次</td>
</tr>
<tr>
<td width="230" valign="top">Datastore <acronym title="Application Programming Interface ">API</acronym></td>
<td width="123" valign="top">1000万次</td>
</tr>
<tr>
<td width="230" valign="top">存储的数据</td>
<td width="123" valign="top">1G</td>
</tr>
<tr>
<td width="230" valign="top">URL抓取的API</td>
<td width="123" valign="top">657千次</td>
</tr>
</tbody>
</table>
</div>
<p style="text-align: center;">表2. App Engine的免费额度表</p>
<p>从上面免费额度来看，除了存储数据的容量外，其它都是非常强大的。</p>
<p>文章来源：<a href="http://www.dbanotes.net/arch/google_app_engine-intro.html">http://www.dbanotes.net/arch/google_app_engine-intro.html</a></p>
<p><a rel="nofollow" href="http://blog.zhourunsheng.com/2011/07/%e6%8e%a2%e7%b4%a2gae%e8%83%8c%e5%90%8e%e7%9a%84%e5%a5%a5%e7%a7%98%e4%b9%8bgae%e7%ae%80%e4%bb%8b/">探索GAE背后的奥秘之GAE简介</a>，首发于<a rel="nofollow" href="http://blog.zhourunsheng.com">润物无声</a>。</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.zhourunsheng.com/2011/07/%e6%8e%a2%e7%b4%a2gae%e8%83%8c%e5%90%8e%e7%9a%84%e5%a5%a5%e7%a7%98%e4%b9%8bgae%e7%ae%80%e4%bb%8b/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>
