mirror of
https://github.com/unknwon/the-way-to-go_ZH_CN.git
synced 2025-08-11 23:08:34 +08:00
9
eBook/20.0.md
Normal file
9
eBook/20.0.md
Normal file
@@ -0,0 +1,9 @@
|
||||
# 20.0 Google App Engine 中的 Go
|
||||
|
||||
本章中的网站地址和原书有所出入,但并不影响,因为 Google 已经对这些网址做了重定向。自这本书出版以来,GAE 的安装方式和使用方式已经发生了变化,原书内容仅供参考,请以[文档](https://cloud.google.com/docs)或网站指引为准。
|
||||
|
||||
## 链接
|
||||
|
||||
- [目录](directory.md)
|
||||
- 上一节:[总结与增强](19.10.md)
|
||||
- 下一节:[什么是 Google App Engine?](20.1.md)
|
32
eBook/20.1.md
Normal file
32
eBook/20.1.md
Normal file
@@ -0,0 +1,32 @@
|
||||
# 20.1 什么是 Google App Engine?
|
||||
|
||||
Google 应用引擎(Google App Engine,下简称 GAE)是 Google 用来**云编程**的方案:让你在 Google 的基础架构上运行 web 应用和存储数据,而不用担心服务器、网络、操作系统或者数据存储等等问题。这种资源的集合通常被称为云,其维护完全由谷歌本身负责。对于你这个开发者来说,只有你的应用程序和它能提供给用户的服务才是重要的。用户可以在任何可以连接到互联网的设备上使用和运行你的应用程序,你只需为你的软件真正需要的资源(CPU 处理时间、网络带宽、磁盘存储、内存等)付费。当有高峰期时,云平台会自动为你的应用程序增加资源,并在不再需要时减少资源:可扩展性是云计算的最大优势之一。协作型应用(一群人一起工作、分享数据、交流等)、提供服务的应用和进行大型计算的应用是云计算的优秀候选者。云计算应用的典型用户界面是一个浏览器环境。
|
||||
|
||||
GAE 在 2008 年推出,并支持 Python 应用程序,并在 2009 年增加了对于 Java 的支持;自 2011 年开始,也有了对 Go 的支持。其开始页面为:https://cloud.google.com/appengine/
|
||||
|
||||
谷歌的 App Engine 为构建和部署网络应用提供了一种可靠、可扩展和简单的方式。超过十万个应用程序被托管在 https://console.cloud.google.com/ 和使用 App Engine 基础设施的自定义域上。它是一个 "平台即服务 "的环境,比 Amazon EC2 这样的 "基础云设施 "的运行水平更高,其试图以更高的效率分享资源。
|
||||
|
||||
<u>沙盒:</u>
|
||||
|
||||
你的应用在一个叫做*“沙盒 (sandbox)”*的环境中运行,其提供到底层操作系统有限的访问权。这些限制能够允许 App Engine 在多个服务器上分配应用程序的网络请求,并启动和停止服务器以满足流量需求。沙盒将你的应用程序隔离在它自己的安全、可靠的环境中,它独立于 Web 服务器的硬件、操作系统和物理位置。上文中所说的限制有:
|
||||
|
||||
- 应用程序不能写到服务器的文件系统;只有在应用程序内上传的文件可以被读取。应用必须使用 *App Engine 数据存储、记忆库 (memcahe)* 或其他服务来处理所有在请求之间持续存在的数据。
|
||||
- 代码仅在响应网络请求、排队或计划任务时运行,并且响应必须在 60 秒内;请求处理程序不能产生一个子进程或在响应发出后执行代码。
|
||||
- 它只能通过提供的 URL 获取和电子邮件服务访问互联网上的其他计算机。其他计算机只能通过在标准 HTTP 协议(或 HTTPS)下的请求来连接到应用程序。
|
||||
|
||||
<u>服务概览:</u>
|
||||
|
||||
1. <u>数据</u> 存储在基于谷歌 Bigtable 的 GAE *数据存储*中:这是一个分布式数据存储服务,具有查询引擎和交易功能;它随着你的数据自动增长。它不是一个传统的关系型数据库,所以不允许使用经典的 SQL 和连接;但它为你提供了一种类似 SQL 的查询语言,称为 *GQL*。数据对象,称为*实体*,有一个*类型*和一组属性。查询可以检索给定种类的实体,并根据属性值进行过滤和排序。属性值可以是任何支持的属性值类型。实体可以被分组——这就是交易 (transaction) 发生的地方:任何的交易都必须在一个组内。你的实体没有数据库模式:实体之间的任何结构必须由你的应用程序代码提供和强制执行。更新使用*乐观的并发控制 (optimistic concurrency control)*,意味着依照最后一次更新改变数据。
|
||||
|
||||
2) <u>应用程序认证</u> 可以与谷歌账户集成
|
||||
3) <u>URL Fetch</u>:通过这项服务,你的应用程序可以访问互联网上的资源,如网络服务或其他数据。
|
||||
4) <u>邮件</u>:也是一个内置的服务,可以在应用程序中使用。
|
||||
5) <u>Memcache</u>:一个高性能的、内存内的键值对缓存;它对那些不需要数据存储的持久性和事务性功能的数据很有用,比如临时数据或从数据存储复制到缓存的高速访问数据。
|
||||
6) <u>图像操作</u>:(译者注:原文这里就没有东西)
|
||||
7) <u>预定任务和任务队列</u> (cron jobs):一个除了响应网络请求外,还可以执行任务的应用程序;它可以按照你配置的时间表执行这些任务,比如在一个每天的或每小时的基数上运行。另外,应用程序也可以执行由应用程序本身分配到队列中的任务,例如在处理一个请求时创建的后台任务。
|
||||
|
||||
## 链接
|
||||
|
||||
- [目录](directory.md)
|
||||
- 上一节:[Google 应用引擎中的 Go](20.0.md)
|
||||
- 下一节:[云上的 Go](20.2.md)
|
16
eBook/20.2.md
Normal file
16
eBook/20.2.md
Normal file
@@ -0,0 +1,16 @@
|
||||
# 20.2 云上的 Go
|
||||
|
||||
2011 年 5 月10 日,在谷歌 I/O 大会上首次宣布了 GAE 对 Go 的支持。其最初是试验性的,只针对注册的测试人员,到 2011 年 7 月 21 日才完全对每个开发者开放。在撰写本文时(2012 年 1 月),目前的 Go App Engine SDK 是 1.6.1(2011-12-13 发布);它只存在于 Linux 和 Mac OS X(10.5 或更高版本),包括 32 和 64 位。支持的 Go 工具链是 r60.3 版本;一些变化是向后不兼容的,其 SDK 的 api_version 是 3。
|
||||
|
||||
当 Go 应用在 App Engine上运行时,它是用 64 位 x86 编译器 (6g) 编译的。在一个给定的实例中只运行一个线程。也就是说,所有的 goroutines 都在同一个操作系统的线程中运行,所以对于一个给定的客户请求来说,没有 CPU 并行性可言。
|
||||
|
||||
Go 是第一个在 App Engine 上运行的编译语言。它之所以能大放异彩,是因为它与其他两个语言运行时相比,表现非常出色。
|
||||
|
||||
- 和 Java 相比:Go 有更好的实例启动时间和更多的并发可能性。
|
||||
- 和 Python 相比:Go 的执行速度要好得多。
|
||||
|
||||
## 链接
|
||||
|
||||
- [目录](directory.md)
|
||||
- 上一节:[什么是 Google App Engine?](20.1.md)
|
||||
- 下一节:[安装 Go App Engine SDK:为 Go 部署的开发环境](20.3.md)
|
110
eBook/20.3.md
Normal file
110
eBook/20.3.md
Normal file
@@ -0,0 +1,110 @@
|
||||
# 20.3 安装 Go App Engine SDK:为 Go 部署的开发环境
|
||||
|
||||
## 20.3.1 安装
|
||||
|
||||
从下载页面下载合适你的系统的 GAE SDK 压缩文件:https://cloud.google.com/appengine/downloads
|
||||
|
||||
例如:你的系统是 64 位的 Linux Ubuntu 11.10 系统,则下载 go_appengine_sdk_linux_amd64-1.6.1.zip 文件。
|
||||
|
||||
用 Archieve Manager 打开并且提取出到你选择的一个目录下(例如你的 home 目录):它会创造一个叫做 google_appengine 的文件,其包含了整个 AppEngine for Go 的开发环境。例如在 /home/user/google_appengine 或者 "install root"/google_appengine/goroot 目录下。
|
||||
|
||||
这个环境包含了您在本地开发、构建和测试您的应用程序所需的一切:它包括一个 AppEngine 服务器来测试您的应用程序,一个 DataStore,用来在这里存储数据,就像您最终在 AppEngine 服务器上托管的实时应用程序一样;以及其他 API 支持和工具,使您可以模仿真正的 AppEngine 来进行开发和测试的目的。由于这个 AppEngine 环境是针对 Go 的,它也包含了 适当的 Go 编译器、软件包和工具作为下载的一部分。
|
||||
|
||||
<u>GAE-Go 和普通 Go 之间的区别:</u>
|
||||
|
||||
GAE-Go 的运行时 (runtime) 提供完整的 Go 语言和几乎所有的标准库,除了一些在 App Engine 环境中没有意义的东西:
|
||||
|
||||
- 现在没有 `unsafe` 包了,并且 `syscall` 包被修剪过了。
|
||||
- 它不支持 cgo(不与 C 库交互),甚至:你不能在 GAE 项目中使用任何二进制库(Go 或其他)。你需要回溯所有东西的源,直到 GAE 编译/链接到了所有的源代码。
|
||||
- 不支持 go install 工具
|
||||
- GAE 经常落后于主发行版一个或多个主要版本。此外,必须考虑到沙盒环境的限制(参考 [20.1 节](20.1.md))。因此,如果尝试打开一个 socket 或写一个文件将返回一个 `os.EINVAL` 错误。
|
||||
|
||||
因此,把你的 GAE 和非 GAE-Go 工具完全分开;如果你只做 GAE 开发,你可以完全不使用标准工具。在 google_appengine 目录下有几个 Python 脚本,是 Google App Engine 的基本工作程序。确保它们是可执行的(如果不是,请使用 `chmod +x *.py` 命令)。同时将它们的路径添加到 `PATH` 变量中,以便你在调用它们时不必包含完整的路径:例如,如果你有一个 bash shell,在你的 .bashrc 或 .profile 文件中添加一行:
|
||||
|
||||
```bash
|
||||
export PATH=/home/user/google_appengine:$PATH
|
||||
```
|
||||
|
||||
<u>注意:</u>
|
||||
|
||||
1) 如果你已经有了一个工作的 Go 环境(就像你在阅读本书时那样),这个 AppEngine 的安装是在它之外的,与它平行而不影响它;特别是您不需要改变您操作系统中的 Go 环境变量。AppEngine 上的 Go 有其自己完全独立的环境,包含自己的 Go 版本(在 "install root"/google_appengine/goroot 目录下)
|
||||
2) 下载文档也是一个好主意,这样你可以在离线时浏览。从官网下载 google-appengine-docs-20111011.zip 并解压。
|
||||
3) GAE 大量使用 Python,这在 Mac OS X 和 Linux 上默认安装;如果由于某种原因不是这种情况,请从 www.python.org 下载并安装 Python 2.5。
|
||||
|
||||
4. 源代码:库和 SDK 是开源的:http://code.google.com/p/appengine-go/。用以下方法下载:
|
||||
|
||||
```bash
|
||||
hg clone https://code.google.com/p/appengine-go/
|
||||
```
|
||||
|
||||
5) 一个给定应用程序的所有 Go 包都内置在一个单一的可执行文件中,并且请求调度由 Go 程序本身处理。由 Go 程序本身处理;这与 Java 和 Python SDK 的情况不同。
|
||||
在 [第 20.8 节](20.8.md),我们将看到如何连接到 GAE 云来部署你的应用程序。但在这你将在你刚刚安装的本地GAE环境中开发、测试和运行你的应用程序,这是对开发环境最好的模拟。
|
||||
|
||||
### 20.3.2 检查和测试
|
||||
|
||||
<u>检查安装:</u>
|
||||
|
||||
为了控制一切工作正常,在控制台中进入 google_appengine,通过调用 dev_appserver.py 来调用本地 AppEngine 服务器。
|
||||
|
||||
如果你看到以下内容:
|
||||
|
||||
```
|
||||
Invalid arguments
|
||||
Runs a development application server for an application.
|
||||
dev_appserver.py [options]
|
||||
Application root must be …
|
||||
```
|
||||
|
||||
则一切正常。
|
||||
|
||||
<u>运行一个演示应用程序:</u>
|
||||
|
||||
在 SDK 捆绑包中有一些演示应用程序。让我们运行一个以确保一切正常。
|
||||
|
||||
- 进入 google_appengine/demos:在那里你可以看到一些文件夹,例如 helloworld、guestbook 等。
|
||||
- 在 demos 目录下,执行命令:`dev_appserver.py helloworld`
|
||||
|
||||
注意,这将自动编译、链接和运行 Go 程序。
|
||||
|
||||
- 有一些警告以及信息 ,但如果最后一行如下:
|
||||
|
||||
```
|
||||
Running helloworld on port 8080: http://localhost:8080
|
||||
```
|
||||
|
||||
就可以了。此时 helloworld 应用程序已经在本地 AppEngine 服务器中被实例化,并且准备好在 `8080` 端口为您机器上的用户提供服务。
|
||||
|
||||
- 打开浏览器并访问 http://localhost:8080
|
||||
|
||||
如果你看到如下页面:
|
||||
|
||||
Hello, World! 세상아 안녕!!
|
||||
|
||||
你就已经成功在本地的 GAE 引擎上运行了一个 Go web 应用了。
|
||||
|
||||
刚才运行的 Go 源代码如下:
|
||||
|
||||
<u>Listing 20.1 helloworld.go:</u>
|
||||
|
||||
```go
|
||||
package helloworld
|
||||
import (
|
||||
“fmt”
|
||||
“net/http”
|
||||
)
|
||||
func init() {
|
||||
http.HandleFunc(“/”, handle)
|
||||
}
|
||||
func handle(w http.ResponseWriter, r *http.Request) {
|
||||
// some Chinese characters after World!
|
||||
fmt.Fprint(w, “<html><body>Hello, World! 세상아 안녕!! </body></html>”)
|
||||
}
|
||||
```
|
||||
|
||||
这是一个简单的 web 应用 (参考 [15 章](15.0.md)),其在 `init()` 函数当中就启动了整个的 handler。注意它被它自己的包包含。
|
||||
|
||||
## 链接
|
||||
|
||||
- [目录](directory.md)
|
||||
- 上一节:[云上的 Go](20.2.md)
|
||||
- 下一节:[建造你自己的 Hello world 应用](20.4.md)
|
192
eBook/20.4.md
Normal file
192
eBook/20.4.md
Normal file
@@ -0,0 +1,192 @@
|
||||
# 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)
|
||||
|
88
eBook/20.5.md
Normal file
88
eBook/20.5.md
Normal file
@@ -0,0 +1,88 @@
|
||||
# 20.5 使用用户服务和探索其 API
|
||||
|
||||
GAE 提供了几个基于 Google 基础设施的有用服务。正如[第 20.1 节](20.1.md)中提到的:GAE 提供了一个 Users 服务,它可以让你的应用程序与 Google 用户账户集成。有了用户服务,您的用户可以使用他们已经拥有的谷歌账户来登录您的应用程序。用户服务使您可以轻松地对该应用程序的问候语进行个性化处理。
|
||||
|
||||
编辑 helloworld2.go 文件,用以下 Go 代码替换它:
|
||||
|
||||
<u>[Listing 20.3 helloworld2_version2.go](examples/chapter_20/helloapp/hello/helloworld2_version2.go)</u>:
|
||||
|
||||
```go
|
||||
package hello
|
||||
|
||||
import (
|
||||
"appengine"
|
||||
"appengine/user"
|
||||
"fmt"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
func init() {
|
||||
http.HandleFunc("/", handler)
|
||||
}
|
||||
|
||||
func handler(w http.ResponseWriter, r *http.Request) {
|
||||
c := appengine.NewContext(r)
|
||||
u := user.Current(c)
|
||||
if u == nil {
|
||||
url, err := user.LoginURL(c, r.URL.String())
|
||||
if err != nil {
|
||||
http.Error(w, err.String(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
w.Header().Set("Location", url)
|
||||
w.WriteHeader(http.StatusFound)
|
||||
return
|
||||
}
|
||||
fmt.Fprintf(w, "Hello, %v!", u)
|
||||
}
|
||||
```
|
||||
|
||||
通过在浏览器中重新加载页面来测试它。你的应用程序会给你一个链接,当你遵循这个链接时,会把你重定向到适合测试你的应用程序的本地版本的谷歌登录页面。你可以在这个页面中输入任何你喜欢的用户名,你的应用程序将看到一个基于该用户名的假的 `user.User` 值。当你的应用程序在 App Engine 上运行时,用户将被引导到 Google 账户的登录页面,然后在成功登录或创建账户后,会被重定向到你的应用程序。
|
||||
|
||||
<u>用户API:</u>
|
||||
|
||||
为了访问这个,我们需要导入一些专门针对 GAE 的 Go 包,即一般的 `appengine` 和 `appengine/user`。
|
||||
|
||||
在处理程序中,我们首先需要制作一个与当前请求r相关联的Context对象,这在一行中完成:
|
||||
|
||||
```go
|
||||
c := appengine.NewContext(r)
|
||||
```
|
||||
|
||||
`appengine.NewContext()` 函数在这里返回一个名为 `c` 的 `appengine.Context` 值:这是 Go App Engine SDK 中许多函数用来与 App Engine 服务通信的值。然后我们从这个上下文中测试是否已经有一个用户在此时登录,方法是:
|
||||
|
||||
```go
|
||||
u := user.Current(c)
|
||||
```
|
||||
|
||||
如果是这样的话,`user.Current` 会返回一个指向用户的 `user.User` 值的指针;否则会返回 `nil`。如果用户还没有登录,即 `u == nil` 时,通过调用用户的浏览器重定向到谷歌账户的登录界面。
|
||||
|
||||
```go
|
||||
url, err := user.LoginURL(c, r.URL.String())
|
||||
```
|
||||
|
||||
第 2 个参数 `r.URL.String()` 是当前请求的 url,这样谷歌账户登录机制可以在成功登录后进行*重定向*:它将在用户登录或注册新账户后将其送回这里。登录界面的发送是通过设置一个 Location 数据头并返回一个 HTTP 状态代码 302“Found”来完成的。
|
||||
|
||||
`LoginURL()` 函数返回一个 error 值作为其第二个参数。尽管这里不太可能发生错误,但检查它并在适当的时候向用户显示错误是很好的做法(在这种情况下,用 http.Error helper):
|
||||
|
||||
```go
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
```
|
||||
|
||||
当用户登录后,我们使用与用户账户相关的名字显示一条个性化的信息:
|
||||
|
||||
```go
|
||||
fmt.Fprintf(w, "Hello, %v!", u)
|
||||
```
|
||||
|
||||
在这种情况下,`fmt.Fprintf()` 函数调用 `*user.User` 的 `String()` 方法来获得字符串形式的用户名称。更多信息可以在这个参考资料中找到:http://code.google.com/appengine/docs/go/users/
|
||||
|
||||
## 链接
|
||||
|
||||
- [目录](directory.md)
|
||||
- 上一节:[建造你自己的 Hello world 应用](20.4.md)
|
||||
- 下一节:[处理窗口](20.6.md)
|
||||
|
63
eBook/20.6.md
Normal file
63
eBook/20.6.md
Normal file
@@ -0,0 +1,63 @@
|
||||
# 20.6 处理窗口
|
||||
|
||||
正如我们在 [15.6](15.6.md)/[7](15.7.md) 节中所看到的,`template` 包经常被用于 web 应用,所以也可以被用于 GAE 应用。下面的应用程序让用户输入一个文本。首先,一个留言簿表格显示出来(通过 `/` 根处理程序),当它被发布时,`sign()` 处理程序将这个文本替换到产生的 html 响应中。`sign()` 函数通过调用 `r.FormValue` 获得窗口数据,并将其传递给 `signTemplate.Execute()`,后者将渲染的模板写入 `http.ResponseWriter`。
|
||||
|
||||
编辑文件 helloworld2.go,用下面的 Go 代码替换它,并试运行:
|
||||
|
||||
<u>[Listing 20.4 helloworld2_version3.go:](examples\chapter_20\helloapp\hello\helloworld2_version3.go)</u>
|
||||
|
||||
```go
|
||||
package hello
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"template"
|
||||
)
|
||||
|
||||
const guestbookForm = `
|
||||
<html>
|
||||
<body>
|
||||
<form action="/sign" method="post">
|
||||
<div><textarea name="content" rows="3" cols="60"></textarea></div>
|
||||
<div><input type="submit" value="Sign Guestbook"></div>
|
||||
</form>
|
||||
</body>
|
||||
</html>
|
||||
`
|
||||
const signTemplateHTML = `
|
||||
<html>
|
||||
<body>
|
||||
<p>You wrote:</p>
|
||||
<pre>{{html .}}</pre>
|
||||
</body>
|
||||
</html>
|
||||
`
|
||||
|
||||
var signTemplate = template.Must(template.New("sign").Parse(signTemplateHTML))
|
||||
|
||||
func init() {
|
||||
http.HandleFunc("/", root)
|
||||
http.HandleFunc("/sign", sign)
|
||||
}
|
||||
|
||||
func root(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Content-Type", "text/html")
|
||||
fmt.Fprint(w, guestbookForm)
|
||||
}
|
||||
|
||||
func sign(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Content-Type", "text/html")
|
||||
err := signTemplate.Execute(w, r.FormValue("content"))
|
||||
if err != nil {
|
||||
http.Error(w, err.String(), http.StatusInternalServerError)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 链接
|
||||
|
||||
- [目录](directory.md)
|
||||
- 上一节:[使用用户服务和探索其 API](20.5.md)
|
||||
- 下一节:[使用数据存储](20.7.md)
|
||||
|
136
eBook/20.7.md
Normal file
136
eBook/20.7.md
Normal file
@@ -0,0 +1,136 @@
|
||||
# 20.7 使用数据存储
|
||||
|
||||
我们现在有了一种用 html 窗口来收集用户信息的方法。通常我们希望使这些信息持久化:我们需要一个地方来放置这些信息,并且需要一个方法来取回这些信息。GAE 在这里为我们提供了它的 DataStore 设施:一个非关系型 (non-relational) 数据库,它可以跨网络服务器甚至跨机器持久保存您的数据。事实上,用户的下一个请求很可能是在不同的计算机上运行的不同的网络服务器,但 GAE 的基础设施在一个简单的 API 后面处理了所有的数据分布、复制和负载平衡,你还可以得到一个强大的查询引擎。
|
||||
|
||||
我们现在将对我们的例子进行一些扩展,制作一个问候结构,该结构可以包含问候的作者、内容和时间,我们要存储这些内容。这是你要做的第一件事:为你的程序*实体*(即你的程序所处理的对象)制作一个合适的数据结构,大多数情况下这将是一个 `struct`。在运行的程序中,这个结构的内存值将包含来自该实体的 DataStore 的数据。
|
||||
|
||||
接下来我们程序的版本如下:
|
||||
|
||||
(A) `url:/`: 检索所有存储的问候语并通过 `template` 包显示它们
|
||||
|
||||
(B) `url:/sign`:存储一个新的问候语到数据存储里面
|
||||
|
||||
我们现在需要导入 `appengin/datastore` 包:
|
||||
|
||||
<u>[Listing 20.5 helloworld2_version4.go:](examples\chapter_20\helloapp\hello\helloworld2_version4.go)</u>
|
||||
|
||||
```go
|
||||
package hello
|
||||
|
||||
import (
|
||||
"appengine"
|
||||
"appengine/datastore"
|
||||
"appengine/user"
|
||||
"net/http"
|
||||
"template"
|
||||
"time"
|
||||
)
|
||||
|
||||
const guestbookTemplateHTML = `
|
||||
<html>
|
||||
<body>
|
||||
{{range .}}
|
||||
{{with .Author}}
|
||||
<p><b>{{html .}}</b> wrote:</p>
|
||||
{{else}}
|
||||
<p>An anonymous person wrote:</p>
|
||||
{{end}}
|
||||
<pre>{{html .Content}}</pre>
|
||||
<pre>{{html .Date}}</pre>
|
||||
{{end}}
|
||||
<form action="/sign" method="post">
|
||||
<div><textarea name="content" rows="3" cols="60"></textarea></div>
|
||||
<div><input type="submit" value="Sign Guestbook"></div>
|
||||
</form>
|
||||
</body>
|
||||
</html>
|
||||
`
|
||||
|
||||
var guestbookTemplate = template.Must(template.New("book").Parse(guestbookTemplateHTML))
|
||||
|
||||
type Greeting struct {
|
||||
Author string
|
||||
Content string
|
||||
Date datastore.Time
|
||||
}
|
||||
|
||||
func init() {
|
||||
http.HandleFunc("/", root)
|
||||
http.HandleFunc("/sign", sign)
|
||||
}
|
||||
|
||||
func root(w http.ResponseWriter, r *http.Request) {
|
||||
c := appengine.NewContext(r)
|
||||
q := datastore.NewQuery("Greeting").Order("-Date").Limit(10)
|
||||
greetings := make([]Greeting, 0, 10)
|
||||
if _, err := q.GetAll(c, &greetings); err != nil {
|
||||
http.Error(w, err.String(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
if err := guestbookTemplate.Execute(w, greetings); err != nil {
|
||||
http.Error(w, err.String(), http.StatusInternalServerError)
|
||||
}
|
||||
}
|
||||
|
||||
func sign(w http.ResponseWriter, r *http.Request) {
|
||||
c := appengine.NewContext(r)
|
||||
g := Greeting{
|
||||
Content: r.FormValue("content"),
|
||||
Date: datastore.SecondsToTime(time.Seconds()),
|
||||
}
|
||||
if u := user.Current(c); u != nil {
|
||||
g.Author = u.String()
|
||||
}
|
||||
_, err := datastore.Put(c, datastore.NewIncompleteKey(c, "Greeting", nil), &g)
|
||||
if err != nil {
|
||||
http.Error(w, err.String(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
http.Redirect(w, r, "/", http.StatusFound)
|
||||
}
|
||||
```
|
||||
|
||||
信号处理程序 (B) 从表单和上下文数据中构建了一个问候值 `g`,然后用 `datastore.Put()` 存储它。DataStore 在内部为数据记录生成自己的唯一 key;为了做到这一点,我们调用 `Put()` 函数,将
|
||||
|
||||
```go
|
||||
datastore.NewIncompleteKey(c, "Greeting", nil)
|
||||
```
|
||||
|
||||
作为第 2 个参数(这个函数需要实体 `Greeting` 的名字)。`Put()` 的第 3 个参数 `&g` 是一个包含 value 的 `struct` (严格来说,此处应为指向这个 `struct` 的指针)。
|
||||
|
||||
`datastore` 包提供了一个查询类型,用于查询数据存储并迭代结果。根处理程序正是通过构造一个查询 `q()` 来实现的,该查询按照日期降序从DataStore 中请求问候对象,限制为 10 个。
|
||||
|
||||
```go
|
||||
q := datastore.NewQuery("Greeting").Order("-Date").Limit(10)
|
||||
```
|
||||
|
||||
我们需要一个数据结构来存储我们的查询结果,也就是 `greetings`,一个 Greeting 值的切片。我们对 `q.GetAll(c, &greetings)` 的调用检索了数据,并将它们存储在我们的切片中;当然,我们会检查可能的错误。
|
||||
|
||||
当一切正常时,我们通过与我们的模板合并来显示数据:
|
||||
|
||||
```go
|
||||
guestbookTemplate.Execute(w, greetings)
|
||||
```
|
||||
|
||||
这是由一个 `range` 结构执行的(参考 [15.7.6 节](15.7.md))。
|
||||
|
||||
再次通过编辑 helloworld2.go 文件进行测试,用 listing 20.5 中的代码替换它;在问候中间的间隙关闭浏览器会话,这样你可以检查它们是否被持久保存。
|
||||
|
||||
<u>清除开发用服务器的数据存储:</u>
|
||||
|
||||
开发用 web 服务器使用一个本地版本的数据存储来测试你的应用程序,使用临时文件。只要临时文件存在,数据就会持续存在,除非你要求,否则 Web 服务器不会重置这些文件。如果你想让开发用服务器在启动前擦除其数据存储,请在启动服务器时使用 `--clear_datastore` 选项:
|
||||
|
||||
```bash
|
||||
dev_appserver.py --clear_datastore helloapp/
|
||||
```
|
||||
|
||||
<u>调试:</u>
|
||||
|
||||
gdb 调试器可以和 Go 一起使用(见 http://golang.org/doc/debugging_with_gdb.html),你可以将 gdb 附加到一个现有的进程中。因此:像往常一样启动 dev_appserver.py,并访问 `localhost:8080` 来启动你的 Go 应用程序。然后执行: `$ ps ax | grep _go_app`,找到 \_go\_app 的 PID 和路径。如果你把 gdb 连接到这个上面,那么你对 dev_appserver 的下一个 HTTP 请求应该会碰到你在代码中设置的任何断点。记住,如果你修改了 Go 的源代码,那么开发应用服务器将重新编译并执行不同的 \_go\_app。
|
||||
|
||||
## 链接
|
||||
|
||||
- [目录](directory.md)
|
||||
- 上一节:[处理窗口](20.6.md)
|
||||
- 下一节:[上传到云端](20.8.md)
|
||||
|
42
eBook/20.8.md
Normal file
42
eBook/20.8.md
Normal file
@@ -0,0 +1,42 @@
|
||||
# 20.8 上传到云端
|
||||
|
||||
我们的留言簿应用程序使用谷歌账户认证用户,让他们提交信息,并显示其他用户留下的信息,让我们认为其基本功能完成了:我们现在将把它部署在云中。如果我们的应用程序会变得非常流行,我们不需要改变任何东西,因为 GAE 会自动处理扩展。
|
||||
|
||||
但是,首先你需要有一个谷歌账户,如 gmail 地址;你可以在 www.google.com/accounts 快速建立一个账户。
|
||||
|
||||
创建和管理 App Engine 网络应用程序是通过 App Engine 管理控制台网站进行的:https://appengine.google.com/
|
||||
|
||||
在快速的 SMS 验证程序之后,你会看到 "创建一个应用程序 "的页面。选择一个应用程序标识符 (*application identifier*)(对所有 GAE 应用程序来说是唯一的),如 ib-tutgae.appspot.com;加上前缀 http://,这将成为你的应用程序的网址。这个标识符以后不能更改,如果是私人应用程序,用你的名字缩写,如果是商业应用程序,用你的公司名称作为前缀是比较好的。然后选择一个应用程序的标题,这在您的应用程序中是可见的,并可以在之后更改,例如 "GAE 应用程序手册"。保留默认的谷歌认证和高复制数据存储,低于一定的配额之下,GAE 将免费运行您的应用程序。点击 "创建应用程序 "按钮后,将出现一个屏幕,显示 "应用程序成功注册 "的信息。
|
||||
|
||||
要在云中上传您的应用程序,请执行以下操作。
|
||||
1) 编辑 app.yaml 文件,将 `application: setting` 的值从 `helloworld` 改为你注册的应用程序 `ib-tutgae`
|
||||
2) 在 GAE 中上传和配置您的应用程序,使用脚本 appcfg.py 执行命令:`appcfg.py update helloapp/`
|
||||
|
||||
通过询问您的谷歌账户数据进行验证,如果一切成功,您的应用程序现在就可以部署在 App Engine 上了!
|
||||
|
||||
步骤 2) 必须在你每次上传新版本的应用程序时执行。
|
||||
|
||||
如果你看到编译错误,请修复源代码并重新运行 appcfg.py;在编译成功之前,它不会启动(或更新)你的应用程序。
|
||||
|
||||
在云端测试它:http://`application-id`.appspot.com
|
||||
|
||||
使用你自己独特的应用程序 ID (`application-id`),在我们的例子中是 http://ib-tutgae.appspot.com
|
||||
|
||||
这也可以在 Windows 平台的浏览器中使用,而不仅仅是在 Linux 或 OS X 上。
|
||||
|
||||
<u>监控你的应用程序:</u>
|
||||
|
||||
再次访问 https://appengine.google.com/,现在将显示一个你的应用程序的列表。点击你的应用程序的链接将显示其控制面板 (*Control Panel*),用于监控你的应用程序。
|
||||
|
||||

|
||||
|
||||
<center><u>Fig 20.1</u>:The Application Control Panel</center>
|
||||
|
||||
这非常重要,因为你的应用程序在云中运行,而这是你访问它的唯一途径(除了用 app\_cfg 上传新的版本)!当你的应用程序在云中运行时,你不能对它进行或调试。当你的代码在云中运行时,你不能自己对它进行剖析 (profile) 或调试。有一个图像显示你的应用程序的负载(每秒钟的请求量),它消耗了多少资源(CPU 使用量、带宽、存储、复制的数据、后端使用量)以及如何计费。还有一个负载视图:每个 URL 模式的请求数和 CPU 负载,以及非常重要的一个错误视图:关于你的应用程序中发生的错误的摘要信息。数据面板,特别是数据存储查看器,可以让你可视化和查询你的存储数据。此外,还有用于管理的特定视图和 GAE 文档的链接。Main/Logs 让您可以访问应用程序的日志,每个请求和错误/异常都会被记录下来(异常不会显示给用户)。
|
||||
|
||||
## 链接
|
||||
|
||||
- [目录](directory.md)
|
||||
- 上一节:[使用数据存储](20.7.md)
|
||||
- 下一节:[21.0](真实世界中 Go 的使用)
|
||||
|
7
eBook/21.0.md
Normal file
7
eBook/21.0.md
Normal file
@@ -0,0 +1,7 @@
|
||||
# 21.0 真实世界中 Go 的使用
|
||||
|
||||
在接下来的章节中,我们将讨论一些 Go 的实际使用案例:我们将探索一些如今在商业领域中使用的 Go 应用程序,并指出为什么选择在这些领域中使用 Go 。考虑到这一语言仅发行了 2 年时间,而使用新语言构建初始项目的老牌企业通常不希望让公众知道这一点,这是非常令人印象深刻的。
|
||||
|
||||
- [目录](directory.md)
|
||||
- 上一节:[上传到云端](20.8.md)
|
||||
- 下一节:[Heroku:一个使用 Go 的高度可用一致数据存储](21.1.md)
|
54
eBook/21.1.md
Normal file
54
eBook/21.1.md
Normal file
@@ -0,0 +1,54 @@
|
||||
# 21.1 Heroku:一个使用 Go 的高度可用一致数据存储
|
||||
|
||||
http://www.heroku.com/(引用 39)
|
||||
|
||||
Heroku 是一家位于美国旧金山的硅谷公司,最近被 Salesforce.com 公司(它为 Ruby 和 Rails、Java、Clojure 和 node.js 应用程序提供强大的、可扩展的、特别是非常易于管理的云主机)收购。Heroku 的两位工程师,Keith Rarick 和 Blake Mizerany 设计了一个开源的 "分布式启动系统",名为 **Doozer**,用于管理跨集群机器的进程,并从实例故障和网络故障中优雅地恢复分区。其中一个需求是,他们需要可靠地同步和在许多服务器之间共享信息。
|
||||
|
||||
系统中的每台服务器都需要有很多关于系统整体的信息(配置数据、锁 (lock) 等),以便能够进行协调,而且这些信息需要保持一致,即使在数据存储失败时也可以使用。因此他们需要一个具有坚实一致性保证的数据存储。为此,他们开发了 Doozer,一个用 Go 语言编写的、新的、一致的、高度可用的数据存储,并且仿照谷歌的(封闭源码)Chubby 程序来管理他们的后端基础设施。
|
||||
|
||||
Doozer 以 Paxos 为基础(Paxos 是一个由不可靠节点组成的的不可靠网络中解决共识问题的协议族),虽然 Paxos 对运行容错系统至关重要,但它因难以实现而臭名昭著。即使是在网上可以找到的实例实现也很复杂,很难遵循,尽管已经为了教育目的而被简化过。而现有的生产系统以更糟糕而闻名。
|
||||
|
||||
Doozer 是作为建立分布式系统的一个坚硬的基础而被开发的。
|
||||
|
||||
- 一个高度可用的(在网络分区期间工作)。
|
||||
- 一致性(没有不一致的写入)。
|
||||
- 数据存储(用于少量的数据)。
|
||||
|
||||
正如开发人员所说:
|
||||
|
||||
> "Doozer 是你放置家族珠宝的地方"。
|
||||
|
||||
它提供了一个单一的基本同步元素:比较-设置对 (compare-config)。
|
||||
|
||||
用例:
|
||||
- 数据库主选 (Databases master election)
|
||||
- 命名服务
|
||||
- 配置
|
||||
|
||||
<u>为什么选择 Go,Go 的特点如何使其成为一个成功的产品:</u>
|
||||
|
||||
Paxos 是以独立的、并发的进程来定义的,这些进程通过传递消息进行通信。这正是 *Go 的并发原语*(goroutines 和 channel,见 [第 14 章](14.0.md))所擅长的问题。在 Doozer 中,这些进程被实现为 *goroutines*,他们的通信被实现为*通道操作*。就像 Go 的*垃圾收集器*将内存使用量降到最低一样,Doozer 的开发者发现 goroutines 和通道改进了基于锁的并发方法。这些工具让他们避免了复杂的“记账” (bookkeeping) 方式,并将注意力集中在手头的问题上。他们仍然惊讶于只用了几行代码就实现了以困难著称的东西。
|
||||
|
||||
Go 中的*标准包*是对于 Doozer 的另一个大成功,其中最值得一提的是 `websocket` 包。
|
||||
|
||||
下面是开发人员自己的一些使用后的感受:
|
||||
|
||||
> “……例如,我们很快就发现 `websocket` 是一个有用的包。一旦我们有了一个工作的数据存储,我们就需要一个简单的方法来反省 (introspect) 它并将活动可视化。使用 `websocket` 包,Keith 能够在回家的火车上添加 web 浏览器,而且不需要外部依赖。这就是 Go 将系统和应用编程完美结合的一个真实证明。
|
||||
>
|
||||
> “部署 Doozer 的过程简单得令人满意。Go 构建静态链接的二进制文件,这意味着 Doozer 没有外部依赖;它是一个单一的文件,可以复制到任何机器上,并立即启动,加入运行中的 Doozer 集群。
|
||||
>
|
||||
> “最后,Go 对简单性和正交性 (orthogonality) 的狂热关注与我们对软件工程的看法是一致的。和 Go 团队一样,我们对 Doozer 的特性也是固执的 (pragmatic)。我们关注细节,更倾向于改变现有的功能而不是引入新的功能。在这个意义上,Go 是一个与 Doozer 的完美匹配。我们已经有了关于 Go的未来项目。Doozer 只是一个更大的系统的开始。”
|
||||
|
||||
他们还喜欢自动格式化工具 *gofmt*,以实现一致的代码风格和布局,从而避免了对这些话题的讨论。
|
||||
|
||||
其他语言也提供了一些类似的并发机制——比如 Erlang 和 Scala,但 Go 的设计也是为了提供最大的效率和控制。在另一篇文章中([引用 12]())Keith Rarick 指出:
|
||||
|
||||
> “Go 来自于 C 和 C++ 这样的系统编程语言,所以它让你有能力真正控制性能特性。当需要测量事物并确保其运行速度的时候,你有足够的灵活性来真正进入那里并做你需要的事情。当你发现你的程序运行缓慢的原因时,你就可以真正控制你所需要的东西来解决它。Go 给了你一个独特的组合:C 语言给了你控制权,但它并不适合于并发。它甚至没有给你提供垃圾收集。Go 为你提供了并发性和垃圾收集,但它仍然让你控制内存布局和资源使用。”
|
||||
|
||||
在 Doozer 中,Go 主要作为一种系统编程语言使用。更多的技术描述可以在([引用 38]())找到;代码可在 https://github.com/ha/doozer 找到。
|
||||
|
||||
## 链接
|
||||
|
||||
- [目录](directory.md)
|
||||
- 上一节:[真实世界中 Go 的使用](21.0.md)
|
||||
- 下一节:[MROffice:一个使用 Go 的呼叫中心网络电话 (VOIP) 系统](21.2.md)
|
30
eBook/21.2.md
Normal file
30
eBook/21.2.md
Normal file
@@ -0,0 +1,30 @@
|
||||
# 21.2 MROffice:一个使用 Go 的呼叫中心网络电话 (VOIP) 系统
|
||||
|
||||
http://mroffice.org/
|
||||
|
||||
这个例子表明,Go 也适用于简单、可靠的应用程序编程。
|
||||
|
||||
MROffice 是一家位于新西兰的公司,专门从事市场调查软件。他们在 Freeswitch 的基础上使用 Go 为市场调查的呼叫中心建立了一个电话解决方案。Kees Varekamp 是有市场研究软件的背景的一位开发人员,他发现该领域的大多数现有软件都很糟糕,于是在 2010 年推出了 MROffice,为市场研究行业提供更好的软件。
|
||||
|
||||
他的旗舰产品名为 [Dialer](http://mroffice.org/telephony.html)。
|
||||
|
||||
Dialer 主要做什么?
|
||||
|
||||
- 它把呼叫中心的面试官和受访者联系起来。
|
||||
- 它在采访平台(提供脚本和收集统计数据)和 VoIP 拨号器(进行实际的电话通话)之间提供一座桥梁。
|
||||
|
||||
<u>为什么是 Go?</u>
|
||||
|
||||
Dialer 的第一个版本是用 Python 写的,但他的经验是,Python 作为一种动态脚本语言,对于长期运行的服务器进程来说,也许不是一个好的选择:发生了很多运行时的错误,而这些错误本可以在编译时被发现。
|
||||
|
||||
正如 Varekamp 先生在悉尼 Go 用户组(2011 年 3 月)所说:
|
||||
|
||||
> “当 Go 出现的时候,我立刻就理解到了 (made sense to me):类型安全,已编译,感觉像一种脚本语言。”
|
||||
|
||||
所以他把 Python 代码移植到 Go 上。*Go 的并发模型*适合这个问题:一个 goroutine 被启动来处理每个呼叫、面试者和被面试者,他们都通过通道来进行通信。`http` 和 `websocket` 库使得编写一个用户管理界面变得容易。
|
||||
|
||||
该产品现在已经在多个呼叫中心运行,并且正在进行使用神经网络的预测拨号器设计。
|
||||
|
||||
- [目录](directory.md)
|
||||
- 上一节:[Heroku:一个使用 Go 的高度可用一致数据存储](21.1.md)
|
||||
- 下一节:[Atlassian:一个虚拟机群管理系统](21.3.md)
|
22
eBook/21.3.md
Normal file
22
eBook/21.3.md
Normal file
@@ -0,0 +1,22 @@
|
||||
# 21.3 Atlassian:一个虚拟机群管理系统
|
||||
|
||||
http://www.atlassian.com/
|
||||
|
||||
在 Atlassian,Go 被用于支持并发的实用程序设计,事实上是用于配置和监控测试服务器。他们为软件开发人员制作开发和协作工具(主要是一个 Java 商店)。他们有一个由虚拟机 (VM) 组成的测试集群,在大量无硬盘主机上运行。它的供应和监控系统是用 Go 编写的;该系统由 3 个部分组成:
|
||||
- 在每个服务器上运行的代理进程,广播其虚拟机的状态。
|
||||
- 一个管理程序,听取代理的广播,并在一个虚拟机没有报告时采取行动。
|
||||
- 一个命令行工具,用于向管理器发布命令。
|
||||
|
||||
代理使用协议缓冲区来编码它所读取的状态信息,并通过 UDP 广播这些信息。管理器读取配置文件并为集群中的每个虚拟机启动一个 goroutine。每个 goroutine 监听来自其相应的虚拟机的公告,并发出指令(shell 命令),使其处于正确的状态。
|
||||
|
||||
<u>为什么 Go 在这里起作用:</u>每个虚拟机的一个 goroutine 很好地映射到它们的配置。
|
||||
|
||||
这个系统也*很容易部署*,因为他们可以运送没有依赖性的二进制文件。
|
||||
|
||||
正如 Atlassian 工程师 Dave Cheney 所说:
|
||||
|
||||
> “代理程序运行在联网启动的机器上,并且完全从 RAM 中运行。与 JVM 或 Python 的运行时相比,单一的静态二进制文件是一个很大的节省。”
|
||||
|
||||
- [目录](directory.md)
|
||||
- 上一节:[MROffice:一个使用 Go 的呼叫中心网络电话 (VOIP) 系统](21.2.md)
|
||||
- 下一节:[Camilistore:一个可寻址内容存储系统](21.4.md)
|
22
eBook/21.4.md
Normal file
22
eBook/21.4.md
Normal file
@@ -0,0 +1,22 @@
|
||||
# 21.4 Camilistore:一个可寻址内容存储系统
|
||||
|
||||
http://camlistore.org/
|
||||
|
||||
在 Camlistore 中,从数据存储到用户界面,“全栈”编程都在 Go 中进行。该系统由 Brad Fitzpatrick 开发,是一个在云上存储个人数据并与朋友和公众分享这些数据的系统。它由一个内容可寻址的数据存储、一个同步器和访问控制机制、一个 API、一个用户界面以及一个个人“web 主目录”组成。
|
||||
|
||||
它是一个语言无关的 (language-agnostic) 项目,但其最主要的部分是用 Go 编写的。它们包括一个 blob 数据服务器、一个 http 服务器、一个 http 用户界面以及一些命令行工具。
|
||||
|
||||
它可以用于:
|
||||
|
||||
- 自动同步远程服务器的个人备份。
|
||||
- 在机器间进行 Dropbox 式的文件同步。
|
||||
- 照片管理和共享。
|
||||
- 网站内容管理。
|
||||
|
||||
以下是 Brad 对这个 Go 项目的一些评论:
|
||||
|
||||
> “我在非常少的时间内,不需要太多的代码就能迸发出 (bust out) 很多快速、正确、可维护的可测试代码,我已经很久没有对一种语言如此兴奋了。我很早就有了 Camlistore 的想法,但在我学习Go之前,它总是显得太痛苦了。”
|
||||
|
||||
- [目录](directory.md)
|
||||
- 上一节:[Atlassian:一个虚拟机群管理系统](21.3.md)
|
||||
- 下一节:[Go 语言的其他应用](21.5.md)
|
79
eBook/21.5.md
Normal file
79
eBook/21.5.md
Normal file
@@ -0,0 +1,79 @@
|
||||
# 21.5 Go 语言的其他应用
|
||||
|
||||
在前面的章节中,我们只讨论了 Go 在商业环境中已经使用的许多地方中的几个。其他一些使用 Go 的机构有:
|
||||
|
||||
1. [Canonical-Ubuntu 公司](http://www.canonical.com/):使用 Go 开发后台基础设施,主要开发者为 Gustavo Niemeyer。例如项目 Ensemble(见 [参考文献 30]())。
|
||||
|
||||
2. [FeedBooks](http://www.feedbooks.com/):用 Go 发布电子书。
|
||||
|
||||
FeedBooks 是一个电子书的发行商,它使用 Go 和 mgo 每天为超过一百万的图书出版提供服务。这是 Feedbooks 的研发工程师 Benoît Larroque 的一条评论:
|
||||
|
||||
> “mgo(一个与 MongoDB 交流的 Go 库)使我们能够每天为超过 100 万本图书出版提供服务,同时也降低我们的服务器负载。”
|
||||
|
||||
3. [Anchor-Orchestra](http://www.anchor.com.au/):一个使用 Go 的分布式执行框架。这家公司的特点是高水平的服务器支持、配置应用程序设置、缓存和解决可扩展性问题。他们还可以与其他网站托管公司合作,专业地设置负载平衡、数据库集群和虚拟环境。
|
||||
|
||||
为此,他们使用 Go 开发并使用 Orchestra 分布式执行框架。
|
||||
|
||||
(更多信息:http://www.anchor.com.au/blog/2011/08/the-automation-waltz/)
|
||||
|
||||
4. [开放知识基金会](http://eris.okfn.org/ww/2011/03/gockan)。
|
||||
|
||||
这个组织使用 Go 进行(元)数据目录的聚合和数据链接。所有现有的软件都是用 Python 写的,所以开发者可以对两者进行比较。他们的结论是:
|
||||
|
||||
- Go *很简单*。一旦通过了最初浅显的学习弯道,它就会像 Python 一样方便、舒适地运行。唯一的缺点是没有像 Python 那样多的库。
|
||||
- Go 是一种*静态类型的语言*。这似乎是一个深奥的细节,但它有一些重要的影响。在 Python 中的许多编程涉及到大量的单元和功能测试,这可能是一个相当大的负担,尽管 CKAN 测试套件有了一些重大的改进,但需要相当长的时间来运行。然而你很快就会发现,许多测试基本上是在测试动态类型 (duck typing) 和可变实体 (variable existence)(例如,当你在重构中重命名一个变量时,不确定你是否正确地重命名了一切)。在像 Go 这样的语言中,这些东西都是由编译器捕获的,不需要单独的测试。这意味着*你可以少写一些测试,因为编译器本身就是一个相当强大的测试套件。*
|
||||
- 尽管它是一种编译语言,但编译过程非常快,*写-编译-测试 的循环并不比 Python 中的 写-测试 循环慢*,因为需要运行的测试较少,如同上文所说,这个循环被进一步压缩了。
|
||||
- Go *远比 Python 更节省内存*……差别是惊人的。
|
||||
- 与 Python 相比,Go 作为一种经过编译和类型检查的语言,它的*速度很快*。
|
||||
- Go 不是面向对象的,至少与 Python 的意义不同。相反,它有一个接口的概念。这使得*设计更加简洁*,因为它不鼓励复杂的多重继承类的层次结构……接口就感觉更干净一些。
|
||||
- Go 有*内置的并发性*。在这项工作中,有很多并行的机会,这是好的。
|
||||
|
||||
5. [Tinkercad 公司](http://tinkercad.com/):这家由 Kai Backman 创办的芬兰公司正在设计用于在浏览器/云端进行 3D 实体建模和打印的软件,其在客户端使用 WebGL 进行渲染。观看 [视频](http://www.youtube.com/watch?v=5aY4a9QnLhw) 了解关于这个主题的技术讲座。这是 Kai 的一句评价:
|
||||
|
||||
> “目前(2011 年)Go 可能是编写并发服务器的最佳语言。”
|
||||
|
||||
6. [Clarity Services Inc.](http://www.clarityservices.com):该公司是一家实时的信用机构,其使用 Go 语言对信用申请进行基于事件的后期处理。
|
||||
|
||||
7. [Cablenet 通信系统有限公司](http://www.cablenet.com.cy/en/):这家塞浦路斯的 cablenet 供应商用 Go 开发了一个内部供应系统。
|
||||
|
||||
8. [Tonika](http://pdos.csail.mit.edu/~petar/5ttt.org/):是一个用 Go 开发的、开源安全网络社交平台。
|
||||
|
||||
9. [Medline](http://eris.okfn.org/ww/2011/05/medline/):使用 Go 的 XML 解析器来将Medline(医学期刊的数据)的压缩 XML 文件转化到 RDF。
|
||||
|
||||
10. [Iron.io](www.iron.io):构建云基础设施软件。
|
||||
|
||||
它用 Go 开发的第一个产品是 SimpleWorker,一个大规模的后台处理和调度系统;他们也在使用 Go 进行其他服务。
|
||||
|
||||
11. [SmartTweets](http://www.facebook.com/apps/application.php?id=135488932982):一个用Go开发的 Facebook 应用程序。这个应用程序将你的 Twitter 状态更新转贴到你的 Facebook 主页上,并允许过滤转发、提及、标签、回复等内容。
|
||||
|
||||
该应用程序现在有超过 12 万名用户。
|
||||
|
||||
> “这是一种稳定的语言,”Michael Hoisie 说,“它可以处理负载。”
|
||||
|
||||
12. 在 [Sandia 国家实验室](http://www.sandia.gov/about/index.html),一个美国开发支持国家安全的基于科学的技术的政府机构,有很多曾经从事过编程的人都在使用这种语言。很多过去使用 C、C++、Perl、Python 或其他什么 HPC 管理软件的人,已经转而使用 Go,并且不打算回头了。
|
||||
|
||||
> Go 在效率、语言能力和编写代码的便利性之间找到了一个好的位置。
|
||||
>
|
||||
> <p style="text-align:right">—— Ron Minnich</p>
|
||||
|
||||
13. [Carbon Games](http://carbongames.com/):一家网络游戏公司,为他们的后台服务使用 Go。
|
||||
|
||||
14. [Vaba软件公司](http://vabasoftware.com/):用 Go 重写了他们的消息和存储引擎。
|
||||
|
||||
15. [Institute for Systems Biology](http://systemsbiology.org/):用 Go 开发了分布式计算分析系统 [*Golem*](http://code.google.com/p/golem/)。
|
||||
|
||||
16. [Second Bit](http://www.secondbit.org/):使用 Go 来驱动他们的 2cloud 服务。
|
||||
|
||||
17. [Numerotron Inc](http://www.stathat.com/):用 Go 开发了他们的统计和事件跟踪系统 *StatHat*。
|
||||
|
||||
最后是谷歌公司本身,它是 Go 的(发明者)之家。
|
||||
|
||||
Go 在谷歌内部的使用是相当保密的,但在 2010 年 5 月,Rob Pike 宣布 Google 的后端基础设施正在运行用 Go 构建的应用程序([参考文献 27]())。Go 被用于一些系统(网络服务器,也包括存储系统和数据库),这些系统在跨越谷歌全球数据中心网络的分布式基础设施中发挥着作用。Go 可能会在未来几年内成为谷歌的标准后端语言。Andrew Gerrand 还说,谷歌员工正在使用 Go 来简单地从服务器上抓取信息。
|
||||
|
||||
> “谷歌有管理应用程序和服务的人,他们需要编写工具来抓取几千台机器的状态并汇总数据,”他说,“以前,这些操作人员会用 Python 写这些东西,但他们发现 Go 在性能和实际写代码的时间方面要快得多。”
|
||||
|
||||
关于 Go 在企业中的使用情况,可以在 http://go-lang.cat-v.org/organizations-using-go 上找到一个全面的清单。
|
||||
|
||||
- [目录](directory.md)
|
||||
- 上一节:[Camilistore:一个可寻址内容存储系统](21.4.md)
|
||||
- 下一节:[附录](Appendices.md)
|
@@ -3,19 +3,19 @@
|
||||
|
||||
## 第一部分:学习 Go 语言
|
||||
|
||||
- 第1章:Go 语言的起源,发展与普及
|
||||
- 第 1 章:Go 语言的起源,发展与普及
|
||||
- 1.1 [起源与发展](01.1.md)
|
||||
- 1.2 [语言的主要特性与发展的环境和影响因素](01.2.md)
|
||||
- 第2章:安装与运行环境
|
||||
- 第 2 章:安装与运行环境
|
||||
- 2.1 [平台与架构](02.1.md)
|
||||
- 2.2 [Go 环境变量](02.2.md)
|
||||
- 2.3 [在 Linux 上安装 Go](02.3.md)
|
||||
- 2.4 [在 Mac OS X 上安装 Go](02.4.md)
|
||||
- 2.5 [在 Windows 上安装 Go](02.5.md)
|
||||
- 2.6 [安装目录清单](02.6.md)
|
||||
- 2.7 [Go 运行时(runtime)](02.7.md)
|
||||
- 2.7 [Go 运行时 (runtime)](02.7.md)
|
||||
- 2.8 [Go 解释器](02.8.md)
|
||||
- 第3章:[编辑器、集成开发环境与其它工具](03.0.md)
|
||||
- 第 3 章:[编辑器、集成开发环境与其它工具](03.0.md)
|
||||
- 3.1 [Go 开发环境的基本要求](03.1.md)
|
||||
- 3.2 [编辑器和集成开发环境](03.2.md)
|
||||
- 3.3 [调试器](03.3.md)
|
||||
@@ -28,7 +28,7 @@
|
||||
|
||||
## 第二部分:语言的核心结构与技术
|
||||
|
||||
- 第4章:基本结构和基本数据类型
|
||||
- 第 4 章:基本结构和基本数据类型
|
||||
- 4.1 [文件名、关键字与标识符](04.1.md)
|
||||
- 4.2 [Go 程序的基本结构和要素](04.2.md)
|
||||
- 4.3 [常量](04.3.md)
|
||||
@@ -38,14 +38,14 @@
|
||||
- 4.7 [strings 和 strconv 包](04.7.md)
|
||||
- 4.8 [时间和日期](04.8.md)
|
||||
- 4.9 [指针](04.9.md)
|
||||
- 第5章:[控制结构](05.0.md)
|
||||
- 第 5 章:[控制结构](05.0.md)
|
||||
- 5.1 [if-else 结构](05.1.md)
|
||||
- 5.2 [测试多返回值函数的错误](05.2.md)
|
||||
- 5.3 [switch 结构](05.3.md)
|
||||
- 5.4 [for 结构](05.4.md)
|
||||
- 5.5 [Break 与 continue](05.5.md)
|
||||
- 5.6 [标签与 goto](05.6.md)
|
||||
- 第6章:[函数(function)](06.0.md)
|
||||
- 第 6 章:[函数 (function)](06.0.md)
|
||||
- 6.1 [介绍](06.1.md)
|
||||
- 6.2 [函数参数与返回值](06.2.md)
|
||||
- 6.3 [传递变长参数](06.3.md)
|
||||
@@ -58,21 +58,21 @@
|
||||
- 6.10 [使用闭包调试](06.10.md)
|
||||
- 6.11 [计算函数执行时间](06.11.md)
|
||||
- 6.12 [通过内存缓存来提升性能](06.12.md)
|
||||
- 第7章:[数组与切片](07.0.md)
|
||||
- 第 7 章:[数组与切片](07.0.md)
|
||||
- 7.1 [声明和初始化](07.1.md)
|
||||
- 7.2 [切片](07.2.md)
|
||||
- 7.3 [For-range 结构](07.3.md)
|
||||
- 7.4 [切片重组(reslice)](07.4.md)
|
||||
- 7.4 [切片重组 (reslice)](07.4.md)
|
||||
- 7.5 [切片的复制与追加](07.5.md)
|
||||
- 7.6 [字符串、数组和切片的应用](07.6.md)
|
||||
- 第8章:[Map](08.0.md)
|
||||
- 第 8 章:[Map](08.0.md)
|
||||
- 8.1 [声明、初始化和 make](08.1.md)
|
||||
- 8.2 [测试键值对是否存在及删除元素](08.2.md)
|
||||
- 8.3 [for-range 的配套用法](08.3.md)
|
||||
- 8.4 [map 类型的切片](08.4.md)
|
||||
- 8.5 [map 的排序](08.5.md)
|
||||
- 8.6 [将 map 的键值对调](08.6.md)
|
||||
- 第9章:[包(package)](09.0.md)
|
||||
- 第 9 章:[包 (package)](09.0.md)
|
||||
- 9.1 [标准库概述](09.1.md)
|
||||
- 9.2 [regexp 包](09.2.md)
|
||||
- 9.3 [锁和 sync 包](09.3.md)
|
||||
@@ -84,7 +84,7 @@
|
||||
- 9.9 [通过 Git 打包和安装](09.9.md)
|
||||
- 9.10 [Go 的外部包和项目](09.10.md)
|
||||
- 9.11 [在 Go 程序中使用外部库](09.11.md)
|
||||
- 第10章:[结构(struct)与方法(method)](10.0.md)
|
||||
- 第 10 章:[结构 (struct) 与方法 (method)](10.0.md)
|
||||
- 10.1 [结构体定义](10.1.md)
|
||||
- 10.2 [使用工厂方法创建结构体实例](10.2.md)
|
||||
- 10.3 [使用自定义包中的结构体](10.3.md)
|
||||
@@ -93,7 +93,7 @@
|
||||
- 10.6 [方法](10.6.md)
|
||||
- 10.7 [类型的 String() 方法和格式化描述符](10.7.md)
|
||||
- 10.8 [垃圾回收和 SetFinalizer](10.8.md)
|
||||
- 第11章:[接口(interface)与反射(reflection)](11.0.md)
|
||||
- 第 11 章:[接口 (interface) 与反射 (reflection)](11.0.md)
|
||||
- 11.1 [接口是什么](11.1.md)
|
||||
- 11.2 [接口嵌套接口](11.2.md)
|
||||
- 11.3 [类型断言:如何检测和转换接口变量的类型](11.3.md)
|
||||
@@ -111,7 +111,7 @@
|
||||
|
||||
## 第三部分:Go 高级编程
|
||||
|
||||
- 第12章:[读写数据](12.0.md)
|
||||
- 第 12 章:[读写数据](12.0.md)
|
||||
- 12.1 [读取用户的输入](12.1.md)
|
||||
- 12.2 [文件读写](12.2.md)
|
||||
- 12.3 [文件拷贝](12.3.md)
|
||||
@@ -124,10 +124,10 @@
|
||||
- 12.10 [XML 数据格式](12.10.md)
|
||||
- 12.11 [用 Gob 传输数据](12.11.md)
|
||||
- 12.12 [Go 中的密码学](12.12.md)
|
||||
- 第13章:[错误处理与测试](13.0.md)
|
||||
- 第 13 章:[错误处理与测试](13.0.md)
|
||||
- 13.1 [错误处理](13.1.md)
|
||||
- 13.2 [运行时异常和 panic](13.2.md)
|
||||
- 13.3 [从 panic 中恢复(Recover)](13.3.md)
|
||||
- 13.3 [从 panic 中恢复 (recover)](13.3.md)
|
||||
- 13.4 [自定义包中的错误处理和 panicking](13.4.md)
|
||||
- 13.5 [一种用闭包处理错误的模式](13.5.md)
|
||||
- 13.6 [启动外部命令和程序](13.6.md)
|
||||
@@ -135,13 +135,13 @@
|
||||
- 13.8 [测试的具体例子](13.8.md)
|
||||
- 13.9 [用(测试数据)表驱动测试](13.9.md)
|
||||
- 13.10 [性能调试:分析并优化 Go 程序](13.10.md)
|
||||
- 第14章:[协程(goroutine)与通道(channel)](14.0.md)
|
||||
- 第 14 章:[协程 (goroutine) 与通道 (channel)](14.0.md)
|
||||
- 14.1 [并发、并行和协程](14.1.md)
|
||||
- 14.2 [协程间的信道](14.2.md)
|
||||
- 14.3 [协程的同步:关闭通道-测试阻塞的通道](14.3.md)
|
||||
- 14.4 [使用 select 切换协程](14.4.md)
|
||||
- 14.5 [通道、超时和计时器(Ticker)](14.5.md)
|
||||
- 14.6 [协程和恢复(recover)](14.6.md)
|
||||
- 14.5 [通道、超时和计时器 (Ticker)](14.5.md)
|
||||
- 14.6 [协程和恢复 (recover)](14.6.md)
|
||||
- 14.7 [新旧模型对比:任务和worker](14.7.md)
|
||||
- 14.8 [惰性生成器的实现](14.8.md)
|
||||
- 14.9 [实现 Futures 模式](14.9.md)
|
||||
@@ -153,7 +153,7 @@
|
||||
- 14.15 [漏桶算法](14.15.md)
|
||||
- 14.16 [对Go协程进行基准测试](14.16.md)
|
||||
- 14.17 [使用通道并发访问对象](14.17.md)
|
||||
- 第15章:[网络、模板与网页应用](15.0.md)
|
||||
- 第 15 章:[网络、模板与网页应用](15.0.md)
|
||||
- 15.1 [tcp 服务器](15.1.md)
|
||||
- 15.2 [一个简单的 web 服务器](15.2.md)
|
||||
- 15.3 [访问并读取页面数据](15.3.md)
|
||||
@@ -169,7 +169,7 @@
|
||||
|
||||
## 第四部分:实际应用
|
||||
|
||||
- 第16章:[常见的陷阱与错误](16.0.md)
|
||||
- 第 16 章:[常见的陷阱与错误](16.0.md)
|
||||
- 16.1 [误用短声明导致变量覆盖](16.1.md)
|
||||
- 16.2 [误用字符串](16.2.md)
|
||||
- 16.3 [发生错误时使用 defer 关闭一个文件](16.3.md)
|
||||
@@ -180,12 +180,12 @@
|
||||
- 16.8 [误用协程和通道](16.8.md)
|
||||
- 16.9 [闭包和协程的使用](16.9.md)
|
||||
- 16.10 [糟糕的错误处理](16.10.md)
|
||||
- 第17章:[模式](17.0.md)
|
||||
- 第 17 章:[模式](17.0.md)
|
||||
- 17.1 [逗号 ok 模式](17.1.md)
|
||||
- 17.2 [defer 模式](17.2.md)
|
||||
- 17.3 [可见性模式](17.3.md)
|
||||
- 17.4 [运算符模式和接口](17.4.md)
|
||||
- 第18章:[出于性能考虑的实用代码片段](18.0.md)
|
||||
- 第 18 章:[出于性能考虑的实用代码片段](18.0.md)
|
||||
- 18.1 [字符串](18.1.md)
|
||||
- 18.2 [数组和切片](18.2.md)
|
||||
- 18.3 [映射](18.3.md)
|
||||
@@ -193,11 +193,11 @@
|
||||
- 18.5 [接口](18.5.md)
|
||||
- 18.6 [函数](18.6.md)
|
||||
- 18.7 [文件](18.7.md)
|
||||
- 18.8 [协程(goroutine)与通道(channel)](18.8.md)
|
||||
- 18.8 [协程 (goroutine) 与通道 (channel)](18.8.md)
|
||||
- 18.9 [网络和网页应用](18.9.md)
|
||||
- 18.10 [其他](18.10.md)
|
||||
- 18.11 [出于性能考虑的最佳实践和建议](18.11.md)
|
||||
- 第19章:[构建一个完整的应用程序](19.0.md)
|
||||
- 第 19 章:[构建一个完整的应用程序](19.0.md)
|
||||
- 19.1 [简介](19.1.md)
|
||||
- 19.2 [短网址项目简介](19.2.md)
|
||||
- 19.3 [数据结构](19.3.md)
|
||||
@@ -208,8 +208,21 @@
|
||||
- 19.8 [多服务器处理架构](19.8.md)
|
||||
- 19.9 [使用代理缓存](19.9.md)
|
||||
- 19.10 [总结和增强](19.10.md)
|
||||
- 第20章:Go 语言在 Google App Engine 的使用
|
||||
- 第21章:实际部署案例
|
||||
- 第 20 章:[Go 语言在 Google App Engine 的使用](20.0.md)
|
||||
- 20.1 [什么是 Google App Engine?](20.1.md)
|
||||
- 20.2 [云上的 Go](20.2.md)
|
||||
- 20.3 [安装 Go App Engine SDK:为 Go 部署的开发环境](20.3.md)
|
||||
- 20.4 [建造你自己的 Hello world 应用](20.4.md)
|
||||
- 20.5 [使用用户服务和探索其 API](20.5.md)
|
||||
- 20.6 [处理窗口](20.6.md)
|
||||
- 20.7 [使用数据存储](20.7.md)
|
||||
- 20.8 [上传到云端](20.8.md)
|
||||
- 第 21 章:[真实世界中 Go 的使用](21.0.md)
|
||||
- 21.1 [Heroku:一个使用 Go 的高度可用一致数据存储](21.1.md)
|
||||
- 21.2 [MROffice:一个使用 Go 的呼叫中心网络电话 (VOIP) 系统](21.2.md)
|
||||
- 21.3 [Atlassian:一个虚拟机群管理系统](21.3.md)
|
||||
- 21.4 [Camilistore:一个可寻址内容存储系统](21.4.md)
|
||||
- 21.5 [Go 语言的其他应用](21.5.md)
|
||||
|
||||
## 附录
|
||||
|
||||
|
Reference in New Issue
Block a user