使用Golang通道处理HTTP请求

时间:2014-09-08 09:28:19

标签: google-app-engine concurrency go

我正在尝试构建一个简单的Golang / Appengine应用程序,该应用程序使用通道来处理每个http请求。原因是我希望每个请求执行合理的大内存计算,并且以线程安全的方式执行每个请求非常重要(即并发请求的计算不会混合)。

基本上我需要一个同步队列,它一次只处理一个请求,并且通道看起来很自然。

Is it possible to use Go's buffered channel as a thread-safe queue?

然而,我无法让我的简单hello世界示例工作。它似乎在'go process(w,cr)'线上失败了;我从服务器得到200响应,但没有竞争对手。如果我从这一行消除'go',那么工作正常,但我猜我没有正确地调用频道。

有人指出我哪里出错了?

谢谢!

// curl -X POST "http://localhost:8080/add" -d "{\"A\":1, \"B\":2}"

package hello

import (
    "encoding/json"
    "net/http"  
)

type MyStruct struct {
    A, B, Total int64
}

func (s *MyStruct) add() {
    s.Total = s.A + s.B
}

func process(w http.ResponseWriter, cr chan *http.Request) {
    r := <- cr
    var s MyStruct
    json.NewDecoder(r.Body).Decode(&s)
    s.add()
    json.NewEncoder(w).Encode(s)
}

func handler(w http.ResponseWriter, r *http.Request) {  
    cr := make(chan *http.Request, 1)
    cr <- r
    go process(w, cr) // doesn't work; no response :-(
    // process(w, cr) // works, but blank response :-(
}

func init() {
    http.HandleFunc("/add", handler)
}

2 个答案:

答案 0 :(得分:3)

不确定这是否是正确的设计,但我怀疑问题在于你开始第二个例行程序,第一个例程继续并完成连接等。

要停止此操作,您可以使用等待组(http://golang.org/pkg/sync/#WaitGroup)使第一个例程等待。

这样就停止了为什么你要把它放到一个线程中的整个推理(因此我认为你有一个设计问题)。

以下是一些未经测试的代码,它们应该起作用或至少在正确的方向上提供帮助。

package main

import (
    "encoding/json"
    "net/http"
    "sync"  
)

type MyStruct struct {
    A, B, Total int64
}

func (s *MyStruct) add() {
    s.Total = s.A + s.B
}

func process(w http.ResponseWriter, cr chan *http.Request) {
    r := <- cr
    var s MyStruct
    json.NewDecoder(r.Body).Decode(&s)
    s.add()
    json.NewEncoder(w).Encode(s)
}

func handler(w http.ResponseWriter, r *http.Request) {  
    cr := make(chan *http.Request, 1)
    cr <- r
    var pleasewait sync.WaitGroup
    pleasewait.Add(1)

    go func() {
        defer pleasewait.Done()
        process(w, cr) // doesn't work; no response :-(
    }()
    // process(w, cr) // works, but blank response :-(

    pleasewait.Wait()
}

func main() {
    http.HandleFunc("/add", handler)
}

答案 1 :(得分:1)

如果大型计算不使用共享可变状态,则编写普通处理程序。不需要频道,也没有频道。

好的,大型计算确实使用共享可变状态。如果只有一个应用程序实例正在运行,则使用sync.Mutex来控制对可变状态的访问。与将工作改组为单个goroutine以一次处理一个计算相比,这很简单。

你在App Engine上运行吗?您可能无法保证应用程序的单个实例正在运行。您需要将数据存储区或内存缓存用于可变状态。如果计算可以脱机完成(请求完成后),那么您可以使用App Engine任务队列一次处理一个计算。

附注:标题提出了解决问题正文中所述问题的方法。最好直接说明问题。我会在上面对此作出评论,但我没有必要的果汁。