进军社会软件是为应用程序增值的好方法。使用社会网络可以更轻松地获得并聚合数据,从而创建富有革新精神的新 Web 应用程序。但是,仍然必须处理创建可伸缩 Web 应用程序的所有常见问题。现在,使用 Google App Engine (GAE) 也可以简化工作。使用 GAE,可以不必考虑管理应用服务器池的所有事务。您不必担心存储大量静态内容和动态数据。相反,您可以将精力集中在创建优秀的 mashup 上。本文是共分三部分的系列文章 “使用 Eclipse 在 Google App Engine 上创建 mashup” 的第一部分,将了解如何开始开发 GAE 应用程序,以及如何使用 Eclipse 简化 GAE 开发。
关于本系列
在本系列中,将了解如何开始使用 Google App Engine (GAE)。在第 1 部分中,将了解如何设置开发环境,以便可以开始创建运行在 GAE 上的应用程序。了解如何使用 Eclipse 简化应用程序的开发和调试。在第 2 部分中,将使用 Eclipse 构建一个 Ajax mashup 并将其部署到 GAE 中。最后,在第 3 部分中,通过为应用程序创建 RESTful Web 服务返回到生态系统,这样其他人就可以使用它创建自己的 mashup。
GAE 是创建 Web 应用程序的平台。使用它的最重要的先决条件是具备 Python 知识,因为要在 GAE 中使用 Python 作为编程语言(目前为 Python V2.5.2)。对于本系列,具备一些典型的 Web 开发技能将会有帮助(例如,HTML、JavaScript 和 CSS 知识)。要针对 GAE 进行开发,需要下载三个软件包。
- Eclipse Classic
- 我使用的是 Eclipse Classic V3.3.2。更新的版本也可以使用。
- Google App Engine SDK
- 阅读 GAE 站点中的官方文档并查找下载 SDK 的链接。
- PyDev
- 使用更新站点 http://pydev.sourceforge.net/updates/ 从 Eclipse 内下载 PyDev,它可以将 Eclipse 转换为一个 Python IDE。
下面详细讨论了后两个软件包的安装。如果您刚开始使用 Eclipse,请参阅 参考资料 获得入门。
设置 GAE
如果曾经花大量时间开发过 Web 应用程序,则很可能在想要开始使用新应用程序堆栈时习惯下载库、Web 服务器和数据库。有时,可以将所有这些包绑定到优秀的安装程序中,从而可以更轻松地使用所有活动部分。一切就绪后,通常需要进行更多的设置,才能正常使用所喜爱的开发环境。幸运的是,使用 GAE 时不会遇到这种情况。让我们看看如何设置它以及如何将它和 Eclipse 结合起来。
设置 GAE 首先需要做的是下载 SDK,可用于 Microsoft® Windows®、Mac OS X 和 Linux®。对于 Windows 和 Mac OS X,SDK 是以安装程序出现的,不但要在系统中安装 SDK,而且要在路径中放置几个关键的可执行脚本以便使用。图 1 显示了 SDK 的目录结构图。
图 1. GAE SDK 目录结构
在根目录中,您应当会看到两个 Python 脚本:appcfg.py 和 dev_appserver.py。dev_appserver.py 脚本用于启动开发应用服务器。没有独立安装,也不需要为开发和测试应用程序进行部署。当您准备好将应用程序部署到 GAE 中时,将使用 appcfg.py 脚本。
您可以在 Google 目录中查找构成 GAE 平台基础的所有 API。您将不可避免地使用和扩展该目录中的类。因此,如果需要运行 GAE 应用程序代码,则需要知道该目录,因为需要了解应用程序代码所使用的 API。作为示例,让我们看一看如何设置用于开发 GAE 代码的 Eclipse。
设置 Eclipse
Eclipse 被称为开发 Java™ 编程语言应用程序的首要 IDE。但是,它不但适用于 Java 开发人员,而且还可以用于许多其他语言,包括 C++、PHP、Ruby 和 Python。实际上,有多个 Eclipse 插件可用于将 Eclipse 转换为 Python IDE。这些插件中最常见的是 PyDev。可以使用更新站点 http://pydev.sourceforge.net/updates/ 从 Eclipse 中安装该插件。
安装 PyDev 之后,需要配置它。打开 Eclipse 并转到 Preferences > PyDev。
图 2. 配置 PyDev
您需要把 Python 安装位置告诉 PyDev。转到 Interpreter > Python 并单击 New,如上所示。只需浏览到 Python V2.5+ 安装,然后 Eclipse 应当会执行剩余操作。单击 OK,然后您将准备好从 Eclipse 中开发 Python。
创建应用程序
首先在 Eclipse 中创建一个新 PyDev。通过选择 Windows > Open Perspective > Other 并从可用透视图列表中选择 PyDev 切换到 PyDev 透视图。
图 3. 打开 PyDev 透视图
现在应当能够通过选择 File > New > PyDev Project 创建新应用程序。在本例中,我们将为源代码创建一个单独的 src 文件夹,但这是可选的并且主要取决于个人习惯。到目前为止,所做的一切都是在 Eclipse 上进行的一般的 Python 开发。现在将开始执行一些特定于 GAE 的操作。
GAE 模板项目
GAE 项目的布局十分简单;只有一个没有子目录的目录。一定有三个文件:app.yaml、index.yaml 和 main.py。下面显示了第一个文件 app.yaml。
清单 1. app.yaml
application: aggrogator version: 1 runtime: python api_version: 1 handlers: - url: .* script: main.py |
这是 GAE 应用程序的主要配置文件。该文件中的大部分内容只需要设置一次,例如应用程序名称、版本和构建该应用程序所使用的 GAE API 的版本。需要更多处理的部分是 handlers 部分。这是 HTTP 请求 URL 和项目中的 Python 脚本之间的映射。在以上情况下,我们将把所有内容都映射到同一个 main.py 脚本中。该文件随后可以执行额外的路径以将 URL 与方法匹配在一起。
另一个 YAML 文件 index.yaml 用于帮助 GAE 高效地获取数据。实际上,即使没有这样一个文件也没有关系,因为如果遗漏了该文件,GAE 也会为您生成一个。现在无需对该文件进行任何操作,清单 2 显示了一个空的 index.yaml。
清单 2. index.yaml
indexes: # AUTOGENERATED # This index.yaml is automatically updated whenever the dev_appserver # detects that a new type of query is run. if you want to manage the # index.yaml file manually, remove the above marker line (the line # saying "# AUTOGENERATED"). If you want to manage some indexes # manually, move them above the marker line. The index.yaml file is # automatically uploaded to the admin console when you next deploy # your application using appcfg.py. |
最后,我们将接触到应用程序的核心:main.py 脚本。实际上您可以给该脚本任意命名,只要它是与 app.yaml 同步的。通常都使用 main.py 名称,因此我们在这里也使用这个名称。在查看该文件之前,让我们先描述一下编写的应用程序的类型。
aggroGator
我们的应用程序名为 aggroGator。它将允许用户关联已经使用的各项 Web 服务。然后将从这些服务中获取数据提要并按时间顺序聚集这些数据提要。对于我们的应用程序,将使用常见的 FeedParser 库来解析提要。还将通过 Google 使用 GAE 的内置身份管理,因此无需编写自己的注册/登录/退出特性。用户将只使用 Google 身份进行登录。记住这一切之后,让我们看一看 main.py。
清单 3. main.py
def main(): application = webapp.WSGIApplication( [('/', MainPage), ('/add', AddService)], debug=True) wsgiref.handlers.CGIHandler().run(application) if __name__ == "__main__": main() |
因此这实际上只是脚本的主要方法。该方法所做的是设置更多路径。在这种情况下,它将把 “/” 的请求发送到名为 MainPage 的类中,并把 “/add” 的请求发送到名为 AddService 的类中。现在让我们看一看 MainPage 类。
清单 4. MainPage
class MainPage(webapp.RequestHandler): def get(self): user = users.get_current_user() if users.get_current_user(): url = users.create_logout_url(self.request.uri) url_linktext = 'Logout' else: url = users.create_login_url(self.request.uri) url_linktext = 'Login' updates = [] account = None if user: account_query = Account.all() account_query.filter('user = ', users.get_current_user()) result_set = account_query.fetch(1) if len(result_set) > 0: account = account_query.fetch(1)[0] if account: updates = [] for service in account.dynamic_properties(): url = getattr(account, service) feed = GenericFeed(url, service) updates.extend(feed.entries()) else: account = Account() account.user = user account.put() updates.sort(key=attrgetter('timestamp'), reverse=True) template_values = { 'account': account, 'updates': updates, 'url': url, 'url_linktext': url_linktext, } path = os.path.join(os.path.dirname(__file__), 'index.html') self.response.out.write(template.render(path, template_values)) |
该类执行了很多操作,因为它是应用程序的主要控制器。首先需要注意的是它只有 get 方法。那意味着它只支持 HTTP GET 请求。对 “/” 执行 POST 将导致错误。接下来,它将检查身份。users 类是来自 GAE SDK 的 API。它将利用 Google 的身份管理。我们将使用它检查用户是否已登录。如果用户已登录,就会知道用户的身份(其 Google ID)。然后创建登录链接或者退出链接,这取决于他是否已登录。如果没有登录,则只显示登录链接;否则,继续查找帐户。下面是已经编写的另一个类:
清单 5. Account 类
class Account(db.Expando): user = db.UserProperty() |
该类将利用 Google 的数据存储 API(Google 的著名 Bigtable 数据库)。Account 实体拥有 User 属性并且是 Expando Model。这将允许我们创建动态属性 — 用于每项服务的 URL。对于每项服务,将使用 GenericFeed 类检索条目列表,如下所示:
清单 6. GenericFeed 类
class GenericFeed: def __init__(self, url, name): self.url = url self.name = name def entries(self): result = urlfetch.fetch(self.url) updates = [] if result.status_code == 200: feed = feedparser.parse(result.content) for entry in feed['entries']: x = Entry() x.service = self.name x.title = entry['title'] x.link = entry['link'] if entry.summary: x.content = entry.summary else: x.content = entry['title'] x.timestamp = entry.updated_parsed updates.append(x) return updates |
这是使用 FeedParser 库的类。在使用该库之前,我们将使用另外一个 GAE API:urlfetch 类。该类将允许 HTTP 请求,但只是 80 端口和 443 端口(用于安全请求)。只使用它对存储的 URL 执行 HTTP GET,然后将结果传递给 FeedParser 库。接着创建 Entry 类的实例,如下所示:
清单 7. Entry 类
class Entry: def __init__(self=None, title=None, link=None, timestamp=None, content=None, service=None): self.title = title self.link = link self.content = content self.service = service self.timestamp = timestamp def printTime(self): return strftime('%B %d,%Y at %I:%M:%S %p',self.timestamp) |
该类几乎只是一个简单的数据结构。它惟一的逻辑是拥有一个输出时间戳的方法。GenericFeed 类将返回与该用户相关的每项服务的 Entry 实例列表。然后按降序根据时间戳排序 Entries(第一个是最新的 Entry)。返回到 MainPage,然后将若干个对象、用户的 Account、排序后的 Entries 列表及登录/退出链接传递给模板。GAE 使用的模板系统类似于常见的 Python 框架 Django 所使用的模板系统。在本例中,将把数据传递给名为 index.html 的模板。
清单 8. index.html 模板
<html> <body> <a href="{{ url }}">{{ url_linktext }}</a> <ol> {% for update in updates %} <li> From {{update.service}}: <a href="{{update.link}}">{{update.content}}</a> posted at: {{update.printTime}} </li> {% endfor %} </ol> {% if account %} <form action="/add" method="post"> <label for="service">Service: </label> <select name="service"> <option>twitter</option> <option>del.icio.us</option> <option>last.fm</option> <option>YouTube</option> </select><br/> <label for="username">Username: </label> <input type="text" name="username"/> <input type="submit" value="Add"/> </form> {% endif %} </body> </html> |
这是一个简单的模板。它大部分只是 HTML,其中带有几个动态部分。首先,它将创建相应的登录/退出链接。接下来,它将遍历条目列表以将其显示给用户。最后,如果用户已登录,则创建允许用户添加服务的表单。该表单将对 /add URL 执行 HTTP POST。如清单 3 所示,该请求将被发送到 AddService 控制器类中。
清单 9. AddService 控制器
class AddService(webapp.RequestHandler): def post(self): # check if user already exists account_query = Account.all() account_query.filter('user = ', users.get_current_user()) result_set = account_query.fetch(1) if len(result_set) > 0: account = account_query.fetch(1)[0] else : account = Account() account.user = users.get_current_user() service = self.request.get('service') username = self.request.get('username') if service == 'twitter': service = 'http://twitter.com/statuses/user_timeline/'+username+'.rss' account.twitter = service if service =='del.icio.us': service = 'http://del.icio.us/rss/' + username account.del_icio_us = service if service == 'last.fm': service = 'http://ws.audioscrobbler.com/1.0/user/'+username+ '/recenttracks.rss' account.last_fm = service if service == 'YouTube': service = 'http://www.youtube.com/rss/user/'+username+'/videos.rss' account.you_tube = service account.put() self.redirect('/') |
该类将查找用户的帐户。然后根据使用的服务创建相应的 URL。使用 Expando 属性把该 URL 添加到帐户的服务中,并且把所有内容保存回 Bigtable 中。最后,重定向到 MainPage。
现在已经看过应用程序的所有代码,并且已经准备好运行。但是如何运行?Eclipse 将再次帮您轻松运行。
在本地进行测试
GAE SDK 将提供在本地运行项目的命令行工具。但是,我们希望利用 Eclipse,因此需要从 Eclipse 中运行所有内容。这将允许我们调试应用程序,稍后我们将看到。运行应用程序的第一步是编辑项目的 PYTHONPATH。完成此操作的最简单方法是在项目上右键单击并选择 Properties。这将打开项目属性。
图 4. 项目属性
正如您所见,需要在左侧菜单中选择 PyDev - PYTHONPATH。然后需要选择 Add source folder 并浏览到 GAE SDK 的安装位置。该位置将根据 OS 的不同而有所变化,并且可以自定义。对于 Windows,默认值(由安装程序设定)为 C:\Program Files\Google\AppEngine,而对于 OS X,默认值为 /usr/local/google_appengine。如果位于 Linux 中或者下载了 ZIP 而不是特定于 OS 的安装程序,请选择放置 SDK 的位置。可以是任意位置,但是需要让 Eclipse 知道该位置。该位置将称为 $APP_ENGINE_HOME。
现在,运行项目还需要做最后一件事:需要为它创建一个 Run 配置文件。为此,选择 Run > Open Run 对话框。
图 5. Run 对话框
该 Run 配置文件被称为 aggroGator。在 Main Module 下,浏览到 $APP_ENGINE_HOME 并选择 dev_appserver.py 脚本。这是模拟 GAE 生产环境的 Python 应用服务器。接下来,转到 Arguments 选项卡,如图 6 所示。
图 6. Arguments 选项卡
在 Program arguments 框中,输入 ${project_loc}/src。Eclipse 变量 ${project_loc} 仅指向当前项目的物理位置。需要将应用程序目录传递给 dev_appserver.py 脚本,因而传递给 /src。如果不把代码放到 src 目录中,则需要相应地调整实参。
现在已经准备好运行应用程序。如果单击 Run,则应当会在 Eclipse 控制台中看到清单 10 中的输出。
清单 10. 控制台输出
INFO 2008-06-08 05:00:29,236 appcfg.py] Server: appengine.google.com INFO 2008-06-08 05:00:29,283 appcfg.py] Checking for updates to the SDK. WARNING 2008-06-08 05:00:29,581 datastore_file_stub.py] Could not read datastore data from /var/folders/oo/ooKE4ln2HqC9exSMWxwprk+++TI/-Tmp-/dev_appserver.datastore WARNING 2008-06-08 05:00:29,582 datastore_file_stub.py] Could not read datastore data from /var/folders/oo/ooKE4ln2HqC9exSMWxwprk+++TI/-Tmp-/dev_appserver.datastore.history INFO 2008-06-08 05:00:29,606 dev_appserver_main.py] Running application aggrogator on port 8080: http://localhost:8080 |
注意,这段代码说明应用程序运行在 http://localhost:8080。在浏览器中转到该 URL 并尝试一下,如下所示:
图 7. aggroGator 欢迎屏幕
单击 Login 将显示图 8。
图 8. 登录屏幕
很明显,这是模拟的登录屏幕。您可以使用任意一个电子邮件地址,因为这不会实际访问 Google 身份验证服务。实际上,test@example.com 可以正常工作。登录后即可以开始添加服务。
图 9. 添加服务
现在可以通过添加服务开始玩转应用程序。如果返回到清单 8 的 AddService 控制器,各项服务提要的 URL 都是被硬编码到该类中。当然,这一切可以在将来改变,并且可能得到错误。这时有调试器会方便得多。让我们看一看如何将 Eclipse 调试器与 GAE 项目结合使用。
调试
使用诸如 Eclipse 之类的 IDE 的主要优点是更易于调试应用程序,甚至复杂的 Web 应用程序。首先需要为 GAE 项目做的是创建 Debug 配置文件。该配置文件类似于 Run 配置文件,因此只需选择项目并单击 Run > Open Debug 对话框。
图 10. Debug 对话框
Eclipse 将智能地把默认值设为先前创建的 Run 运行设置。无需修改,并且只需单击 Debug。查看 Eclipse 控制台,并且应当会看到类似于清单 11 的一些输出。
清单 11. 调试输出
pydev debugger: warning: psyco not available for debugger speedups pydev debugger: starting INFO 2008-06-08 05:18:37,704 appcfg.py] Server: appengine.google.com INFO 2008-06-08 05:18:37,755 appcfg.py] Checking for updates to the SDK. INFO 2008-06-08 05:18:38,196 dev_appserver_main.py] Running application aggrogator on port 8080: http://localhost:8080 |
前两行显示 pydev 调试器的输出。现在可以在项目中设置断点并且开始调试。在图 11 中,我们将调试 AddService 控制器。
图 11. 调试 AddService
现在可以开始逐步调试代码并检查变量。如果任何一项服务发生更改,或者需要添加新服务,这将使您可以轻松地找到并修复错误。
结束语
在本文中,很快就实现了一个完整的应用程序。Google App Engine SDK 已经安装并且与 Eclipse 连接在一起。这将允许我们快速编写、测试和调试代码。我们了解了 GAE 项目中的许多关键概念,包括 URL 路由、通过 URL 获取与外部站点进行交互、使用演示模板以及使用 Bigtable。我们的应用程序已经准备好部署到 GAE 中,将在第 2 部分中了解这些内容。(责任编辑:A6)