Creating a Proxy Server with Go(codingcookies.com) |
Creating a Proxy Server with Go(codingcookies.com) |
I know it's possible to pull select() into Golang programs (I ended up having to, to write a fast port scanner), but Golang people look at you weirdly when you tell them you did that.
Is the default minimal stack segment size too big? Or the scheduler too heavyweight?
// The booleans representing the free active connection spaces.
spaces := make(chan bool, *maxConnections)
// Initialize the spaces
for i := 0; i < *maxConnections; i++ {
spaces <- true
}
}Is this really how people use go???
In this case, this particular part of the application is worth questioning, because the error condition isn't reasonable. Right now, if the proxy is full, it accepts a TCP connection from a client, and then it just ... stalls. It doesn't disconnect the client, it doesn't read from the client, it just hangs. So if a client were to actually connect to this proxy when it's full, they'd just open up a connection and wait.
Using sync/atomic package isn't common. Yes, you could do it, but it would be more common to just have a goroutine with a counter in it, and a select statement to serialize increment and decrement messages.
And then there are the actual handlers, which throw away the error information if there's an error actually writing to the connection.
You're right that sync/atomic could've taken care of this, I wasn't aware of that package and figured channels were the way to go in Go.
As for making the waiting chan buffered, the reason I wanted to keep track of pending connections and active connections is because I'd like to proxy from a high-power server to a low-power server such as a Raspberry Pi. I agree with you that it could have done without though.
Thanks for the tips! :-)
package main
import (
"net/http"
"net/http/httputil"
"net/url"
)
func main() {
target, _ := url.Parse("http://127.0.0.1:8000")
http.ListenAndServe(":80", httputil.NewSingleHostReverseProxy(target))
}
This will http proxy :80 to :8000.One of the last slides: http://talks.golang.org/2012/concurrency.slide#54
The "asynchronous pattern" thing is a religious canard. Golang devotees are (rightfully) happy to abandon event-structured programs in favor of programs that look like socket tutorial code but perform like event servers. But we're not talking about asynchronous control; we're talking about a synchronous loop. I've noticed this when talking to Golang people: they hear "select" or "poll" and automatically a switch goes off in their head that lights the "BAD!" lamp. I'm not sure that's valid.
Another example, which I gave upthread, was a high-speed port scanner; when I left it to the Golang scheduler to handle the sockets without Golang's timeout idiom, I quickly starved the program of sockets, because of the interaction between timeouts and the scheduler. I pulled select(2) into the program (for that one use only! Just to avoid using Golang timeouts for a simple connect(2) timeout) and the program not only ran quickly but properly handled the socket descriptors.
This isn't a critique of Golang, which I like working with. Rather, I'm criticizing a specific Golang idiom.