I try to use one http client to make multiple requests on the same host through different proxy servers. It is important to make every new req through new proxy (round robin scheme).
This is my code sample
package main
import (
"fmt"
"net/http"
"net/url"
)
var client *http.Client
func main() {
roundRobin := NewRoundRobinProxy(
"http://myproxy1:8888",
"http://myproxy2:8888",
"http://myproxy3:8888")
client = &http.Client{
Transport: &http.Transport{
MaxConnsPerHost: 10,
DisableKeepAlives: false, // if it's true - it works fine, app really calls Proxy func on EACH req
Proxy: roundRobin.Proxy,
},
}
sendReq("https://www.binance.com")
sendReq("https://www.binance.com")
sendReq("https://www.binance.com")
sendReq("https://www.binance.com")
}
func sendReq(urlStr string) {
req, _ := http.NewRequest("GET", urlStr, nil)
resp, _ := client.Do(req)
resp.Body.Close()
fmt.Println("got resp from ", urlStr)
}
type RoundRobinProxy struct {
urls []*url.URL
cursor int
}
func NewRoundRobinProxy(urls ...string) *RoundRobinProxy {
p := &RoundRobinProxy{cursor: 0}
for _, v := range urls {
u, _ := url.Parse(v)
p.urls = append(p.urls, u)
}
return p
}
func (p *RoundRobinProxy) Proxy(*http.Request) (*url.URL, error) {
fmt.Println("i'm in proxy, cursor=", p.cursor)
u := p.urls[p.cursor]
if p.cursor < len(p.urls)-1 {
p.cursor++
} else {
p.cursor = 0
}
return u, nil
}
So if I run this code I expect i'm in proxy... message as times as I have requests (4). But in fact I see this picture:
i'm in proxy, cursor= 0
got resp from https://www.binance.com
got resp from https://www.binance.com
got resp from https://www.binance.com
got resp from https://www.binance.com
So it uses first proxy in the pool and then cached it somehow.
Yes, the one solution is to set DisableKeepAlives=true. In that case it works
i'm in proxy, cursor= 0
i'm in proxy, cursor= 1
got resp from https://www.binance.com
i'm in proxy, cursor= 2
i'm in proxy, cursor= 0
got resp from https://www.binance.com
i'm in proxy, cursor= 1
i'm in proxy, cursor= 2
got resp from https://www.binance.com
i'm in proxy, cursor= 0
i'm in proxy, cursor= 1
got resp from https://www.binance.com
There are more in proxy messages, than requests. But it doesn't matter (maybe some redirects done under the hood)
But it's important to reuse tcp connections to avoid handshake overhead on each request.
Are there any ideas besides using pool of clients (each with one proxy) in place of pool of proxies. I wish to find more straightforward and elegant solution)) thanks
This isn't a go issue, it's just the way keepalive works. When you are using TCP keepalive, it keeps the connection open - as you recognize, this lets you avoid some of the handshake overhead. But what you are connected to is the proxy - in this case, yes, the first one in the list.
What's happening:
You setup your client with a proxy function to return the correct URL mapping and keepalive enabled
You issue the request
Your client is returned the proxy address and connects to proxy 0
Subsequent requests to the same URL will go over that existing connection to proxy 0.
Since the connection from the client terminates at the specific proxy, that is what is being kept alive.
I would follow the suggestion of 1:1 client-to-proxy - then you can load-balance across the proxies while still using keepalive.
Related
I have a test case for a golang project that uses crypto/tls to start a TLS client and a server. My test accesses the ciphertexts exchanged by the client and server to run assertions.
I'm looking for a way to get the same ciphertexts across consecutive executions of the test.
The values of the negotiated keys and the plaintext returned by the server are not relevant to the test.
The test sample of tls sample could help you, define one zeroSource implemented Read interface for Rand of tls.config.
type zeroSource struct{}
func (zeroSource) Read(b []byte) (n int, err error) {
for i := range b {
b[i] = 0
}
// assign the same key to b
return len(b), nil
}
// server side
server.TLS = &tls.Config{
Rand: zeroSource{}, // for example only; don't do this.
}
// client side
client := &http.Client{
Transport: &http.Transport{
TLSClientConfig: &tls.Config{
Rand: zeroSource{}, // for reproducible output; don't do this.
},
},
}
I am learning golang and wanted to build a TCP port scanner with SOCKS5 proxies as a relay for mass scanning.
Although all of the S5 proxies are being checked for every target scan, sometimes there are some False positives - and I cannot find the reason why.
Preparing proxyDialer:
func create_socks5_tcp_dialer(socks5_addr string) proxy.Dialer {
//socks5_dialer_tcp, err := proxy.SOCKS5("tcp", socks5_addr, nil, proxy.Direct)
socks5_dialer_tcp, err := proxy.SOCKS5("tcp", socks5_addr, nil, &net.Dialer{Timeout: 5 * time.Second, KeepAlive: 5 * time.Second})
if err != nil {
fmt.Println("Error connecting to proxy:", err)
}
return socks5_dialer_tcp
}
Validating socks5 address:
func socks5_validator(socks5_addr, vps_opened, vps_closed string) (bool, string) {
/* Check if SOCKS5 proxy is valid.
1. Connect to the open port on the server under my control using proxy.
2. Connect to the closed port on the server under my control using proxy.
- If both checks are true then, SOCKS5 proxy is considered as valid.
- If one of the check is false, SOCKS5 proxy is considered as invalid.
3. Returns true/false and s5_addr.
*/
// Create SOCKS5 dialer
socks5_dialer_tcp := create_socks5_tcp_dialer(socks5_addr)
// Make connection using SOCKS5 proxy to the opened port on the vps.
conn_1, err := socks5_dialer_tcp.Dial("tcp", vps_opened)
// If it was successful and not generate any error then check1 is passed.
if err == nil {
//fmt.Println("CHECK 1: PASSED")
conn_1.Close()
// If error was generated then check is not passed and do not make check2.
} else {
//fmt.Println("CHECK 1: NOT PASSED")
return false, socks5_addr
}
// Make connection using SOCKS5 proxy to the closed port on the vps.
conn_2, err := socks5_dialer_tcp.Dial("tcp", vps_closed)
// If it was unsuccessful and error was generated then check2 is passed.
if err != nil {
//fmt.Println("CHECK 2: PASSED")
// If both checks were passed then return false.
return true, socks5_addr
// If error was not generated then check2 is not passed.
} else {
//fmt.Println("CHECK 2: NOT PASSED")
conn_2.Close()
return false, socks5_addr
}
}
Port scanning
s5_dialer_tcp := create_socks5_tcp_dialer(socks5_addr)
// Scan target using s5
conn, err := s5_dialer_tcp.Dial("tcp", target)
if err != nil {
//open
} else {
//closed
}
My question is:
Do I correctly scan TCP services through the SOCKS5 proxy and do I validate this proxy properly?
Link to the full code:
https://github.com/Karmaz95/crimson_prober
I don't think that these are actual false positives. Instead you are having the wrong assumptions of how these proxies will work: You assume that if the single check for a specific port open (connect success) and a specific port closed (connect failure) on a specific server at a specific time succeeds, then the proxy can be used to reliably check many arbitrary ports on arbitrary servers at arbitrary times.
This assumption is likely not valid, especially given that you seem to use proxies which are outside of your control.
A common behavior of such proxies is that they provide only restricted access, i.e. common ports like HTTP and HTTPS will work while other ports will be blocked. Proxies might also employ rate limiting, so they will simply deny access through the proxy after a while. And free proxies available in some lists often cease to work after a while.
I'm looking to add what's essentially a "status" boolean field to my gRPC Server.
The idea is that the state of the server could be set when certain conditions are met, so when any new gRPC requests come in, the response would be tailored accordingly.
I have a server struct that I'm using, and for this example, I've added a HappyHour bool field.
I have a goroutine that runs when the server is started, and every minute it checks the time of day. If the time of day is between 5-7pm, HappyHour gets set to true. Otherwise, it gets set to false.
type server struct {
HappyHour bool
mu sync.RWMutex
}
func (s *server) OrderDrink(ctx context.Context, req *bar.DrinkRequest) (*flatbuffers.Builder, error) {
drink := string(req.drink())
qty := req.qty()
// Code here to look up the price of the drink in the database
total := price*qty
// Use a mutex to check HappyHour status
s.mu.RLock()
defer mu.RLock()
if s.HappyHour == true {
total=total/2
}
b := flatbuffers.NewBuilder(0)
// Serialize the response and send it to the client
}
func main() {
lis, err := net.Listen("tcp", "0.0.0.0:50051")
if err != nil {
log.Fatal(err)
}
s := grpc.NewServer(grpc.CustomCodec(flatbuffers.FlatbuffersCodec{}))
var srv server
bar.RegisterBARServer(s, &srv)
// Monitor happy hour
go happyHour(&srv)
}
Things seem to be working from my initial testing, but I'm quite new to gRPC, and am a bit worried about using a mutex on the server struct.
Will there be negative performance impacts if there are many clients connecting to the server? If I'm reading or updating HappyHour, and the lock has been acquired, would this impact new users connecting or sending requests? Or would this only impact the actual HappyHour field?
I am trying to understand GoLang "Go" together with gRPC and to make a simple service scalable.
Lets say I have a client1 that calls a service1(adds numbers) that calls service2(determines if the result is prime), and service2 returns the result to service1 that returns the result to client1 all via gRPC.
When I use protocol buffers "proto3" and generate the Go code via protoc.
I get generated methods that call the service in one particular way.
I see no distinction to call the methods asynchronously "Go".
And the underlying call seems to be "Invoke" which I believe is synchronous,the call returns once a result is received.
How do I make service1 "performant", I know I can run this in a cluster and have copies, but that would mean I can only serve clients as per the amount of instances within the cluster.
I want a "single" service to be able to serve multiple clients(e.g. 1000) .
Here is a simple server and I am not sure if this is performant or not:
I do know that the getprime function does dial every time,
and this could probably be moved to make this dial persist and be re-used; But more importantly I want to make a simple performant scaleable service and get a good understanding.
(A)
Perhaps the whole design is incorrect and the service1 should just return
as soon as the instruction is received "ack", do the addition and send the next request to sercice2 which determines if the answer is prime or not; again service2 just responds with an acknowledgement of the request being received. Once prime is determined by the service2 a call is made to the client with an answer.
If (A) above is the better approach, then still please explain the bottlenecks below; what happens when multiple clients are processed?
The call to "Listen" does what, "blocks, or does not block", etc.
package main
import (
pb "demo/internal/pkg/proto_gen/calc"
"fmt"
"golang.org/x/net/context"
"google.golang.org/grpc"
"google.golang.org/grpc/reflection"
"log"
"net"
)
const (
port = ":8080"
)
type service struct {
}
func (s *service) Calculate(ctx context.Context, req *pb.Instruction) (*pb.Response, error) {
var answer float64
answer = req.Number1 + req.Number2
// call service prime
p := getprime(int(answer))
pa := pb.PrimeAnswer{Prime: p}
return &pb.Response{Answer: answer, Prime: &pa}, nil
}
const (
primeAddress = "127.0.0.1:8089"
)
func getprime(number int) bool {
conn, err := grpc.Dial(primeAddress, grpc.WithInsecure())
if err != nil {
log.Fatalf("Did not connect to prime service: %v", err)
}
defer conn.Close()
client := pb.NewPrimeServiceClient(conn)
p := pb.PrimeMessage{"", float64(number)}
r, err := client.Prime(context.Background(), &p)
if err != nil {
log.Fatalf("Call to prime service failed: %v", err)
}
return r.Prime
}
func main() {
lis, err := net.Listen("tcp", port)
if err != nil {
log.Fatalf("failed to listen: %v", err)
}
s := grpc.NewServer()
pb.RegisterCalculatorServer(s, &service{})
reflection.Register(s)
if err := s.Serve(lis); err != nil {
log.Fatalf("failed to serve: %v", err)
}
}
Thanks for your question. It is true that gRPC-Go is sync only; that is your Unary RPC(the one in your example) will return only when the RPC has finished (got a response from the server).
About performance:
The Dial operation establishes an underlying connection which may be expensive. So it not wise to do it every time getprime is called. A better way is to create a client, keep it around and make calls to the prime server on it. This way only first RPC incurs the cost of connection.
For each RPC request a server gets we launch a goroutine to process that request. So in general, this should scale fairly well.
About (A): It is not uncommon for a service handler to make an RPC call to yet another server and wait for its response before returning back.
Note that there's no way for a server to make call to the client.
To phrase what JimB said as an answer: "Synchronous" means that the function that makes the remote call waits for a reply before continuing, not that the whole server or client does. The server is normally multithreaded, even when processing synchronous calls; it can accept and work on a second call while it's responding to the first.
And similarly, if a client has multiple concurrent tasks that each have a gRPC call running, that won't block the process. Clients like that could include net/http servers serving end users, or gRPC servers handling multiple RPCs.
Where you might add explicit go statements is if you want to do something else from the specific function making the RPC call. For example, if you want to issue several RPC calls at once then wait for all their results to come in, you could write code following the examples of fan-out calls.
Hi I developed a little go server that does (at the moment) nothing but forwarding the request to a local service on the machine it is running.
So nearly the same as nginx as reverse proxy.
But I observed a really bad performance that even uses up all resources of the server and runs into timeouts on further requests.
I know that this cannot be as performant as nginx, but I don't think that it should be that slow.
Here is the server I use for forwarding the request:
package main
import (
"github.com/gorilla/mux"
"net/http"
"github.com/sirupsen/logrus"
"bytes"
"io/ioutil"
)
func main() {
router := mux.NewRouter()
router.HandleFunc("/", forwarder).Methods("POST")
server := http.Server{
Handler: router,
Addr: ":8443",
}
logrus.Fatal(server.ListenAndServeTLS("cert.pem", "key.pem"))
}
var client = &http.Client{}
func forwarder(w http.ResponseWriter, r *http.Request) {
// read request
body, err := ioutil.ReadAll(r.Body)
if err != nil {
logrus.Error(err.Error())
ServerError(w, nil)
return
}
// create forwarding request
req, err := http.NewRequest("POST", "http://localhost:8000", bytes.NewReader(body))
if err != nil {
logrus.Error(err.Error())
ServerError(w, nil)
return
}
resp, err := client.Do(req)
if err != nil {
logrus.Error(err.Error())
ServerError(w, nil)
return
}
// read response
respBody, err := ioutil.ReadAll(resp.Body)
if err != nil {
logrus.Error(err.Error())
ServerError(w, nil)
return
}
resp.Body.Close()
// return response
w.Header().Set("Content-Type", "application/json; charset=utf-8")
w.WriteHeader(resp.StatusCode)
w.Write(respBody)
}
From the client side I just measure the roundtrip time. And when I fire 100 Requests per second the response time goes up quite fast.
It starts with a response time of about 50ms. After 10 Seconds the response time is at 500ms. After 10 more seconds the response time is at 8000ms and so on, until I get timeouts.
When I use the nginx instead of my server there is no problem running 100 requests per second. Using nginx it stays at 40ms per each request.
Some observation:
using nginx: lsof -i | grep nginx
has no more than 2 connections open.
using my server the number of connection increases up to 500 and then the connections with state SYN_SENT increases and then the requets run into timeouts.
Another finding: I measured the delay of this code line:
resp, err := client.Do(req)
There is where most of the time is spent, but the could also just be because the go routines are starving!?
What I also tried:
r.Close = true (or KeepAlive = false)
I modified timeouts on the server side
I modified all this stuff on the http client used by my forward server (keepalive false, request.Close = true) etc.
I don't know why I got such a bad performance.
My guess is that go runs into problems because of the huge number of go routines. Maybe most of the time is used up scheduling this go routines and so the latency goes up?
I also tried to use the included httputil.NewSingleHostReverseProxy(). Performance is a little bit better, but still the same problem.
UPDATE:
Now I tried fasthttp:
package main
import (
"github.com/sirupsen/logrus"
"github.com/valyala/fasthttp"
)
func StartNodeManager() {
fasthttp.ListenAndServeTLS(":8443", "cert.pem", "key.pem", forwarder)
}
var client = fasthttp.Client{}
func forwarder(ctx *fasthttp.RequestCtx) {
resp := fasthttp.AcquireResponse()
req := fasthttp.AcquireRequest()
req.Header.SetMethod("POST")
req.SetRequestURI("http://127.0.0.1:8000")
req.SetBody(ctx.Request.Body())
err := client.Do(req, resp)
if err != nil {
logrus.Error(err.Error())
ctx.Response.SetStatusCode(500)
return
}
ctx.Response.SetBody(resp.Body())
fasthttp.ReleaseRequest(req)
fasthttp.ReleaseResponse(resp)
}
Little bit better but after 30 seconds the first timeouts arrive and the response time goes up to 5 seconds.
The root cause of the problem is GO http module is not handling connections to upstream in
a manged way, time is increasing because lots of connections are getting opened and they go into time_wait state.
So with number of increasing connections, you will get decrease in performance.
You just have to set
// 1000 what I am using
http.DefaultTransport.(*http.Transport).MaxIdleConns = 1000
http.DefaultTransport.(*http.Transport).MaxIdleConnsPerHost = 1000
in your forwarder and this will solve your problem.
By the way, use go std library reverse proxy, this will take away lot of headache.
But still for reverse proxy you need to set MaxIdleConns and MaxIdleConnsPerHost , in it's transport.
Follow the article given below.
First of all you should profile your app and find out where is the bottleneck.
Second I would be looking to way write code with less memory allocation in heap and more on stack.
Few ideas:
Do you need read request body for all request?
Do you need always read response body?
Can you pass body of client request to request to server? func NewRequest(method, url string, body io.Reader) (*Request, error)
Use sync.Pool
Consider using fasthttp as it creates less pressure to garbage collector
Check if your server uses same optimisation as Nginx. E.g. Keep-Alive, caching, etc.
Again profile and compare against Nginx.
Seems there is a lot of space for optimization.