You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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状态码,立即终止该主机的所有请求任务,同时停止向该主机生成新请求,从根源避免黑名单生效前的无效请求溢出。

具体实现步骤

  1. 主机分组:通过url.Parse拆分输入URL的主机信息,将同一主机的所有路径请求归为一组。
  2. 主机级终止信号:为每个主机创建context.WithCancel上下文,触发429时调用cancel()终止该主机的所有goroutine。
  3. 独立并发控制:每个主机单独启动指定数量的goroutine,共享该主机的上下文,消费专属任务队列。
  4. 实时止损:在请求函数中检测到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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.10 11:58:32