Go net/http单端口绑定多Handler对应不同GlobalVars的实现疑问
The key here is to use Go's built-in http.ServeMux (the standard router) as your top-level handler, which will route incoming requests to the appropriate GlobalVars instance based on a path prefix. This way, you can run all three instances on the same port while keeping their state (queues, configs) completely separate.
Step-by-Step Implementation
First, create your three independent GlobalVars instances and start their worker goroutines:
// Initialize three separate instances with unique configs and queues gv1 := GlobalVars{ jobs: make(chan QueueElement), appConfig: config1, // Your first instance's configuration } go worker(&gv1) gv2 := GlobalVars{ jobs: make(chan QueueElement), appConfig: config2, // Your second instance's configuration } go worker(&gv2) gv3 := GlobalVars{ jobs: make(chan QueueElement), appConfig: config3, // Your third instance's configuration } go worker(&gv3)
Next, set up a ServeMux to route requests to each instance. Use http.StripPrefix to remove the instance-specific path prefix from the request URL—this ensures your existing ServeHTTP logic works unchanged (it will see the same paths it was designed to handle):
// Create a main router mux := http.NewServeMux() // Register each GlobalVars instance under a unique path prefix mux.Handle("/instance1/", http.StripPrefix("/instance1", &gv1)) mux.Handle("/instance2/", http.StripPrefix("/instance2", &gv2)) mux.Handle("/instance3/", http.StripPrefix("/instance3", &gv3))
Finally, start your server with the ServeMux as the top-level handler:
// Start the server on the shared port server := http.Server{ Handler: mux, Addr: ":" + sharedPort, // Use the same port for all instances } log.Fatal(server.ListenAndServe())
How This Works
- The
ServeMuxacts as a traffic director: when a request comes in (e.g.,/instance1/api/submit), it routes it to the correspondingGlobalVarsinstance. http.StripPrefixmodifies the request's URL path to remove the instance prefix (so/instance1/api/submitbecomes/api/submit), meaning your existingServeHTTPbranch logic doesn't need any changes.- Each
GlobalVarsinstance maintains its own queue and config, so their workers operate independently—exactly what you need.
Alternative Routing Strategies
If path prefixes aren't your preferred approach, here are two other options:
Subdomain-Based Routing
Route requests based on the subdomain in the Host header:
func mainHandler(gv1, gv2, gv3 *GlobalVars) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { switch r.Host { case "instance1.yourdomain.com": gv1.ServeHTTP(w, r) case "instance2.yourdomain.com": gv2.ServeHTTP(w, r) case "instance3.yourdomain.com": gv3.ServeHTTP(w, r) default: http.Error(w, "Unrecognized instance", http.StatusBadRequest) } } } // Use this handler in your server setup server := http.Server{ Handler: mainHandler(&gv1, &gv2, &gv3), Addr: ":" + sharedPort, }
Note: This requires DNS setup for your subdomains, or modifying your local hosts file for testing.
Header-Based Routing
Use a custom request header (e.g., X-Instance-ID) to direct requests:
func mainHandler(gv1, gv2, gv3 *GlobalVars) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { instanceID := r.Header.Get("X-Instance-ID") switch instanceID { case "1": gv1.ServeHTTP(w, r) case "2": gv2.ServeHTTP(w, r) case "3": gv3.ServeHTTP(w, r) default: http.Error(w, "Invalid instance ID", http.StatusBadRequest) } } }
This is less visible in URLs but requires clients to set the header correctly.
Recommendation
The path prefix approach is the most straightforward and REST-compliant option here—it's easy to test, integrates with existing clients, and doesn't require extra setup like DNS or custom headers.
内容的提问来源于stack exchange,提问作者MdTp

