如何在Go中实现多端口多路由监听并控制各监听组的启停状态?
多端口多路由独立启停的Go内置包实现
要实现多组带独立路由的端口监听,且能单独控制启停,用Go内置的http.ServeMux和http.Server就能搞定。每个监听组对应独立的路由复用器和服务器实例,配合context实现优雅启停。
核心改造点
- 为每个
location实例创建专属的http.ServeMux,用来注册该实例下的所有路由 - 用context管理服务器的生命周期,通过取消context触发优雅停机
- 维护服务注册表,方便定位和操作任意监听实例
修改后的代码示例
import ( "context" "log" "net/http" "sync" ) // 补充context相关字段的location结构体 type location struct { root string locationType int // loadBalancing、fileService等常量 hashRing *HashRing // 假设已定义的负载均衡结构 httpService *http.Server stopCtx context.Context stopCancel context.CancelFunc // 其他原有字段... } const ( loadBalancing = 1 fileService = 2 ) func (location *location) listen(mu *sync.Mutex, servicesPoll *map[string]*location) { // 1. 创建专属路由复用器 mux := http.NewServeMux() // 2. 注册该实例下的路由和处理函数 switch location.locationType { case loadBalancing: // 注册负载均衡相关路由 mux.HandleFunc("/proxy/", func(w http.ResponseWriter, r *http.Request) { location.hashRing.forward(w, r, mu) }) mux.HandleFunc("/status/", location.checkStatus) case fileService: // 注册文件服务相关路由 mux.HandleFunc("/static/", func(w http.ResponseWriter, r *http.Request) { location.getFile(w, r, mu) }) mux.HandleFunc("/upload/", location.uploadFile) } // 3. 创建带context的服务器实例 location.stopCtx, location.stopCancel = context.WithCancel(context.Background()) server := &http.Server{ Addr: location.root, Handler: mux, } location.httpService = server (*servicesPoll)[location.root] = location // 4. 启动服务器,同时监听停止信号 go func() { <-location.stopCtx.Done() // 优雅停机,等待现有请求处理完成 if err := server.Shutdown(context.Background()); err != nil { log.Printf("server %s shutdown error: %v", location.root, err) } }() err := server.ListenAndServe() if err != nil && err != http.ErrServerClosed { log.Printf("server %s listen error: %v", location.root, err) } // 停机后从注册表移除 mu.Lock() delete(*servicesPoll, location.root) mu.Unlock() } // 停止指定location的服务 func (location *location) Stop() { if location.stopCancel != nil { location.stopCancel() } } // 示例:停止所有服务 func StopAllServices(mu *sync.Mutex, servicesPoll *map[string]*location) { mu.Lock() defer mu.Unlock() for _, loc := range *servicesPoll { loc.Stop() } } // 假设的辅助方法 func (location *location) checkStatus(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusOK) w.Write([]byte("load balancer is running")) } func (location *location) uploadFile(w http.ResponseWriter, r *http.Request) { // 文件上传逻辑实现 }
关键说明
- 路由分离:每个
location的ServeMux独立管理自己的路由规则,不同端口/实例的路由互不干扰 - 优雅启停:通过
context.WithCancel生成的cancel函数触发Shutdown,服务器会停止接收新请求,等待现有请求处理完毕后退出 - 服务管理:
servicesPoll注册表记录所有运行中的服务,方便单独或批量启停
内容的提问来源于stack exchange,提问作者lvyonghuan
相关产品推荐
相关产品推荐

