如何使用 golang 在多个转发代理之间轮换发出请求

ube*_*ebu 8 proxy post go

我想传递 POST 请求的转发代理服务器列表

目前我只能使用单个转发代理来完成此操作

serverProxy := "http://user:password@123.45.67.89:3128"

request, error := http.NewRequest("POST", httpposturl, bytes.NewBuffer(requestJSON))
request.Header.Set("Content-Type", "application/json; charset=UTF-8")

proxyURL, _ := url.Parse(serverProxy)
proxy := http.ProxyURL(proxyURL)
transport := &http.Transport{Proxy: proxy}
client := &http.Client{Transport: transport}
Run Code Online (Sandbox Code Playgroud)

我想做的是将列表传递给url.Parse并希望它使用循环平衡来使用它们

所以像这样

serverProxy := "http://user:password@123.45.67.89:3128, http://user:password@223.45.67.89:3128"
Run Code Online (Sandbox Code Playgroud)

然后它将选择要使用的代理服务器并在请求中轮换它们

这可能吗?

更新:

我希望能够像这样传递旋转的代理服务器

proxyServer := roundRobin("http://round:robin@123.45.67.89:3128, http://robin:round@223.45.67.89:3128")
fmt.Println("proxy server used", proxyServer, "\n")
transport := &http.Transport{Proxy: proxyServer}
client := &http.Client{Transport: transport}
Run Code Online (Sandbox Code Playgroud)

Cha*_*dan 2

这是蒙太奇的回答和解释:

要求是通过代理以循环方式转发请求

由于我们使用的http.Client是发出请求,因此我们可以查看http.Client文档,看看它是否提供通过代理转发请求的任何支持,当我们查看文档时,我们可以看到它确实支持传递代理,我们可以通过将传递给的http.Transport类型传递该代理http.Clienthttp.Transport通过字段获取代理,Proxy该字段接受返回的 func *url.URL,并且包内提供了error类似 和 等现有方法http.ProxyURL,我们可以使用它们来传递代理,但这些方法的问题是它们只采用单个代理服务器,这不能解决我们的问题因此,我们需要创建自己的函数,该函数接受多个代理服务器 URL 并在它们之间进行循环。http.ProxyFromEnvironmenthttphttp.Transport

如果我们将现有方法实现之一视为创建我们自己的方法的基础,那么我们就http.ProxyURL可以找到该实现here。我复制了下面的实现

func ProxyURL(fixedURL *url.URL) func(*Request) (*url.URL, error) {
    return func(*Request) (*url.URL, error) {
        return fixedURL, nil
    }
}
Run Code Online (Sandbox Code Playgroud)

我们可以看到它是一个简单的闭包,它接受单个 url 并返回一个闭包函数,然后该函数返回作为参数传入的 url。所以我们可以以此为基础并创建我们自己的循环闭包函数

func roundRobin(proxies ...string) func(*http.Request) (*url.URL, error) {
    var urls []*url.URL
    for _, proxy := range proxies {
        u, err := url.Parse(proxy)
        if err != nil {
            log.Fatal(err)
        }
        urls = append(urls, u)
    }

    var mu sync.Mutex
    var i, lenUrls int = 0, len(urls)
    return func(r *http.Request) (*url.URL, error) {
        mu.Lock()
        i = (i + 1) % lenUrls
        u := urls[i]
        mu.Unlock()
        return u, nil
    }
}
Run Code Online (Sandbox Code Playgroud)

让我们回顾一下roundRobin函数的实现,它是一个可变参数函数,它接受字符串格式的代理 url 作为参数,在内部url.URL通过使用解析字符串进行转换url.Parse,然后使用解析后的内容url.URL创建 url 片段[]*url.URL,然后将其用于转发以循环方式请求

完整的工作示例如下:

package main

import (
    "fmt"
    "log"
    "net/url"
    "net/http"
    "sync"
)

func roundRobin(proxies ...string) func(*http.Request) (*url.URL, error) {
    var urls []*url.URL
    for _, proxy := range proxies {
        u, err := url.Parse(proxy)
        if err != nil {
            log.Fatal(err)
        }
        urls = append(urls, u)
    }

    var mu sync.Mutex
    var i, lenUrls int = 0, len(urls)
    return func(r *http.Request) (*url.URL, error) {
        mu.Lock()
        i = (i + 1) % lenUrls
        u := urls[i]
        mu.Unlock()
        return u, nil
    }
}

func main() {
    proxyFn := roundRobin("http://user:password@123.45.67.89:3128", "http://user:password@223.45.67.89:3128")
    transport := &http.Transport{Proxy: proxyFn}
    client := &http.Client{Transport: transport}

    req, err := http.NewRequest("POST", "http://example.com", nil)
    req.Header.Set("Content-Type", "application/json; charset=UTF-8")

    resp, err := client.Do(req)
    if err != nil {
        fmt.Println(resp)
    }

    fmt.Println(proxyFn(nil))

    fmt.Println(proxyFn(nil))
}
Run Code Online (Sandbox Code Playgroud)

操场

另一个版本

package main

import (
    "fmt"
    "log"
    "net/http"
    "net/url"
    "sync"
)

func praseUrls(proxies ...string) (urls []*url.URL) {
    for _, proxy := range proxies {
        u, err := url.Parse(proxy)
        if err != nil {
            log.Fatal(err)
        }
        urls = append(urls, u)
    }
    return
}

func roundRobin(max int) func() int {
    var i int
    return func() int {
        i = (i + 1) % max
        return i
    }
}

func proxyFn(urls []*url.URL) func(*http.Request) (*url.URL, error) {
    var m sync.Mutex
    fn := roundRobin(len(urls))
    return func(*http.Request) (*url.URL, error) {
        m.Lock()
        u := urls[fn()]
        m.Unlock()
        return u, nil
    }
}

func main() {
    proxies := []string{"http://user:password@123.45.67.89:3128", "http://user:password@223.45.67.89:3128"}
    urls := praseUrls(proxies...)
    transport := &http.Transport{Proxy: proxyFn(urls)}
    client := &http.Client{Transport: transport}

    req, err := http.NewRequest("POST", "http://example.com", nil)
    req.Header.Set("Content-Type", "application/json; charset=UTF-8")

    resp, err := client.Do(req)
    if err != nil {
        fmt.Println(resp)
    }
}
Run Code Online (Sandbox Code Playgroud)

操场

注意:最好从 env 变量传递代理 url,这将有助于防止任何代理服务器更改或添加新代理服务器