How to rotate between multiple forwarding proxies for outgoing requests with golang

huangapple go评论106阅读模式
英文:

How to rotate between multiple forwarding proxies for outgoing requests with golang

问题

我将为您翻译以下内容:

我想要传递一个转发代理服务器列表用于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}

我想要做的是将一个列表传递给url.Parse,并希望它使用轮询负载均衡来使用它们

所以像这样

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

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

这种做法可行吗?

更新:

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

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}
英文:

I will like to pass a list of forwarding proxy servers for POST request

Currently i am able to do it with just single forwarding proxy

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}

what i will like to do is pass a list to url.Parse and want it to use them using round robin balancing

so something like this

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

and then it will select which of the proxy servers to use and rotate them within requests

Is this possible?

> UPDATE:

I want to be able to pass the rotated proxy server like this

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}

答案1

得分: 3

创建一个代理函数,通过你的代理URL进行轮询。在你的传输中使用该函数:

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

transport := &http.Transport{Proxy: roundRobin(yourProxyURLs)}
client := &http.Client{Transport: transport}
英文:

Create a proxy function that round-robins through your proxy URLs. Use that function in your transport:

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


transport := &http.Transport{Proxy: roundRobin(yourProxyURLs)}
client := &http.Client{Transport: transport}

答案2

得分: 2

以下是Montage的答案和解释:

要求是以轮询的方式通过代理转发请求。

由于我们使用http.Client来进行请求,我们可以查看http.Client的文档,看看它是否支持通过代理转发请求。当我们查看文档时,可以看到它确实支持传递代理,我们可以通过http.Transport类型传递代理,然后将其传递给http.Clienthttp.Transport通过Proxy字段接收代理,该字段接收一个返回*url.URLerror的函数。在http包中提供了一些现有的方法,如http.ProxyURLhttp.ProxyFromEnvironment,我们可以使用这些方法将代理传递给http.Transport,但是这些方法的问题是它们只接受一个单独的代理服务器,这不能解决我们目前的问题,因此我们需要创建自己的函数,该函数接受多个代理服务器的URL,并在它们之间进行轮询。

如果我们以现有方法的实现作为我们自己方法的基础,让我们选择http.ProxyURL作为我们的基础,我们可以在这里找到实现。我已经复制了以下实现:

func ProxyURL(fixedURL *url.URL) func(*Request) (*url.URL, error) {
	return func(*Request) (*url.URL, error) {
		return fixedURL, nil
	}
}

我们可以看到这是一个简单的闭包,它接受一个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
	}
}

让我们来看看roundRobin函数的实现。它是一个可变参数函数,接受代理URL(以字符串格式)作为参数,内部使用url.Parse将字符串解析为url.URL,然后使用解析后的url.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))
}

Playground

另一个版本:

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)
	}
}

Playground

**注意:**最好从环境变量中传递代理URL,这样可以在代理服务器发生更改或添加新代理时更方便。

英文:

Here's the Montage's answer with explanation:

The requirement is to forward request through proxies in round-robin fashion

Since we are using http.Client to make request we can look at http.Client documentation to see if it provide any support for forwarding request through proxy when we look at the documentation we can see that it does support passing proxy which we can pass through http.Transport type which will be passed to http.Client. http.Transport takes proxy through Proxy field which takes in func that return *url.URL and error there are existing methods like http.ProxyURL and http.ProxyFromEnvironment provided within http package that we can use to pass proxies to http.Transport but the problem with these methods is that they only take a single proxy server which does not solve our problem at hand and hence we would require to create our own function which takes in multiple proxy servers urls and round-robin between them.

If we look at one of the existing method implemention as our base for creating our own method lets go with http.ProxyURL for our case the implementation can be found here. I have copied the implementation below

func ProxyURL(fixedURL *url.URL) func(*Request) (*url.URL, error) {
	return func(*Request) (*url.URL, error) {
		return fixedURL, nil
	}
}

we can see that its a simple closure which takes in single url and return a closure function which then intern return the url passed in as parameter. so we can take it base and create our own round-robin clouse function

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
	}
}

Lets go over the roundRobin function implementation it is a variadic function which takes in proxy url(s) in string format as argument, which internally gets converted to url.URL by parsing the string using url.Parse then using the parsed url.URL to create slice of urls []*url.URL which then being used to forward request in round-robin fashion

Complete working example can be found below:

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))
}

Playground

Another version

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)
	}
}

Playground

Note: It would be better to pass proxy urls from env variable which would help in case any proxy server changes or new are added

答案3

得分: 0

这是用于解析字符串的代码的Montage答案。

func roundRobin(serverProxy string) func(*http.Request) (*url.URL, error) {
    parts := strings.Split(serverProxy, ",")
    var urls []*url.URL
    for _, part := range parts {
        u, err := url.Parse(strings.TrimSpace(part))
        if err != nil {
            log.Fatal(err)
        }
        urls = append(urls, u)
    }
    var mu sync.Mutex
    var i int
    return func(r *http.Request) (*url.URL, error) {
        mu.Lock()
        i = (i + 1) % len(urls)
        u := urls[i]
        mu.Unlock()
        return u, nil
    }
}

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

请注意,这是一个用于在HTTP请求中实现轮询代理的代码示例。它将给定的服务器代理字符串解析为URL,并在每个请求中按顺序选择一个URL。

英文:

Here's the Montage's answer with code to parse a string.

func roundRobin(serverProxy string) func(*http.Request) (*url.URL, error) {
parts := strings.Split(serverProxy, ",")
var urls []*url.URL
for _, part := range parts {
u, err := url.Parse(strings.TrimSpace(part))
if err != nil {
log.Fatal(err)
}
urls = append(urls, u)
}
var mu sync.Mutex
var i int
return func(r *http.Request) (*url.URL, error) {
mu.Lock()
i = (i + 1) % len(urls)
u := urls[i]
mu.Unlock()
return u, nil
}
}
serverProxy := "http://user:password@123.45.67.89:3128, http://user:password@223.45.67.89:3128"
transport := &http.Transport{Proxy: roundRobin(serverProxy)}
client := &http.Client{Transport: transport}

huangapple
  • 本文由 发表于 2022年8月20日 08:06:05
  • 转载请务必保留本文链接:https://go.coder-hub.com/73423278.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定