Go + App Engine =

Johan Euphrosine
2012年3月15日

proppy-go-ae.appspot.com

关于我

proppy-go-ae.appspot.com

App Engine

App Engine 允许用户在 Google 的基础设施之上构建自己的 web 应用

proppy-go-ae.appspot.com

Go Runtime

proppy-go-ae.appspot.com

Hello Gopher!

package gopher

import (
    "fmt"
    "net/http"
)

// init 函数将在应用启动时调用。
func init() {
    // 为 /hello 这个 URL 注册接管函数。
    http.HandleFunc("/hello", hello)
}

// hello 是 HTTP 接管函数,将输出字符串“Hello Gopher!”
func hello(w http.ResponseWriter, r *http.Request) {
    fmt.Fprint(w, "Hello, Gopher!")
}
proppy-go-ae.appspot.com

Hello App Engine!

application: gopher
version: 1
runtime: go
api_version: go1beta

handlers:
- url: /images
  static_dir: images
- url: /doc
  static_dir: doc
- url: /.*
  script: _go_app
$ dev_appserver.py myapp/
$ appcfg.py update myapp/
proppy-go-ae.appspot.com

演示

Hello Gopher!

proppy-go-ae.appspot.com

Hello hackernews! (XML 解析)

package gopher

import (
    "appengine"
    "appengine/urlfetch"
    "encoding/xml"
    "fmt"
    "net/http"
    "time"
)

func init() {
    // 为 /hackernews 这个 URL 注册接管函数。
    http.HandleFunc("/hackernews", hackernews)
}
proppy-go-ae.appspot.com

将 XML 映射为 Go 数据类型

<rss version="2.0">
  <channel>
    <item>
      <title />
      <link />
...
type HNFeed struct {
    Data HNFeedData `xml:"channel"`
}

type HNFeedData struct {
    Items []Item `xml:"item"`
}

type Item struct {
    Title string `xml:"title"`
    Url   string `xml:"link"`
}
proppy-go-ae.appspot.com

解码

func hackernewsItems(c appengine.Context) []Item {
    client := urlfetch.Client(c)
    resp, err := client.Get(HNFeedUrl)
    if err != nil {
        c.Errorf("装取 %s 出错: %s", HNFeedUrl, err.Error())
        return []Item{}
    }
    defer resp.Body.Close()
    decoder := xml.NewDecoder(resp.Body)
    var feed *HNFeed
    if err = decoder.Decode(&feed); err != nil {
        c.Errorf("解码 HNFeed 出错: %s", err.Error())
        return []Item{}
    }
    return feed.Data.Items
}
proppy-go-ae.appspot.com

并输出

func hackernews(w http.ResponseWriter, r *http.Request) {
    c := appengine.NewContext(r)
    w.Header().Add("Content-Type", "text/plain")
    for _, item := range hackernewsItems(c) {
        fmt.Fprintf(w, "%s: %s\n", item.Title, item.Url)
    }
}
proppy-go-ae.appspot.com

演示

Hello Hacker News!

proppy-go-ae.appspot.com

Hello proggit! (JSON 解析)

package gopher

import (
    "appengine"
    "appengine/urlfetch"
    "encoding/json"
    "fmt"
    "net/http"
    "time"
)

func init() {
    // 为 /proggit 这个 URL 注册接管函数。
    http.HandleFunc("/proggit", proggit)
}
proppy-go-ae.appspot.com

将 Json 映射为 Go 数据类型

{"data": { "children": [
   {"data": { "title": ... , "url": ...}}, ...
type RedditFeed struct {
    Data RedditFeedData
}

type RedditFeedData struct {
    Items []RedditFeedItem `json:"children"`
}

type RedditFeedItem struct {
    Data Item
}
type Item struct {
    Title string `xml:"title"`
    Url   string `xml:"link"`
}
proppy-go-ae.appspot.com

解码

func proggitItems(c appengine.Context) []Item {
    client := urlfetch.Client(c)
    resp, err := client.Get(ProggitFeedUrl)
    if err != nil {
        c.Errorf("装取 %s 出错: %s", ProggitFeedUrl, err.Error())
        return []Item{}
    }
    defer resp.Body.Close()
    decoder := json.NewDecoder(resp.Body)
    var feed *RedditFeed
    if err = decoder.Decode(&feed); err != nil {
        c.Errorf("解码 RedditFeed 出错: %s", err.Error())
        return []Item{}
    }
    items := make([]Item, len(feed.Data.Items))
    for i, item := range feed.Data.Items {
        items[i] = item.Data
    }
    return items
}
proppy-go-ae.appspot.com

并输出

func proggit(w http.ResponseWriter, r *http.Request) {
    c := appengine.NewContext(r)
    w.Header().Add("Content-Type", "text/plain")
    for _, item := range proggitItems(c) {
        fmt.Fprintf(w, "%s: %s\n", item.Title, item.Url)
    }
}
proppy-go-ae.appspot.com

演示

Hello Proggit!

proppy-go-ae.appspot.com

Progginator! (整合)

func progginator(w http.ResponseWriter, r *http.Request) {
    c := appengine.NewContext(r)
    hnItems := hackernewsItems(c)
    pgItems := proggitItems(c)
    w.Header().Add("Content-Type", "text/plain")
    for _, item := range hnItems {
        fmt.Fprintf(w, "%s: %s\n", item.Title, item.Url)
    }
    for _, item := range pgItems {
        fmt.Fprintf(w, "%s: %s\n", item.Title, item.Url)
    }
}
proppy-go-ae.appspot.com

演示

Progginator! proppy-go-ae.appspot.com

命令行输出:

2012-03-15 04:17:59.148 hackernews: 261.695ms
2012-03-15 04:17:59.246 proggit: 97.961ms
2012-03-15 04:17:59.246 progginator: 359.822ms

并行处理

func progginator_(w http.ResponseWriter, r *http.Request) {
    c := appengine.NewContext(r)
    results := make(chan []Item)
    go func() {
        results <- hackernewsItems(c)
    }()
    go func() {
        results <- proggitItems(c)
    }()
    w.Header().Add("Content-Type", "text/plain")
    for i := 0; i < 2; i++ {
        items := <-results
        for _, item := range items {
            fmt.Fprintf(w, "%s: %s\n", item.Title, item.Url)
        }
    }
}
proppy-go-ae.appspot.com

演示

Progginator!

命令行输出:

2012-03-15 04:18:03.804 proggit: 110.451ms
2012-03-15 04:18:03.887 hackernews: 193.751ms
2012-03-15 04:18:03.887 progginator_: 193.824ms
proppy-go-ae.appspot.com

哇!

proppy-go-ae.appspot.com

课后作业

proppy-go-ae.appspot.com

谢谢观看!

	 := "code.google.com/appengine/docs/go/"
	 := "golang.org"
	 := "profiles.google.com/proppy"
翻译:xslidian@guao.hk proppy-go-ae.appspot.com