Go 超时后关闭通道
我有一个小的围棋程序,每滴答(1秒)都会发出许多请求。我正试图同时提出这些请求。我想计算并记录一次成功请求的数量,然后继续。如果请求没有及时完成,我不想阻止主代码 下面的代码实现了这一点,但我认为我没有正确关闭concurrentReqs中的通道。因为任何错过最后期限的请求仍然会记录在上一个勾号中。我还认为,主功能中的ticker将阻止等待concurrentReqs完成。我在select中尝试将Go 超时后关闭通道,go,concurrency,channel,Go,Concurrency,Channel,我有一个小的围棋程序,每滴答(1秒)都会发出许多请求。我正试图同时提出这些请求。我想计算并记录一次成功请求的数量,然后继续。如果请求没有及时完成,我不想阻止主代码 下面的代码实现了这一点,但我认为我没有正确关闭concurrentReqs中的通道。因为任何错过最后期限的请求仍然会记录在上一个勾号中。我还认为,主功能中的ticker将阻止等待concurrentReqs完成。我在select中尝试将close(ch)移动到超时情况的内部,但这会导致“关闭通道发送”错误 我的理解是,使用带有最后期限
close(ch)
移动到超时情况的内部,但这会导致“关闭通道发送”错误
我的理解是,使用带有最后期限的上下文(可能是在我的主代码中设置的)可能是解决这个问题的一个方法,但我正努力想办法解决这个问题,我想知道是否还有其他方法可以尝试
注意:concurrentReqs中的超时故意较低,因为我正在本地测试
主程序包
进口(
“fmt”
“时间”
“net/http”
)
类型响应结构{
num int
状态代码int
请求持续时间。持续时间
}
func singleRequest(url字符串、i int、tick int)响应{
开始:=时间。现在()
客户端:=http.client{Timeout:100*time.毫秒}
resp,\:=client.Get(url)
fmt.Printf(“%d:%d\n”,勾选,i)
延迟响应主体关闭()
返回响应{statusCode:int(resp.statusCode),requestDuration:time.Since(start)}
}
func concurrentReqs(url字符串、reqsPerTick int、tick int)(结果[]响应){
ch:=制造(chan响应,reqsPerTick)
超时:=time.After(20*time.毫秒)//故意低
结果=生成([]响应,0)
对于i:=0;i ch我建议使用带有超时的上下文来取消和超时。另外,我认为使用等待组和互斥保护的结果写入可以通过消除第二个循环来帮助简化
package main
import (
"context"
"fmt"
"log"
"net/http"
"sync"
"time"
)
type response struct {
num int
statusCode int
requestDuration time.Duration
}
func singleRequest(ctx context.Context, url string, i int, tick int) (response, error) {
start := time.Now()
req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
if err != nil {
return response{requestDuration: time.Since(start)}, err
}
resp, err := http.DefaultClient.Do(req)
if err != nil {
return response{requestDuration: time.Since(start)}, err
}
fmt.Printf("%d: %d\n", tick, i)
defer resp.Body.Close()
return response{statusCode: int(resp.StatusCode), requestDuration: time.Since(start)}, nil
}
func concurrentReqs(url string, reqsPerTick int, tick int) (results []response) {
mu := sync.Mutex{}
results = make([]response, 0)
ctx, cancel := context.WithTimeout(context.Background(), 20*time.Millisecond)
defer cancel()
wg := sync.WaitGroup{}
for i := 0; i < reqsPerTick; i++ {
wg.Add(1)
go func(i int, t int) {
defer wg.Done()
response, err := singleRequest(ctx, url, i, tick)
if err != nil {
log.Print(err)
return
}
mu.Lock()
results = append(results, response)
mu.Unlock()
}(i, tick)
}
wg.Wait()
return results
}
func main() {
var url string = "http://end-point.svc/req"
c := time.Tick(1 * time.Second)
for next := range c {
// You may want to wrap this in a goroutine to make sure tick is not skipped.
// Otherwise if concurrentReqs takes more than a tick time for whatever reason, a tick will be skipped.
things := concurrentReqs(url, 100, next.Second())
fmt.Printf("%s: Successful Reqs - %d\n", int(next.Second()), len(things))
}
}
主程序包
进口(
“上下文”
“fmt”
“日志”
“net/http”
“同步”
“时间”
)
类型响应结构{
num int
状态代码int
请求持续时间。持续时间
}
func singleRequest(ctx context.context,url字符串,i int,tick int)(响应,错误){
开始:=时间。现在()
req,err:=http.NewRequestWithContext(ctx,http.MethodGet,url,nil)
如果错误!=零{
返回响应{requestDuration:time.Since(start)},错误
}
resp,err:=http.DefaultClient.Do(请求)
如果错误!=零{
返回响应{requestDuration:time.Since(start)},错误
}
fmt.Printf(“%d:%d\n”,勾选,i)
延迟响应主体关闭()
返回响应{statusCode:int(resp.statusCode),requestDuration:time.Since(start)},nil
}
func concurrentReqs(url字符串、reqsPerTick int、tick int)(结果[]响应){
mu:=sync.Mutex{}
结果=生成([]响应,0)
ctx,cancel:=context.WithTimeout(context.Background(),20*time.毫秒)
推迟取消
wg:=sync.WaitGroup{}
对于i:=0;i
为什么要关闭频道?如果超时,您不会关闭频道。关闭
应该在延迟
中。不确定这是否是您的问题,但这是一个问题:-)即使您关闭频道,gorotines也会完成执行并自行记录,频道关闭对“singleRequest”是否成功没有影响执行,因为只有其结果被推入通道