在Golang的handler中如何启动goroutine
在Golang的handler中如何启动goroutine 我有一个HTTP处理程序。我希望启动一个Go协程,然后填充响应写入器并将控制权返回给浏览器,但同时保持Go协程的运行。我能够使用通道来保持Go协程运行,但页面无法传递给浏览器,之前的页面只是被挂起,处于等待状态。
func realTimeHandler(writer http.ResponseWriter, request *http.Request) {
fmt.Println("realTimeHandler")
log.Println("realTimeHandler")
p := data.DaemonPage{}
initializeHandling(&p, request)
initializeTrackSpecification(&p)
p.TrackSpecification.Stock.Symbol = getStringValue(&p, "stockSymbol")
p.RealTime = true
ch := make(chan bool)
go finnHubRealTime.Realtime(p.TrackSpecification.Stock.Symbol, ch)
RenderPage(writer, &p)
done := <-ch
log.Println(done)
}
如果没有通道锁,新页面会被渲染,但函数会退出,从而终止了 finnHubRealTime.Realtime 函数的执行。
更多关于在Golang的handler中如何启动goroutine的实战教程也可以访问 https://www.itying.com/category-94-b0.html
你确定问题不是main函数退出导致的吗?看看这个StackOverflow上的问题。我假设你阻塞了main函数,但以防万一:
Goroutine Termination on Calling Function Return
另外:
之前的页面只是挂起,在等待。
这让我怀疑finnHubRealTime.Realtime是否陷入了无限循环或类似的情况。如果你能展示那里的实现,可能会有所帮助。
更多关于在Golang的handler中如何启动goroutine的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
如果需要对金融数据进行持续实时的监控,使用 socket io 会更加合适。如果想将长时间运行的计算结果分片返回,可以返回分块响应。
从 Go 服务器发送分块 HTTP 响应
go
由 kimura 提问于 12:15AM - 06 Nov 14 UTC
我认为这不太优雅,但它能工作。在 main 包中,一个全局变量:
var realTimeGoRoutinesControllerChannel = make(chan *data.DaemonPage)
然后,在 main() 中
go func() {
for {
p := <-realTimeGoRoutinesControllerChannel
ch := make(chan bool)
go finnHubRealTime.Realtime(p.TrackSpecification.Stock.Symbol, ch)
}
}()
然后在处理程序中:
func realTimeHandler(writer http.ResponseWriter, request *http.Request) {
fmt.Println("realTimeHandler")
log.Println("realTimeHandler")
p := data.DaemonPage{}
initializeHandling(&p, request)
initializeTrackSpecification(&p)
p.TrackSpecification.Stock.Symbol = getStringValue(&p, "stockSymbol")
p.RealTime = true
realTimeGoRoutinesControllerChannel <- &p
RenderPage(writer, &p)
}
感谢您的分享。我目前使用的服务是 Finnhub,它支持 WebSocket,这是我之前从未使用过的技术。生成实时报价的代码如下:
w, _, err := websocket.DefaultDialer.Dial("wss://ws.finnhub.io?token=TOKEN"XXX, nil)
if err != nil {
panic(err)
}
defer w.Close()
symbols := []string{stockSymbol}
for _, s := range symbols {
msg, _ := json.Marshal(map[string]interface{}{"type": "subscribe", "symbol": s})
w.WriteMessage(websocket.TextMessage, msg)
}
var msg interface{}
stock.LatestPrice = -1
stock.Symbol = stockSymbol
for doRealTime {
err := w.ReadJSON(&msg)
之后,我只需要处理 msg 中的报价数据。现在,我正在尝试连接到交易 API,但遗憾的是,它使用的是 OAuth 1.0 协议,这相当麻烦,而且我已经有十多年没接触过它了。开发这个应用所使用的技术,大部分我都是第一次接触,因此我正在学习很多新知识。
在HTTP处理程序中启动goroutine的正确做法是:不要阻塞主处理流程。你的代码中done := <-ch会阻塞,导致响应无法立即发送。以下是修改后的示例:
func realTimeHandler(writer http.ResponseWriter, request *http.Request) {
fmt.Println("realTimeHandler")
log.Println("realTimeHandler")
p := data.DaemonPage{}
initializeHandling(&p, request)
initializeTrackSpecification(&p)
p.TrackSpecification.Stock.Symbol = getStringValue(&p, "stockSymbol")
p.RealTime = true
// 启动goroutine但不等待它完成
go func() {
// 使用context来处理超时或取消
ctx := context.Background()
finnHubRealTime.Realtime(p.TrackSpecification.Stock.Symbol, ctx)
}()
// 立即渲染页面并返回响应
RenderPage(writer, &p)
}
如果你的finnHubRealTime.Realtime函数需要持续运行,可以这样实现:
// 使用全局变量或依赖注入来管理goroutine生命周期
var (
realtimeWorkers sync.Map
)
func realTimeHandler(writer http.ResponseWriter, request *http.Request) {
// ... 初始化代码 ...
symbol := p.TrackSpecification.Stock.Symbol
// 检查是否已经为该symbol启动了goroutine
if _, loaded := realtimeWorkers.LoadOrStore(symbol, true); !loaded {
// 启动新的goroutine
go func(sym string) {
ctx := context.Background()
finnHubRealTime.Realtime(sym, ctx)
// 完成后清理
realtimeWorkers.Delete(sym)
}(symbol)
}
RenderPage(writer, &p)
}
如果需要传递数据给goroutine但不想阻塞:
func realTimeHandler(writer http.ResponseWriter, request *http.Request) {
// ... 初始化代码 ...
// 使用带缓冲的channel避免阻塞
dataCh := make(chan interface{}, 1)
go func(ch chan interface{}) {
// 从channel接收数据(非阻塞方式)
select {
case data := <-ch:
// 处理数据
_ = data
default:
// 没有数据时继续执行
}
// 执行长时间运行的任务
finnHubRealTime.Realtime(p.TrackSpecification.Stock.Symbol, context.Background())
}(dataCh)
// 可以发送数据到goroutine(不会阻塞)
dataCh <- someData
RenderPage(writer, &p)
}
关键点:
- 不要在handler中等待goroutine完成
- 使用context管理goroutine生命周期
- 考虑使用sync.Map或类似结构管理长时间运行的goroutine
- 带缓冲的channel可以避免阻塞主流程

