Go语言中如何终止向返回429状态码的主机发送请求?
问题描述
我正在编写一个用于合法渗透测试的URL路径爆破脚本,当前采用Go语言并发代码实现。需求是:当某主机返回429(请求过多)状态码触发限流时,停止向该主机发送请求,继续对其他主机进行爆破。
我曾尝试通过互斥锁维护黑名单数组,goroutine在发送请求前检查目标主机是否在黑名单中,但该方案存在缺陷——在将主机加入黑名单前,已发送大量请求。请问是否有更优方案?也可调整并发设计模式。
当前代码如下:
urls := make(chan string) for i := 0; i < concurrency; i++ { wg.Add(1) go func() { for url := range fileNamesChan { sendRequest(url) } wg.Done() }() } for _, url := range inputURLS { for _, fileName := range fileNames { urls <- url + fileName } } close(urls) wg.Wait()
优化方案
核心思路
放弃全局URL通道的混编设计,改为按目标主机分组管理任务,给每个主机分配独立的终止信号。一旦检测到429状态码,立即终止该主机的所有请求任务,同时停止向该主机生成新请求,从根源避免黑名单生效前的无效请求溢出。
具体实现步骤
- 主机分组:通过
url.Parse拆分输入URL的主机信息,将同一主机的所有路径请求归为一组。 - 主机级终止信号:为每个主机创建
context.WithCancel上下文,触发429时调用cancel()终止该主机的所有goroutine。 - 独立并发控制:每个主机单独启动指定数量的goroutine,共享该主机的上下文,消费专属任务队列。
- 实时止损:在请求函数中检测到429后,立即触发终止信号,后续该主机的goroutine会因上下文取消直接退出。
修改后的代码示例
package main import ( "context" "net/url" "sync" ) func main() { var globalWG sync.WaitGroup // 按主机分组任务:key为主机Host,value为该主机的所有待请求URL hostTasks := make(map[string][]string) for _, baseURL := range inputURLS { parsedURL, err := url.Parse(baseURL) if err != nil { continue // 跳过无效URL } var tasks []string for _, fileName := range fileNames { tasks = append(tasks, baseURL+fileName) } hostTasks[parsedURL.Host] = tasks } // 遍历每个主机的任务组 for host, tasks := range hostTasks { globalWG.Add(1) go func(host string, tasks []string) { defer globalWG.Done() // 创建该主机的专属上下文,用于发送终止信号 ctx, cancel := context.WithCancel(context.Background()) defer cancel() taskChan := make(chan string, len(tasks)) // 写入该主机的所有任务 for _, task := range tasks { taskChan <- task } close(taskChan) var hostWG sync.WaitGroup // 为该主机启动指定数量的并发goroutine for i := 0; i < concurrency; i++ { hostWG.Add(1) go func() { defer hostWG.Done() for { select { case url, ok := <-taskChan: if !ok { return } // 先检查上下文是否已终止,避免无效请求 select { case <-ctx.Done(): return default: statusCode, err := sendRequest(url) if err == nil && statusCode == 429 { // 触发限流,立即终止该主机所有任务 cancel() return } } case <-ctx.Done(): return } } }() } hostWG.Wait() }(host, tasks) } globalWG.Wait() } // 自定义请求函数,返回状态码和错误 func sendRequest(url string) (int, error) { // 此处实现你的HTTP请求逻辑 return 200, nil }
方案优势
- 精准隔离:单个主机触发限流后,仅终止该主机的任务,完全不影响其他主机的爆破流程。
- 即时止损:上下文取消信号会立即终止所有该主机的goroutine,不会再发送任何后续请求,彻底避免无效请求浪费资源。
- 资源可控:按主机拆分任务后,可针对不同主机单独调整并发数或添加速率限制(比如结合
golang.org/x/time/rate),进一步降低触发限流的概率。
内容的提问来源于stack exchange,提问作者Someguyy
相关产品推荐
相关产品推荐

