# 20.4 建造你自己的 Hello world 应用
现在让我们建造一个像 [20.3 节](20.3.md)中的 demo 一样的应用,但这次我们会探索得更深一些。
## 20.4.1 映像结构 (map-structure):创造一个简单的 http-handler
创建一个目录,并给它起一个你的应用程序特有的名字,如:helloapp。这个应用程序的所有文件都在这个目录中。在这个目录中再创建一个名为 hello 的目录。这将包含我们的 hello 包的 Go 源代码文件。然后在 hello 目录下,创建一个名为 helloworld2.go 的文件,并赋予其以下内容(事实上与上文中的 demo 应用几乎相同):
<u>[Listing 20.2 helloworld2_version1.go](examples\chapter_20\helloapp\hello\helloworld2_version1.go)</u>:
```go
package hello
import (
"fmt"
"net/http"
)
func init() {
http.HandleFunc("/", handler)
}
func handler(w http.ResponseWriter, r *http.Request) {
fmt.Fprint(w, "Hello, world!")
}
```
注意包的名称:在编写独立的 Go 程序时,我们会把这段代码放在 `package main` 中,但 Go GAE Runtime 提供了 `main` 包和 HTTP Listener,所以你应该把你的代码放在你选择的包中,此时指的是 hello 包。其次,由于 Go App Engine 应用程序通过 Web 服务器与外部世界进行通信,所以编写这些应用程序非常像编写独立的 Go Web 应用程序(见[第 15 章](15.0.md))。所以我们导入 `http` 包,并为我们的应用程序中使用的不同url 模式定义处理函数。我们没有 `main()` 函数,所以处理程序的设置必须移到 `init()` 函数中去。另外,网络服务器本身的启动是由 GAE 为我们完成的。我们的 Go 包 hello 对任何请求的响应是发送一个包含 "Hello, world!"的消息。
## 20.4.2 创建配置文件 app.yaml
所有的 GAE 应用程序都需要一个 yaml 配置文件 app.yaml,它包含了 GAE 的应用程序元数据(yaml 是一种文本文件格式,经常用于开源项目,更多信息见 www.yaml.org)。另外,这个文件告诉 App Engine 服务要使用哪个运行时,哪些 URL 应该由我们的 Go 程序处理。你可以从演示程序中复制一个 app.yaml 文件,把它放在映像 helloapp 里面,并删除 favicon.ico 的 handler。
应用程序的映像/文件结构应该如下:
```
helloapp\ // map of the GAE application
app.yaml // configuration file
hello\ // map containing the source files
helloworld2.go
```
只有app.yaml是必须的名字,映像、Go文件和包的名字可以有不同的选择,但按照惯例,它们的名字是一样的或类似的,根映像的后缀是 app。
app.yaml 由 AppEngine 读取和解释,AppEngine 以下时间段内托管和执行你的程序:
- 当您将您的应用程序上传到 AppEngine 以使其被托管。
- 当它被执行时。
- 当用户访问它时。
它可以包含注释,前面有一个 `#`,并包含以下语句:
```yaml
application: helloworld
version: 1
runtime: go
api_version: 3
# routing-table: routing of different urls to different types of handlers
handlers:
- url: /.*
script: _go_app
```
app.yaml 中的 `application: value helloworld` 是您的应用程序标识符。这个值在开发过程中可以是任何东西;以后在向 App Engine 注册您的应用程序时,您将选择一个唯一的标识符(在所有 GAE 应用程序中唯一)并更新这个值。
`version` 表示您的应用程序正在运行的版本:事实上,GAE 可以并行地运行您的应用程序的几个版本,但其中一个必须被指定为默认版本。它可以包含字母数字字符,以及连字符。因此,你可以运行一个测试版本,如T2-31 和一个生产版本 P2-1。
`runtime` 是编写应用程序的语言(其他允许的值是 Java 和 Python)。如果你在上传应用软件的新版本之前调整它,App Engine 将保留以前的版本,并让你使用管理控制台回退到以前的版本。
`api_version` 是本 SDK 中使用的 Go API 的版本;它们可能与以前的版本不兼容。你可以在以前的 api_version SDK 中构建你的应用程序的早期版本;如果 GAE 仍然允许,它们可以继续运行,但通常有一个时间限制,而你应该将你的应用程序更新到新的api版本:bin map中的gofix工具可能能够完成大部分所需的更新。
`handler` 部分是循环表 (routing table):它告诉 GAE 如何将发送到服务器上的请求映射到代码中。对于每一个传入的请求 url 模式(本地开发时在 http://localhost:8080/ 之后的部分,在云端运行时在 http://appname.appspot.com/ 之后的部分)与 url 后面的正则表达式相匹配。
对于第一个匹配的 url 模式,相应的脚本会被执行。在我们的例子中,每一个路径与正则表达式 `/.*` 相匹配的 URL 请求(即:所有 URL)都应该由 Go 程序处理。`_go_app` 值是 dev_appserver.py 识别的一个神奇字符串;生产的 App Engine 服务器会忽略它。
如果你看一下演示的 helloworld 应用程序的 app.yaml 文件,你会发现它在处理程序中包含一个初始部分:
```yaml
handlers:
- url: /favicon\.ico
static_files: favicon.ico
upload: favicon\.ico
- url: /.*
script: _go_app
```
一些文件 (`static_files`) ,如图片,不会改变(在这个例子中是图片favicon.ico)。这些文件可以放在不同的 AppEngine 服务器上的一种共同缓存中,使它们能够更快地提供给用户。如果您的应用程序有许多这样的文件,把它们放在一个单独的目录中,按惯例命名为 static。
`upload` 表示当您部署应用程序时,什么必须上传到云端;例如,如果它包含 images/(\*.ico|\*.gif|\*.jpg),它将把本地 images 目录内所有这些类型的文件上传到 AppEngine 服务器。
正如我们将看到的,大多数 GAE 应用程序也使用模板文件,这些文件可以存储在根应用程序地图中,或在一个特殊的目录 tmpl 中。
因此,一个 GAE 应用程序的一般结构可能是:
```
yourapp\ // map of the GAE application
app.yaml // configuration file
yourpackage\ // map containing the source files
package1.go
…
tmpl\ // map containing template files
root.html
update.html
…
static\ // map containing static files
yourapp.ico
…
```
与 demo 一样,在控制台窗口中进入包含 helloapp 的映像,并发出如下命令:`dev_appserver.py helloapp`
或者你可以通过任何一个映像的 console 窗口并且唤醒:
```bash
dev_appserver.py /path_to_map_helloapp/helloapp
```
在这两种情况下,网络服务器现在都在运行,并监听 8080 端口的请求。通过在你的网络浏览器中访问以下 URL 来测试该应用程序:http://localhost:8080/
你应该看到: Hello, world!
在服务器控制台,出现以下文字:
```
$ dev_appserver.py helloapp
INFO 2011-10-31 08:54:29,021 appengine_rpc.py:159] Server: appengine.google.com
INFO 2011-10-31 08:54:29,025 appcfg.py:463] Checking for updates to the SDK.
INFO 2011-10-31 08:54:29,316 appcfg.py:481] The SDK is up to date.
WARNING 2011-10-31 08:54:29,316 datastore_file_stub.py:512] Could not read datastore
data from /tmp/dev_appserver.datastore
INFO 2011-10-31 08:54:29,317 rdbms_sqlite.py:58] Connecting to SQLite database ‘’
with file ‘/tmp/dev_appserver.rdbms’
INFO 2011-10-31 08:54:29,638 dev_appserver_multiprocess.py:637] Running application
helloworld on port 8080: http://localhost:8080
<-(A)
INFO 2011-10-31 08:56:13,148 __init__.py:365] building _go_app
<-(B)
INFO 2011-10-31 08:56:15,073 __init__.py:351] running _go_app
INFO 2011-10-31 08:56:15,188 dev_appserver.py:4143] “GET / HTTP/1.1” 200 -
<-(C)
```
在 (A) 处服务器准备好了,在 (B) 处服务器编译并运行 Go 程序,在 (C) 处我们的应用程序的请求进来了,此时 HTML 输出页面被提供到服务器上。
当服务器被终止或尚未启动,而客户端请求网址 http://localhost:8080/,浏览器在FireFox 中会打印出这样的信息:
```
Unable to connect Firefox can’t establish a connection to the server at localhost:8080.
```
## 20.4.3 迭代开发
开发应用的服务器会观察你的文件中的变化,当你更新你的源代码时(编辑+保存),它重新编译它们并重新启动你的本地应用;不需要重新启动 dev_appserver.py
现在试试:让 Web 服务器运行,然后编辑 helloworld2.go,将 "Hello, world!" 改为其他内容。重新加载 http://localhost:8080/,就可以看到变化了:这和编写 Rails 或 Django 应用程序一样,都是动态运行的。
要关闭 Web 服务器,确保终端窗口处于活动状态,然后按 Ctrl+C(或适当的用于控制台的 "break "键):
```
INFO 2011-10-31 08:56:21,420 dev_appserver.py:4143] “GET / HTTP/1.1” 200 -
INFO 2011-10-31 08:57:59,836 __init__.py:365] building _go_app <-(D)
INFO 2011-10-31 08:58:00,365 __init__.py:351] running _go_app
INFO 2011-10-31 08:58:00,480 dev_appserver.py:4143] “GET / HTTP/1.1” 200 -
^CINFO 2011-10-31 08:58:32,769 dev_appserver_main.py:665] Server interrupted by user,
terminating <-(E)
```
这可以从上面第一个列表之后的服务器控制台输出中看到:在 (D) 处,apperver 看到 Go 的源代码被改变了,并重新编译;在 (E) 处,服务器被终止了。
## 20.4.4. 与 GoClipse IDE 的集成
a) 窗口/首选项/Go:
将所有内容指向 GAE 的 Go 根目录
b) 运行/外部工具/外部工具配置/选择程序:
制作新的配置:点击 New 按钮。
名称:GAE
位置:/home/user/google_appengine/dev_appserver.py
工作目录:/home/user/workspace/bedilly/src/pkg/helloapp
参数: home/user/workspace/bedilly/src/pkg/helloapp
应用/运行
通过配置一个外部工具,部署你的应用程序也很容易:http://code.google.com/p/goclipse/wiki/DeployingToGoogleAppEngineFromEclipse
## 链接
- [目录](directory.md)
- 上一节:[安装 Go App Engine SDK](20.3.md)
- 下一节:[使用用户服务和探索其 API](20.5.md)