Reputation: 377
I have a structural problem: I can't figure out how I can do with the way I built it up now (I can find solutions where I can see it work with other models).
I'm using standard net/http
, and I initiate my server with the following code:
gv := GlobalVars{
jobs: make(chan QueueElement),
appConfig: appConfig,
}
go worker(&gv)
server := http.Server{
Handler: &gv,
Addr: ":" + appConfig.Port,
}
log.Fatal(server.ListenAndServe())
Then I have one handler that checks for all the routes in a case:
func (gv *GlobalVars) ServeHTTP(w http.ResponseWriter, r *http.Request) {
}
The application is starting up some APIs that pass jobs to a queue.
My issue is, I need to start up 3 of these (different queues, different config) but same globalvar structure.
But this one only has one Handler that I can set - how would I add multiple handlers (I don't want multiple servers, has to run on the same port) that can understand its addressing different globalvars variables?
server := http.Server{
Handler: &gv,
Addr: ":" + appConfig.Port,
}
Upvotes: 7
Views: 11538
Reputation: 3350
Have a look at the net.http.ServeMux type:
ServeMux is an HTTP request multiplexer. It matches the URL of each incoming request against a list of registered patterns and calls the handler for the pattern that most closely matches the URL.
A ServeMux is itself an http.Handler
and multiplexes to different subhandlers by request route.
From what I understand of your question, you want to have different handlers on the same server, and each handler addresses a different queue with a different config.
Using a ServeMux one can achieve that easily:
gv1 := GlobalVars{
jobs: make(chan QueueElement),
appConfig: appConfig1,
}
gv2 := GlobalVars{
jobs: make(chan QueueElement),
appConfig: appConfig2,
}
gv3 := GlobalVars{
jobs: make(chan QueueElement),
appConfig: appConfig3,
}
sm := http.NewServeMux()
// Let gv{1,2,3} handle routes{1,2,3} respectively
sm.Handle("/route1", &gv1)
sm.Handle("/route2", &gv2)
sm.Handle("/route3", &gv3)
// Register the ServeMux as the sole Handler. It will delegate to the subhandlers.
server := http.Server{
Handler: sm,
Addr: ":" + globalAppConfig.Port,
}
Note, you don't have to construct an http.Server
by yourself. If you just need one server, you can use http package level functions http.ListenAndServe and http.Handle which take care of creating a Server and default ServeMux for you.
// same GlobalVars as above
// ...
// Instead of creating a ServeMux we can use the global DefaultServeMux
http.Handle("/route1", &gv1)
http.Handle("/route2", &gv2)
http.Handle("/route3", &gv3)
// Calling the package level ListenAndServe uses the single global server.
// Passing nil as the Handler uses the DefaultServeMux as Handler on which we registered the Handlers above.
log.Fatal(http.ListenAndServe(":" + globalAppConfig.Port, nil)
UPDATE
A small example of the standard ServeMux
with two Handler
s serving 3 routes
// Keeping this type simple for the example
type GlobalVars struct {
appConfig string
}
// This method makes every GlobalVars a net.http.Handler
func (gv *GlobalVars) ServeHTTP(w http.ResponseWriter, req *http.Request) {
fmt.Fprintf(w, "%s here. Receiving request for %s\n", gv.appConfig, req.URL.Path)
}
func main() {
gv1 := &GlobalVars{
appConfig: "gv1",
}
gv2 := &GlobalVars{
appConfig: "gv2",
}
// Handle requires a route and a Handler, our gvs are Handlers.
// gv1 handles two routes, while gv2 handles only one.
http.Handle("/route1", gv1)
http.Handle("/route2", gv1)
http.Handle("/route3", gv2)
log.Fatal(http.ListenAndServe(":8000", nil))
}
If I call all three routes after one another, I will receive the following responses:
$ curl localhost:8000/route1
gv1 here. Receiving request for /route1
$ curl localhost:8000/route2
gv1 here. Receiving request for /route2
$ curl localhost:8000/route3
gv2 here. Receiving request for /route3
This shows how to use stateful variables as Handlers (e.g. variables of type GlobalVars).
NOTE: The handler method ServeHTTP
has a pointer receiver for GlobalVars
. This means the method could potentially change the GlobalVars
variable. HTTP Handlers are executed concurrently (imagine multiple requests to the same handler in a very short time period, you'll want to stay as responsive as possible). This example only reads the appConfig value, so it's fine. Once a write to the variable/field comes into the mix, however, you'll need proper synchronization.
Upvotes: 11