Le langage Go est un langage de programmation très adapté à la programmation simultanée. Ses performances sont bien utilisées lors de la mise en œuvre de services ou d'applications à haute concurrence. Dans le développement quotidien, nous pouvons rencontrer des scénarios qui nécessitent des interfaces de requêtes simultanées ou un traitement simultané de grandes quantités de données. Cet article explique comment implémenter des interfaces de requêtes simultanées dans Golang.
Dans le développement réel, nous pouvons rencontrer des scénarios dans lesquels nous devons demander une interface et obtenir des données de réponse, tels que :
Dans un seul fil de discussion, si vous devez demander plusieurs interfaces, vous devez terminer une demande d'interface avant de demander une autre interface, ce qui ralentira l'ensemble du processus. Au contraire, l'utilisation de l'interface de requêtes simultanées peut lancer plusieurs requêtes en même temps, améliorant considérablement l'efficacité des requêtes.
Goroutine est une fonction spéciale du langage Go qui peut s'exécuter dans un thread spécial parallèle au thread principal. Plusieurs goroutines exécutées en même temps peuvent demander plusieurs interfaces en même temps, puis effectuer un traitement d'intégration de données une fois la demande terminée. L'utilisation simultanée de goroutines est relativement facile à mettre en œuvre et peut être réalisée via le mot-clé go.
Dans le développement réel, nous pouvons constater que certaines coroutines peuvent prendre plus de temps et prendre plus de temps pour renvoyer des résultats. Dans ce cas, nous devons attendre que la coroutine renvoie le résultat et effectuer le traitement ultérieur. À l'heure actuelle, nous devons utiliser sync.WaitGroup pour contrôler le nombre de goroutines afin de garantir que toutes les demandes reçoivent des résultats de réponse.
package main import ( "fmt" "io/ioutil" "net/http" "sync" ) var wg sync.WaitGroup // 声明一个sync.WaitGroup实例,用于协程控制 func main() { urls := []string{"https://www.baidu.com", "https://www.qq.com", "https://www.taobao.com", "https://www.jd.com", "https://www.mi.com"} // 通过遍历urls,启动goroutine for _, url := range urls { wg.Add(1) // 添加一个goroutine go getBody(url) } wg.Wait() // 等待所有goroutine结束 } // getBody用于获取传入url的响应结果,并打印。 func getBody(url string) { resp, err := http.Get(url) // 发起http GET请求 if err != nil { fmt.Println(err) return } defer resp.Body.Close() body, err := ioutil.ReadAll(resp.Body) if err != nil { fmt.Println(err) return } fmt.Printf("url: %s, contents: %s ", url, string(body)) wg.Done() // 相当于wg.Add(-1),标志该goroutine已经结束 }
Dans le code ci-dessus, nous déclarons d'abord une instance sync.WaitGroup pour contrôler le nombre de coroutines. Ensuite, dans la fonction main()
, plusieurs coroutines sont démarrées en parcourant des URL. En même temps, chaque fois qu'une coroutine est démarrée, la méthode wg.Add(1) est appelée, indiquant celle-là. doit attendre une coroutine Finish. Dans ce cas, le nombre de coroutines en attente enregistrées dans WaitGroup deviendra le nombre d'url dans les urls. Puis dans la ligne go getBody(url)
, on démarre la coroutine qui demande l'url, puis on appelle la méthode wg.Done()
à la fin de la coroutine, ce qui signifie que la coroutine est terminée. main()
函数中,通过遍历urls启动了多个协程,同时每次启动协程时,都会调用wg.Add(1)方法,表示需要等待一个协程完成。这样的话,WaitGroup中记录的等待的协程数量就会变成urls中url数量。然后在go getBody(url)
这一行,我们启动了请求url的协程,然后在协程结束的时候调用了wg.Done()
方法,表示该协程已经结束。
最后,wg.Wait()
调用使主协程等待所有协程结束。
在实际开发中,我们需要注意一些细节,这些细节可以帮助我们更好地使用并发请求接口。
一、并发数量的控制
在并发请求接口的时候,我们需要控制并发的数量,特别是当接口请求数量比较大时,避免一次性请求使服务器受到太大压力。我们可以设立一个最大值,这样可以保证并发的最高数量。我们可以使用golang中的缓冲通道实现最大并发数的控制。
ch := make(chan struct{}, 5) // 声明一个缓冲通道,大小为5,控制并发数量为5 for _, url := range urls { ch <- struct{}{} // 把协程数量放在通道里 wg.Add(1) // 添加一个goroutine go func(url string) { defer wg.Done() getBody(url) <-ch // 从通道里取出一个值,表示这个协程已经结束 }(url) }
在声明缓冲通道的过程中,我们设置缓冲大小为5,表示最多同时运行5个goroutine,接着我们遍历urls,向通道中加入结构体值。
在启动goroutine的时候,我们声明了一个func(url string)
为处理函数,避免同时运行goroutine的最大数量超过5个,然后调用getBody(url)
方法。在goroutine结束的时候,我们通过通道释放一个信号,表示有一个goroutine结束了——<-ch
。
二、避免请求阻塞
在进行并发请求接口的时候,我们需要避免请求阻塞,通常出现在在一个请求长时间没有相应时。我们可以使用Golang中的context.Context解决这个问题。如果请求超时,则取消阻塞的请求。
url := "https://httpstat.us/200?sleep=8000" ctx, cancel := context.WithTimeout(context.Background(), time.Millisecond*5000) // 告诉请求,5秒之后自动取消 defer cancel() req, err := http.NewRequestWithContext(ctx, "GET", url, nil) // 使用请求上下文 if err != nil { log.Fatal(err) } client := http.DefaultClient resp, err := client.Do(req) // 发起请求 if err != nil { log.Fatal(err) } if resp.StatusCode == http.StatusOK { contents, err := ioutil.ReadAll(resp.Body) if err != nil { log.Fatal(err) } fmt.Printf("%s ", contents) }
在上面的代码中,我们使用了context.WithTimeout
方法创建了一个请求上下文,其timeout设置为5秒,例如http://httpstat.us/200?sleep=8000,这个请求需要8秒才能返回数据。然后我们使用http.NewRequestWithContext方法创建一个使用请求上下文的请求。在发送请求时,我们使用http.DefaultClient
发起请求。最后,如果响应状态码是200,则输出响应数据。
当请求超时时,请求链路就会被直接关掉。这时我们会受到“context deadline exceeded”错误的提示。
三、避免请求重复
在请求接口时,可能会遇到重复请求同一个接口的情况,在这种情况下,我们应该避免重复请求同一个接口,这会浪费宝贵的时间和资源。我们可以使用Golang中的sync.Map解决这个问题。
var m = sync.Map{} url := "https://httpbin.org/get" wg.Add(2) go doGet(url, &m, &wg) go doGet(url, &m, &wg) wg.Wait() func doGet(url string, m *sync.Map, wg *sync.WaitGroup) { _, loaded := m.LoadOrStore(url, true) // 表示url已经被请求过,如果已存在,则直接返回,否则返回false并储存 if loaded { fmt.Printf("url %s already requested. ", url) wg.Done() return } resp, err := http.Get(url) if err != nil { log.Fatal(err) } defer resp.Body.Close() contents, err := ioutil.ReadAll(resp.Body) if err != nil { log.Fatal(err) } fmt.Printf("%s ", contents) wg.Done() }
在上面的代码中,我们使用了一个sync.Map来保证url只被请求一次。在doGet
协程中,我们使用m.LoadOrStore(url, true)来判断url是否已经被请求过,如果请求过了,就return
直接退出协程。否则,我们发起http.Get请求并在log中打印响应数据。最后,我们通过wg.Done()
wg.Wait()
fait que la coroutine principale attend la fin de toutes les coroutines. 🎜🎜Bonnes pratiques pour les requêtes simultanées🎜🎜Dans le développement réel, nous devons prêter attention à certains détails, ce qui peut nous aider à mieux utiliser l'interface de requêtes simultanées. 🎜🎜1. Contrôle du nombre de simultanéités🎜🎜Lors de requêtes d'interface simultanées, nous devons contrôler le nombre de simultanéités, surtout lorsque le nombre de requêtes d'interface est relativement important, pour éviter de mettre trop de pression sur le serveur avec une seule. demandes de temps. Nous pouvons définir une valeur maximale pour garantir le plus grand nombre de simultanéités. Nous pouvons utiliser le canal tampon dans Golang pour contrôler le nombre maximum de simultanéités. 🎜rrreee🎜Dans le processus de déclaration du canal tampon, nous définissons la taille du tampon sur 5, ce qui signifie que jusqu'à 5 goroutines peuvent être exécutées en même temps. Ensuite, nous parcourons les URL et ajoutons la valeur de structure au canal. 🎜🎜Lors du démarrage de la goroutine, nous avons déclaré un func(url string)
comme fonction de traitement pour éviter que le nombre maximum de goroutines exécutées en même temps ne dépasse 5, puis avons appelé getBody(url )
code>méthode. Lorsque la goroutine se termine, nous émettons un signal via le canal, indiquant qu'une goroutine est terminée - <-ch
. 🎜🎜2. Éviter le blocage des requêtes🎜🎜Lors de la création d'interfaces de requêtes simultanées, nous devons éviter le blocage des requêtes, qui se produit généralement lorsqu'une requête ne répond pas pendant une longue période. Nous pouvons résoudre ce problème en utilisant context.Context dans Golang. Si la demande expire, annulez la demande bloquée. 🎜rrreee🎜Dans le code ci-dessus, nous avons utilisé la méthode context.WithTimeout
pour créer un contexte de requête avec son délai d'attente fixé à 5 secondes, tel que http://httpstat.us/200?sleep=8000 , cette requête prend 8 secondes pour renvoyer les données. Nous créons ensuite une requête en utilisant le contexte de requête à l'aide de la méthode http.NewRequestWithContext. Lors de l'envoi d'une requête, nous utilisons http.DefaultClient
pour lancer la requête. Enfin, si le code d'état de la réponse est 200, les données de réponse sont sorties. 🎜🎜Lorsque la demande expire, le lien de demande sera directement fermé. À ce moment-là, une erreur « délai de contexte dépassé » nous sera demandée. 🎜🎜3. Évitez les demandes répétées🎜🎜Lors de la demande d'une interface, vous pouvez rencontrer des demandes répétées pour la même interface. Dans ce cas, nous devons éviter de demander à plusieurs reprises la même interface, ce qui fera perdre un temps et des ressources précieux. Nous pouvons résoudre ce problème en utilisant sync.Map dans Golang. 🎜rrreee🎜Dans le code ci-dessus, nous utilisons un sync.Map pour garantir que l'URL n'est demandée qu'une seule fois. Dans la coroutine doGet
, nous utilisons m.LoadOrStore(url, true) pour déterminer si l'url a été demandée. Si elle a été demandée, return
quitte directement la coroutine. . Sinon, nous lançons une requête http.Get et imprimons les données de réponse dans le journal. Enfin, nous utilisons la méthode wg.Done()
pour marquer la fin de la coroutine. 🎜Cet article explique comment utiliser Golang pour implémenter l'interface de requêtes simultanées. Contrôlez le nombre de simultanéités à l’aide du traitement de simultanéité goroutine, du contrôle de coroutine WaitGroup et des canaux tampons. Évitez le blocage des requêtes en définissant un délai d'attente dans le contexte de la requête et utilisez sync.Map pour éviter la duplication des requêtes. En utilisant ces technologies, nous pouvons considérablement améliorer l’efficacité de l’interface de requête, améliorer l’efficacité du codage et l’expérience de programmation.
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!