深度探讨 Golang 中并发发送 HTTP 请求的最佳技术

在这里插入图片描述

目录

推荐

使用 Goroutines 的基本方法

Goroutine 入门

处理多个请求

并发 HTTP 请求的方法

基本 Goroutine

WaitGroup

Channels

Worker Pools

使用通道限制 Goroutine

使用信号量限制 Goroutines

那么,最好的方法是什么?

评估你的需求

错误处理

集中误差通道

Error Group

包装 Goroutine

总结

⭐️ 好书推荐


推荐

 前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。 点击跳转到网站

在 Golang 领域,并发发送 HTTP 请求是优化 Web 应用程序的一项重要技能。本文探讨了实现此目的的各种方法,从基本的 goroutine 到涉及通道和sync.WaitGroup 的高级技术。我们将深入研究并发环境中性能和错误处理的最佳实践,为你提供提高 Go 应用程序速度和可靠性的策略。让我们深入探讨 Golang 中并发 HTTP 请求的世界!

使用 Goroutines 的基本方法

当谈到在 Golang 中实现并发时,最直接的方法是使用 goroutine。这些是 Go 中并发的构建块,提供了一种简单而强大的并发执行函数的方法。

Goroutine 入门

要启动一个 goroutine,只需在函数调用前加上go关键字即可。这会将函数作为 goroutine 启动,从而允许主程序继续独立运行。这就像开始一项任务并继续前进而不等待它完成。

例如,考虑发送 HTTP 请求的场景。通常,你会调用类似 的函数sendRequest(),并且你的程序将等待该函数完成。使用 goroutine,你可以同时执行此操作:

go sendRequest("http://example.com")

处理多个请求

假设你有一个 URL 列表,并且需要向每个 URL 发送一个 HTTP 请求。如果没有 goroutine,你的程序将一个接一个地发送这些请求,这非常耗时。使用 goroutine,你几乎可以同时发送它们:

urls := []string{"http://example.com", "http://another.com", ...}  
for _, url := range urls {  
go sendRequest(url)  
}

这个循环为每个 URL 启动一个新的 goroutine,大大减少了程序发送所有请求所需的时间。

并发 HTTP 请求的方法

在本节中,我们将深入研究在 Go 中并发处理 HTTP 请求的各种方法。每种方法都有其独特的特点,了解这些可以帮助你选择适合特定需求的正确方法。

我们使用 insrequester 包(开源请求程序)来处理本文中提到的 HTTP请求

基本 Goroutine

在 Go 中并发发送 HTTP 请求的最简单方法是使用 goroutine。Goroutines 是由 Go 运行时管理的轻量级线程。这是一个基本示例:

requester := insrequester.NewRequester().Load()  urls := []string{"http://example.com", "http://example.org", "http://example.net"}  
for _, url := range urls {  
go requester.Get(insrequester.RequestEntity{Endpoint: url})  
}  time.Sleep(2 * time.Second) // 等待 goroutine 完成

这种方法很简单,但一旦启动就缺乏对 goroutine 的控制。通过这种方式无法获取Get方法的返回值。你需要睡眠大约一段时间来等待所有 goroutine。即使你调用 sleep,你可能仍然不确定它们是否完成。

WaitGroup

为了改进基本的 goroutine,sync.WaitGroup可用于更好的同步。它等待 goroutine 集合完成执行:

requester := insrequester.NewRequester().Load()  
wg := sync.WaitGroup{}  urls := []string{"http://example.com", "http://example.org", "http://example.net"}  
wg.Add(len(urls))  for _, url := range urls {  
go requester.Get(insrequester.RequestEntity{Endpoint: url})  
}  wg.Wait() //等待所有要完成的 goroutine

这确保了 main 函数等待所有 HTTP 请求完成。

Channels

Channels 是 Go 中用于 goroutine 之间通信的强大功能。它们可用于从多个 HTTP 请求收集数据:

requester := insrequester.NewRequester().Load()  urls := []string{"http://example.com", "http://example.org", "http://example.net"}  
ch := make(chan string, len(urls))  for _, url := range urls {  
go func() {  
res, _ := requester.Get(insrequester.RequestEntity{Endpoint: url})  
ch <- fmt.Sprintf("%s: %d", url, res.StatusCode)  
}()  
}  for range urls {  
response := <-ch  
fmt.Println(response)  
}

通道不仅可以同步 goroutine,还可以促进它们之间的数据传递。

Worker Pools

Worker Pool 是一种模式,其中创建固定数量的工作人员(goroutines)来处理可变数量的任务。这有助于限制并发 HTTP 请求的数量,从而防止资源耗尽。

以下是在 Go 中实现 Worker Pool 的方法:

// 定义 Job 结构体,包含一个 URL 字段
type Job struct {URL string
}// worker 函数用于处理作业,接收请求者、作业通道、结果通道和等待组作为参数
func worker(requester *insrequester.Request, jobs <-chan Job, results chan<- *http.Response, wg *sync.WaitGroup) {for job := range jobs {// 使用请求者获取 URL 对应的响应res, _ := requester.Get(insrequester.RequestEntity{Endpoint: job.URL})// 将结果发送到结果通道,并减少等待组计数results <- reswg.Done()}
}func main() {// 创建并加载请求者requester := insrequester.NewRequester().Load()// 定义要处理的 URL 列表urls := []string{"http://example.com", "http://example.org", "http://example.net"}// 定义工作池中的工作者数量numWorkers := 2// 创建作业通道和结果通道jobs := make(chan Job, len(urls))results := make(chan *http.Response, len(urls))var wg sync.WaitGroup// 启动工作者for w := 0; w < numWorkers; w++ {go worker(requester, jobs, results, &wg)}// 将作业发送到工作者池wg.Add(len(urls))for _, url := range urls {jobs <- Job{URL: url}}close(jobs)wg.Wait()// 收集结果并输出for i := 0; i < len(urls); i++ {fmt.Println(<-results)}
}

使用工作池可以让你有效地管理大量并发 HTTP 请求。它是一个可扩展的解决方案,可以根据工作负载和系统容量进行调整,从而优化资源利用率并提高整体性能。

使用通道限制 Goroutine

该方法使用通道创建类似信号量的机制来限制并发 goroutine 的数量。它在你需要限制 HTTP 请求以避免服务器不堪重负或达到速率限制的情况下非常有效。

以下是实现它的方法:

// 创建请求者并加载配置
requester := insrequester.NewRequester().Load()// 定义要处理的 URL 列表
urls := []string{"http://example.com", "http://example.org", "http://example.net"}
maxConcurrency := 2 // 限制并发请求的数量// 创建一个用于限制并发请求的通道
limiter := make(chan struct{}, maxConcurrency)// 遍历 URL 列表
for _, url := range urls {limiter <- struct{}{} // 获取一个令牌。在这里等待令牌从限制器释放go func(url string) {defer func() { <-limiter }() // 释放令牌// 使用请求者进行 POST 请求requester.Post(insrequester.RequestEntity{Endpoint: url})}(url)
}// 等待所有 goroutine 完成
for i := 0; i < cap(limiter); i++ {limiter <- struct{}{}
}

在这种情况下使用延迟至关重要。如果将 <-limiter语句放在 Post 方法之后,并且 Post 方法触发恐慌或类似异常,则 <-limiter行将不会被执行。这可能会导致无限等待,因为信号量令牌永远不会被释放,最终导致超时问题。

使用信号量限制 Goroutines

sync/semaphore 包提供了一种干净有效的方法来限制并发运行的 goroutine 数量。当你想要更系统地管理资源分配时,此方法特别有用。

// 创建请求者并加载配置
requester := insrequester.NewRequester().Load()// 定义要处理的 URL 列表
urls := []string{"http://example.com", "http://example.org", "http://example.net"}
maxConcurrency := int64(2) // 设置最大并发请求数量// 创建一个带权重的信号量
sem := semaphore.NewWeighted(maxConcurrency)
ctx := context.Background()// 遍历 URL 列表
for _, url := range urls {// 在启动 goroutine 前获取信号量权重if err := sem.Acquire(ctx, 1); err != nil {fmt.Printf("无法获取信号量:%v\n", err)continue}go func(url string) {defer sem.Release(1) // 在完成时释放信号量权重// 使用请求者获取 URL 对应的响应res, _ := requester.Get(insrequester.RequestEntity{Endpoint: url})fmt.Printf("%s: %d\n", url, res.StatusCode)}(url)
}// 等待所有 goroutine 释放它们的信号量权重
if err := sem.Acquire(ctx, maxConcurrency); err != nil {fmt.Printf("等待时无法获取信号量:%v\n", err)
}

与手动管理通道相比,这种使用信号量包的方法提供了一种更加结构化和可读的并发处理方式。当处理复杂的同步要求或需要更精细地控制并发级别时,它特别有用。

那么,最好的方法是什么?

在探索了 Go 中处理并发 HTTP 请求的各种方法之后,问题出现了:最好的方法是什么?正如软件工程中经常出现的情况一样,答案取决于应用程序的具体要求和约束。让我们考虑确定最合适方法的关键因素:

评估你的需求

  • 请求规模:如果你正在处理大量请求,工作池或基于信号量的方法可以更好地控制资源使用。
  • 错误处理:如果强大的错误处理至关重要,那么使用通道或信号量包可以提供更结构化的错误管理。
  • 速率限制:对于需要遵守速率限制的应用程序,使用通道或信号量包限制 goroutine 可能是有效的。
  • 复杂性和可维护性:考虑每种方法的复杂性。虽然渠道提供了更多控制,但它们也增加了复杂性。另一方面,信号量包提供了更直接的解决方案。

错误处理

由于 Go 中并发执行的性质,goroutines 中的错误处理是一个棘手的话题。由于 goroutine 独立运行,管理和传播错误可能具有挑战性,但对于构​​建健壮的应用程序至关重要。以下是一些有效处理并发 Go 程序中错误的策略:

集中误差通道

一种常见的方法是使用集中式错误通道,所有 goroutine 都可以通过该通道发送错误。然后,主 goroutine 可以监听该通道并采取适当的操作。

func worker(errChan chan<- error) {// 执行任务if err := doTask(); err != nil {errChan <- err // 将任何错误发送到错误通道}
}func main() {errChan := make(chan error, 1) // 用于存储错误的缓冲通道go worker(errChan)if err := <-errChan; err != nil {// 处理错误log.Printf("发生错误:%v", err)}
}

或者你可以在不同的 goroutine 中监听 errChan。

func worker(errChan chan<- error, job Job) {// 执行任务if err := doTask(job); err != nil {errChan <- err // 将任何错误发送到错误通道}
}func listenErrors(done chan struct{}, errChan <-chan error) {for {select {case err := <-errChan:// 处理错误case <-done:return}}
}func main() {errChan := make(chan error, 1000) // 存储错误的通道done := make(chan struct{})       // 用于通知 goroutine 停止的通道go listenErrors(done, errChan)for _, job := range jobs {go worker(errChan, job)}// 等待所有 goroutine 完成(具体方式需要根据代码的实际情况进行实现)done <- struct{}{} // 通知 goroutine 停止监听错误
}

Error Group

golang.org/x/sync/errgroup 包提供了一种便捷的方法来对多个 goroutine 进行分组并处理它们产生的任何错误。errgroup.Group确保一旦任何 goroutine 发生错误,所有后续操作都将被取消。

import "golang.org/x/sync/errgroup"func main() {g, ctx := errgroup.WithContext(context.Background())urls := []string{"http://example.com", "http://example.org"}for _, url := range urls {// 为每个 URL 启动一个 goroutineg.Go(func() error {// 替换为实际的 HTTP 请求逻辑_, err := fetchURL(ctx, url)return err})}// 等待所有请求完成if err := g.Wait(); err != nil {log.Printf("发生错误:%v", err)}
}

这种方法简化了错误处理,特别是在处理大量 goroutine 时。

包装 Goroutine

另一种策略是将每个 goroutine 包装在一个处理其错误的函数中。这种封装可以包括从恐慌或其他错误管理逻辑中恢复。

func work() error {// 进行一些工作return err
}func main() {go func() {err := work()if err != nil {// 处理错误}}()// 等待工作完成的某种方式
}

综上所述,Go 并发编程中错误处理策略的选择取决于应用程序的具体要求和上下文。无论是通过集中式错误通道、专用错误处理 goroutine、使用错误组,还是将 goroutine 包装在错误管理函数中,每种方法都有自己的优点和权衡。

总结

总之,本文探讨了在 Golang 中并发发送 HTTP 请求的各种方法,这是优化 Web 应用程序的一项关键技能。我们已经讨论了基本的 goroutine、sync.WaitGroup、通道、工作池以及限制 goroutine 的方法。每种方法都有其独特的特点,可以根据特定的应用要求进行选择。

此外,本文还强调了并发 Go 程序中错误处理的重要性。管理并发环境中的错误可能具有挑战性,但对于构建健壮的应用程序至关重要。已经讨论了使用集中式错误通道、errgroup 包或使用错误处理逻辑包装 goroutine 等策略来帮助开发人员有效地处理错误。

最终,在 Go 中处理并发 HTTP 请求的最佳方法的选择取决于请求规模、错误处理要求、速率限制以及代码的整体复杂性和可维护性等因素。开发人员在应用程序中实现并发功能时应仔细考虑这些因素。

⭐️ 好书推荐

《Go专家编程(第2版)》

在这里插入图片描述

【内容简介】

本书深入地讲解了Go语言常见特性的内部机制和实现方式,大部分内容源自对Go源码的分析,并从中提炼出实现原理。通过阅读本书,读者可以快速、轻松地了解Go语言的内部运作机制。

本书首先介绍常见数据结构及控制结构的实现原理,包括管道、切片、Hash表、select 和 for-range 等,这部分内容大都以几个精心准备的测验题目开头,每个测验题目均对应一个知识点,读者借此可以测验自身对该知识点的掌握程度。接着介绍了Go语言最核心的概念,包括协程的概念、协程调度模型、协程调度策略,以及内存分配和垃圾回收相关的内容。本书还介绍了测试、泛型、依赖管理等比较实用的特性。最后结合笔者的见闻,整理了一些发生在真实项目中的编程陷阱。

📚 京东购买链接:《Go专家编程(第2版)》

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/242766.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

DevOps系列文章之 GitLab CI/CD

CICD是什么? 由于目前公司使用的gitlab&#xff0c;大部分项目使用的CICD是gitlab的CICD&#xff0c;少部分用的是jenkins&#xff0c;使用了gitlab-ci一段时间后感觉还不错&#xff0c;因此总结一下 介绍gitlab的CICD之前&#xff0c;可以先了解CICD是什么 我们的开发模式…

algotithm -- 排序算法

排序算法总结表&#xff1a; 1. In-place 和 Out-place 含义 参考链接 in-place 占用常数内存&#xff0c;不占用额外内存 假如问题规模是n&#xff0c;在解决问题过程中&#xff0c;只开辟了常数量的空间&#xff0c;与n无关&#xff0c;这是原址操作&#xff0c;就是In-…

安卓平板局域网内远程控制工控机方法

安卓平板局域网内远程控制工控机方法 将所需要远程控制的工控机通过网线连接到具有WiFi功能的路由器上&#xff0c;将安卓平板连接上WiFi&#xff0c;如下图所示 下载NoMachine远程软件安装包&#xff0c;官网地址&#xff1a;https://www.nomachine.com/ 点击Download now按钮…

Vulnhub靶机:FunBox 3

一、介绍 运行环境&#xff1a;Virtualbox 攻击机&#xff1a;kali&#xff08;10.0.2.15&#xff09; 靶机&#xff1a;FunBox 3&#xff08;10.0.2.28&#xff09; 目标&#xff1a;获取靶机root权限和flag 靶机下载地址&#xff1a;https://download.vulnhub.com/funbo…

2018年认证杯SPSSPRO杯数学建模C题(第一阶段)机械零件加工过程中的位置识别全过程文档及程序

2018年认证杯SPSSPRO杯数学建模 基于轮廓特征的机械零件位置识别研究 C题 机械零件加工过程中的位置识别 原题再现&#xff1a; 在工业制造自动生产线中&#xff0c;在装夹、包装等工序中需要根据图像处理利用计算机自动智能识别零件位置&#xff0c;并由机械手将零件自动搬…

【Elasticsearch】索引恢复(recovery)流程梳理之副本分片数据恢复

replica shard重启具体流程 replica shard node &#xff08;generic threadpool&#xff09; 也是因为应用新的集群状态触发recovery&#xff0c;进入index阶段进入translog 阶段。先尝试重放本地的translog到global checkpoint向primary shard发起start recovery的请求&…

第6章 现代通信技术

文章目录 6.1 图像与多媒体通信6.1.1 图像通信6.1.2 多媒体通信技术1、多媒体通信概念2、多媒体通信的组成3、多媒体通信的业务分类4、实用化的多媒体通信系统类型5、多媒体通信应用系统&#xff08;1&#xff09;多媒体会议电视系统&#xff08;2&#xff09;IPTV 6.2 移动通信…

uniapp uni.chooseLocation调用走失败那里,错误码:112

问题&#xff1a;我配置了百度上所有能配置的&#xff0c;一直调用不成功&#xff0c;如下图配置的 1:第一个 配置 代码&#xff1a; "permission": {"scope.userLocation": {"desc": "你的位置信息将用于小程序位置接口的效果展示"}…

Statistics with Python知识总结:库、统计图

前言 统计学作为一门重要的数据分析领域&#xff0c;为我们理解和解释数据提供了有力的工具。而Python是用来进行统计自动化和画图的重要工具。本文总结了与统计学相关的Python数据库和不同类型的统计图的关键知识点&#xff0c;帮助读者更好地理解工具&#xff0c;以及各知识…

RocketMQ学习总结

一、架构 1、NameServer&#xff1a;注册中心。Broker信息注册到NameServer&#xff1b;producer/consumer根据某个topic通过NameServer获取对应broker的路由信息 &#xff1b; 2、Broker&#xff1a;负责存储、拉取、转发消息&#xff1b; 3、Producer&#xff1a;消息生产者…

浅谈情绪的分类合集

一、什么是情绪分类 情绪分类&#xff0c;是指区分或者对比一种情绪与另一种情绪的方法&#xff0c;目前在情绪研究&#xff08;emotion research&#xff09;与情感科学&#xff08;affective science&#xff09;是具有争议的问题。有两个讨论情绪分类的基本观点&#xff1a…

ARP相关

ARP报文格式&#xff1a; 目的以太网地址&#xff0c;48bit&#xff0c;发送ARP请求时&#xff0c;目的以太网地址为广播MAC地址&#xff0c;即0xFF.FF.FF.FF.FF.FF。 源以太网地址&#xff0c;48bit。 帧类型&#xff0c;对于ARP请求或者应答&#xff0c;该字段的值都为0x08…

Traceroute 详解

前言 如果您是网络管理员&#xff0c;系统管理员或任何系统操作团队的一员&#xff0c;那么您可能已经听说过名为TRACEROUTE的工具。默认情况下&#xff0c;它是大多数操作系统中都提供的非常方便的工具。 网络管理员和系统管理员在日常活动中最常使用此工具。它基本上是一个…

pandas操作excel

目录 一&#xff1a;创建excel 二&#xff1a;修改excel 三&#xff1a;查找excel 四&#xff1a;删除数据 五&#xff1a;合并excel数据 一&#xff1a;创建excel import pandas as pd # 创建DataFrame对象 data { Name: [Alice, Bob, Charlie], Age: [25, 30, 35], S…

Microsoft Visual C++ RunTime怎么下载?

64位下载链接 下载好程序后双击&#xff0c;勾选“我同意许可条款和条件”&#xff0c;然后点击“安装” 安装完成后点击“关闭”即可 感谢您的阅读与关注&#xff0c;服务器大本营助您成为更专业的服务器管理员&#xff01;

32 登录页组件

效果演示 实现了一个登录页面的样式&#xff0c;包括一个容器、左侧和右侧部分。左侧部分是一个背景图片&#xff0c;右侧部分是一个表单&#xff0c;包括输入框、复选框、按钮和忘记密码链接。整个页面的背景色为白色&#xff0c;容器为一个圆角矩形&#xff0c;表单为一个半透…

华为机考入门python3--(0)模拟题2-vowel元音字母翻译

分类&#xff1a;字符串 知识点&#xff1a; 字符串转list&#xff0c;每个字符成为list中的一个元素 list(string) 字符串变大小写 str.upper(), str.lower() 题目来自【华为招聘模拟考试】 # If you need to import additional packages or classes, please import …

UE5 独立程序的网络TCP/UDP服务器与客户端基础流程

引擎源码版&#xff0c;复制\Engine\Source\Programs\路径下的BlankProgram空项目示例。 重命名BlankProgram&#xff0c;例如CustomTcpProgram&#xff0c;并修改项目名称。 修改.Build.cs内容 修改Target.cs内容 修改Private文件夹内.h.cpp文件名并修改.cpp内容 刷新引擎 …

C++入门学习(七)整型

整型就是整数类型的数据&#xff08;-1&#xff0c;0&#xff0c;1等等&#xff09; 数据类型占用空间取值范围short(短整型)2字节 (-2^15 ~ 2^15-1) 32768~32767 int(整型)4字节(-2^31 ~ 2^31-1)long(长整形) Windows为4字节, Linux为4字节(32位), 8字节(64位) (-2^31 ~ 2^31…

为什么需要放行回源IP

为什么需要放行回源IP 网站以“独享模式”成功接入WAF后&#xff0c;所有网站访问请求将先经过独享引擎配置的ELB然后流转到独享引擎实例进行监控&#xff0c;经独享引擎实例过滤后再返回到源站服务器&#xff0c;流量经独享引擎实例返回源站的过程称为回源。在服务器看来&…