我正在构建一个使用一些外部api来获取数据的go应用程序。不幸的是我的一个来源非常不可靠,所以我想为我的api调用实现一个超时功能,这样我的应用程序就不会被卡住了。不幸的是,我不知道如何开始。请你帮助我好吗?谢谢!
go func() {
for {
ch := make(chan bool, 1)
defer close(ch)
apiCall1()
apiCall2()
apiCall3()
}
}()
答案 0 :(得分:2)
如果您的API调用是http请求,则实现超时的最佳方法是在http客户端本身中指定一个。这样,http.Get(url)
将超时并返回可以适当执行的错误。默认的http客户端具有极长的超时,可能导致应用程序挂起。阅读this以获得对此的良好描述。
以下是apiCall1()
的示例实现,如果发生错误则返回错误,包括如果在10秒内未收到响应则超时。
func apiCall1() error {
var netClient = &http.Client{
Timeout: time.Second * 10,
}
resp, err := netClient.Get("http://someurl.com/apiEndpoint")
if err != nil {
return err
}
if resp.StatusCode != http.StatusOK {
return fmt.Errorf("http request returned code %d", resp.StatusCode)
}
return handleResponse(resp) // Return error if there is one, nil if not.
}
func handleResponse(resp *http.Response) error {
...
}
当您致电apiCall1()
时,您可以随时处理错误。
if err := apiCall1(); err != nil {
log.Print(err)
}
问题的并发性方面有点不清楚,但我会留下两个有一些想法的要点:
go apiCall1()
并处理apiCall1()
函数中的错误/超时。WaitGroup
同时进行调用。答案 1 :(得分:1)
你可以使用一种叫做断路器设计的东西,如果你想要完整的证据和强大的参考: - https://godoc.org/github.com/rubyist/circuitbreaker - https://martinfowler.com/bliki/CircuitBreaker.html
OR
创建这样的东西:
package main
import "fmt"
import "time"
func apiCall(u string, checked chan<- bool) {
time.Sleep(4 * time.Second)
checked <- true
}
func call(urls []string) bool {
ch := make(chan bool, 1)
for _, url := range urls {
go func(u string) {
checked := make(chan bool)
go apiCall(u, checked)
select {
case ret := <-checked:
ch <- ret
case <-time.After(5 * time.Second):
ch <- false
}
}(url)
}
return <-ch
}
func main() {
fmt.Println(call([]string{"url1"}))
}
答案 2 :(得分:1)
研究anyCall()
函数所有的魔法都在其中:)
在这里,我假设你想要3个电话中的一个或超时中的任何一个回复。
https://play.golang.org/p/b-XREKSnP1
package main
import (
"fmt"
"math/rand"
"time"
)
var sluggishness = 10
// your 3 synchronous unreliable api sources
// time.Sleep() is hard work of your api backends :)
func apiCall1() string {
time.Sleep(time.Second * time.Duration(rand.Intn(sluggishness)))
return "api call 1"
}
func apiCall2() string {
time.Sleep(time.Second * time.Duration(rand.Intn(sluggishness)))
return "api call 2"
}
func apiCall3() string {
time.Sleep(time.Second * time.Duration(rand.Intn(sluggishness)))
return "api call 3"
}
// apiCall makes 3 calls concurrently and returns first
func anyCall() string {
// our communicaton channels
api1ret := make(chan string)
api2ret := make(chan string)
api3ret := make(chan string)
// here we fire off 3 api calls concurrently
go func() {
// call and block till we get reply
api1ret <- apiCall1()
// close channel after we are done
// since we are only sending one value
defer close(api1ret)
}()
go func() {
api2ret <- apiCall2()
defer close(api2ret)
}()
go func() {
api3ret <- apiCall3()
defer close(api3ret)
}()
// select blocks till one of channels unblocks with a value
// or time.After() unblocks after 5 sec
select {
case val := <-api1ret:
return val
case val := <-api2ret:
return val
case val := <-api3ret:
return val
case <-time.After(time.Second * 5):
return "timeout after 5 sec"
}
}
func main() {
// make the apiCall 10 times
for i := 0; i < 10; i++ {
fmt.Println(anyCall())
}
fmt.Println("done")
}
这是一个简单的例子,假设apiCall1 / 2/3总是最终在合理的时间内返回。对于这些调用可能会阻塞很长时间的情况,您可能需要更复杂的方案。由于所有go func()
次呼叫最终会累积到大量泄漏。
玩得开心!