协程超时
Goroutine Timeout
type Response struct {
data interface{}
status bool
}
func Find() (interface{}, bool) {
ch := make(chan Response, 1)
go func() {
data, status := findCicCode()
ch <- Response{data: data, status: status}
}()
select {
case response := <-ch:
return response.data, response.status
case <-time.After(50 * time.Millisecond):
return "Request timed out", false
}
}
所以,我有上面的功能。基本上 findCicCode()
函数调用在内部对外部服务进行 3 个 http 调用。我在这里为这 3 个 http 调用添加了组合超时。不能在我的情况下设置个人超时。但如果超过超时,它仍然会在后台进行 api 调用。
我不确定这里是否有 goroutine 泄漏。如果超时,有没有办法取消这些 https 请求?
您可以使用 context.Context
.
控制取消 http 请求
// create a timeout or cancelation context to suit your requirements
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
defer cancel()
req, err := http.NewRequest("GET", location, nil)
// add the context to each request and they will be canceled in unison
resp, err := http.Do(req.WithContext(ctx))
如果你愿意,你可以通过在通道上(在主 goroutine 中)进行单个接收操作来为任意工作创建自己的超时系统,并且无论哪个 goroutine 先到达它的发送操作 - time.Sleep
或实际工作的人获胜。
这是一个完整的可运行文件 example/simulation。调整超时和延迟值以模拟不同的场景。该通道是无缓冲的,并在读取单个值后关闭以允许另一个 goroutine 在发送时退出。
package main
import(
"fmt"
"time"
)
type Response struct {
Data []byte
Status int
}
func Wait(s int) {
time.Sleep(time.Duration(s) * time.Second)
}
func FindWrapper(ch chan Response, delay int) {
// Put real find stuff here...
// Dummy response after wait for testing purposes
Wait(delay)
ch <- Response{[]byte("Some data..."), 200}
}
func main() {
timeout := 3
delay := 4
ch := make(chan Response)
// whoever sends to ch first wins...
go func() {
Wait(timeout)
ch <- Response{}
}()
go FindWrapper(ch, delay)
r := <-ch
close(ch)
if r.Data == nil {
r.Status = 500 // or whatever you want for timeout status
}
fmt.Printf("Data: %s Status: %d\n", string(r.Data), r.Status)
}
缓冲通道也可以。你可以用 sync.WaitGroup
完成同样的事情,你只调用一次添加然后在 wg.Wait()
.
之后关闭频道
也就是说,我建议尝试使用 JimB 的 Context
超时解决方案,因为它可能适用于您的用例并且是一个不太复杂的解决方案。
type Response struct {
data interface{}
status bool
}
func Find() (interface{}, bool) {
ch := make(chan Response, 1)
go func() {
data, status := findCicCode()
ch <- Response{data: data, status: status}
}()
select {
case response := <-ch:
return response.data, response.status
case <-time.After(50 * time.Millisecond):
return "Request timed out", false
}
}
所以,我有上面的功能。基本上 findCicCode()
函数调用在内部对外部服务进行 3 个 http 调用。我在这里为这 3 个 http 调用添加了组合超时。不能在我的情况下设置个人超时。但如果超过超时,它仍然会在后台进行 api 调用。
我不确定这里是否有 goroutine 泄漏。如果超时,有没有办法取消这些 https 请求?
您可以使用 context.Context
.
// create a timeout or cancelation context to suit your requirements
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
defer cancel()
req, err := http.NewRequest("GET", location, nil)
// add the context to each request and they will be canceled in unison
resp, err := http.Do(req.WithContext(ctx))
如果你愿意,你可以通过在通道上(在主 goroutine 中)进行单个接收操作来为任意工作创建自己的超时系统,并且无论哪个 goroutine 先到达它的发送操作 - time.Sleep
或实际工作的人获胜。
这是一个完整的可运行文件 example/simulation。调整超时和延迟值以模拟不同的场景。该通道是无缓冲的,并在读取单个值后关闭以允许另一个 goroutine 在发送时退出。
package main
import(
"fmt"
"time"
)
type Response struct {
Data []byte
Status int
}
func Wait(s int) {
time.Sleep(time.Duration(s) * time.Second)
}
func FindWrapper(ch chan Response, delay int) {
// Put real find stuff here...
// Dummy response after wait for testing purposes
Wait(delay)
ch <- Response{[]byte("Some data..."), 200}
}
func main() {
timeout := 3
delay := 4
ch := make(chan Response)
// whoever sends to ch first wins...
go func() {
Wait(timeout)
ch <- Response{}
}()
go FindWrapper(ch, delay)
r := <-ch
close(ch)
if r.Data == nil {
r.Status = 500 // or whatever you want for timeout status
}
fmt.Printf("Data: %s Status: %d\n", string(r.Data), r.Status)
}
缓冲通道也可以。你可以用 sync.WaitGroup
完成同样的事情,你只调用一次添加然后在 wg.Wait()
.
也就是说,我建议尝试使用 JimB 的 Context
超时解决方案,因为它可能适用于您的用例并且是一个不太复杂的解决方案。