使用GAE/Go实现使用TaskQueue的API
在之前的文章中,我抱怨说在Google App Engine中无法创建一个只返回响应并在另一进程中执行繁重处理的API。但是后来有人告诉我,可以通过使用TaskQueue来实现这一点。于是我决定亲自尝试一下。
源代码在这里
克大猩猩/ GAE API示例
不使用TaskQueue进行实施(NG模式)
之前的实现大致是这样的。
使用这种方法时,当在goroutine内部尝试使用context时,会发生无效的安全凭证(上下文已过期)错误。
package main
import (
"fmt"
"net/http"
"encoding/json"
"google.golang.org/appengine"
"google.golang.org/appengine/log"
"google.golang.org/appengine/urlfetch"
"google.golang.org/appengine/taskqueue"
)
const url = "https://www.google.com/"
type Response struct {
Status string `json:"status"`
Message string `json:"message"`
}
func main() {
http.HandleFunc("/back", helloBackground)
appengine.Main()
}
// NGパターン
func helloBackground(w http.ResponseWriter, r *http.Request) {
// URLにリクエストして返ってきたステータスをログに残すgoroutine
go func() {
ctx := appengine.NewContext(r) // App Engineのcontext作成
client := urlfetch.Client(ctx) // contextを使ってhttp.Clientを作成
resp, err := client.Get(url)
if err != nil {
log.Errorf(ctx, "TEST: helloBackground: %s", err)
return
}
log.Infof(ctx, "'%s' returns %s", url, resp.Status)
}()
// レスポンス
json.NewEncoder(w).Encode(Response{
Status: "ok",
Message: "processing in background.",
})
}
用 TaskQueue 进行实现
使用TaskQueue修复它会变成这样。
package main
import (
"fmt"
"net/http"
"encoding/json"
"google.golang.org/appengine"
"google.golang.org/appengine/log"
"google.golang.org/appengine/urlfetch"
"google.golang.org/appengine/taskqueue"
)
const url = "https://www.google.com/"
type Response struct {
Status string `json:"status"`
Message string `json:"message"`
}
func main() {
http.HandleFunc("/back", helloBackground)
http.HandleFunc("/back/worker", helloBackgroundWorker)
appengine.Main()
}
// レスポンスだけ速攻で返して時間のかかる処理はTask Queueに渡すAPI(10分 or 24時間でタイムアウト)
func helloBackground(w http.ResponseWriter, r *http.Request) {
ctx := appengine.NewContext(r) // App Engineのcontext作成
// タスクをTaskQueueに投げる
t := taskqueue.NewPOSTTask("/back/worker", map[string][]string{})
if _, err := taskqueue.Add(ctx, t, ""); err != nil {
http.Error(w, "internal server error", http.StatusInternalServerError)
return
}
// レスポンス
json.NewEncoder(w).Encode(Response{
Status: "ok",
Message: "processing in background.",
})
}
// URLにリクエストして返ってきたステータスをログに残すworker
func helloBackgroundWorker(w http.ResponseWriter, r *http.Request) {
ctx := appengine.NewContext(r) // App Engineのcontext作成
client := urlfetch.Client(ctx) // contextを使ってhttp.Clientを作成
resp, err := client.Get(url)
if err != nil {
log.Errorf(ctx, "TEST: helloBackground: %s", err)
return
}
log.Infof(ctx, "'%s' returns %s", url, resp.Status)
return
}
当你在taskqueue.NewPOSTTask的第二个参数中传递一个map作为参数时,可以将参数传递给TaskQueue。
这个地方的做法可以参考这篇文章。
GAE/Go任务队列之一~推送队列~ – 轮次