Golang动态注册处理器的实现方法

Golang动态注册处理器的实现方法 我想知道是否有人能为我指出一种动态注册处理程序的模式,这些处理程序可以根据一个列表来启用。

我已经编写了所有的处理程序,并且像这样构建了一个映射:

type handlers map[string]http.HandlerFunc

h := make(handlers)
h["a"] = handlerA
h["b"] = handlerB

然后,我遍历配置文件中的路由列表:

for routeName, route := range s.config.Routes {
	mux.Handle(route.Path, http.HandlerFunc(h[routeName]))
}

是否有更简洁/更好/更优雅/更符合惯用法的解决方案?非常感谢任何想法或意见。


更多关于Golang动态注册处理器的实现方法的实战教程也可以访问 https://www.itying.com/category-94-b0.html

5 回复

我很好奇,你正在处理什么样的问题,需要动态定义路由?

更多关于Golang动态注册处理器的实现方法的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


n0muh:

我很好奇是否有人能给我指点一种模式,用于动态注册处理程序,这些处理程序可以从一个列表中启用。

你在使用模板吗?

感谢@kync抽出时间。实际上,可能确实有另一种方法来解决我正在尝试解决的问题。

我们正在构建一个HTTP服务器,用于接收来自在不同供应商平台上配置的Webhook的请求。目标是构建所有供应商特定的处理器,以处理和转换这些请求,并将它们转发到另一个服务。我们希望能够在配置文件中基于每个处理器启用这些功能,这样就不会注册未被使用的端点。

我正在思考Logstash输入、过滤器和输出的配置与启用方式。在Logstash中,你会有一个如下所示的配置文件:

input {
  syslog {
    port => 12345
    syslog_field => "syslog"
  }
}

这样只有syslog输入会被启用,但你也可以这样运行它:

input {
  file {
    path => "/tmp/access_log"
    start_position => "beginning"
  }
  syslog {
    port => 12345
    syslog_field => "syslog"
  }
}

这样你就会同时启用syslog和文件读取器。

我可以从配置文件中提取输入,然后遍历它们,如果配置存在,就注册相应的处理器。

在你的解决方案中,直到你需要通过自定义规则来区分不同的供应商之前,都没有问题。我会这样做:

type StripeHandler struct {
	Active bool
}

func (s StripeHandler) Matches(r *http.Request) bool {
	// 你可以检查查询字符串中的键、主机名,或者任何你需要用来识别的信息
	if r.URL.Path == "/stripe" {
		return true
	}
	return false
}
func (s StripeHandler) IsActive() bool {
	return s.Active
}
func (s StripeHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	fmt.Println("stripe handled")
}

type PaypalHandler struct {
	Active bool
}

func (p PaypalHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	fmt.Println("paypal handled")
}
func (p PaypalHandler) IsActive() bool {
	return p.Active
}
func (p PaypalHandler) Matches(r *http.Request) bool {
	if r.URL.Path == "/paypal" {
		return true
	}
	return false
}

type VendorHandler interface {
	IsActive() bool
	Matches(r *http.Request) bool
	ServeHTTP(w http.ResponseWriter, r *http.Request)
}

type WebhookHandler struct {
	handlers []VendorHandler
}

func (wh *WebhookHandler) Register(h VendorHandler) {
	wh.handlers = append(wh.handlers, h)

}
func (wh *WebhookHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	for _, handler := range wh.handlers {
		if handler.IsActive() && handler.Matches(r) {
			handler.ServeHTTP(w, r)
			break
		}
	}
}

func main() {
	configs := map[string]bool{"stripe": true, "paypal": false}
	wh := WebhookHandler{}
	for key, active := range configs {
		if !active {
			continue
		}
		switch key {
		case "stripe":
			{
				wh.Register(StripeHandler{Active: true})
			}
		case "paypal":
			{
				wh.Register(PaypalHandler{Active: true})
			}
		}
	}
	uri, _ := url.Parse("https://play.golang.org/stripe")
	wh.ServeHTTP(nil, &http.Request{URL: uri})
	uri, _ = url.Parse("https://play.golang.org/paypal")
	wh.ServeHTTP(nil, &http.Request{URL: uri})
	fmt.Println()
}

我会采用上述解决方案,并不是因为它比你的更好,而是因为我喜欢尽可能地避免通过配置文件来配置服务。

https://play.golang.org/p/i099WLUK8_6

一种常见的动态注册处理器模式是使用中间件或包装函数来控制处理器的启用状态。以下是一个示例实现:

type RouteConfig struct {
    Path    string
    Enabled bool
    Handler string
}

type HandlerManager struct {
    handlers map[string]http.HandlerFunc
    config   []RouteConfig
}

func NewHandlerManager(config []RouteConfig) *HandlerManager {
    hm := &HandlerManager{
        handlers: make(map[string]http.HandlerFunc),
        config:   config,
    }
    
    // 注册所有处理器
    hm.handlers["a"] = handlerA
    hm.handlers["b"] = handlerB
    hm.handlers["c"] = handlerC
    
    return hm
}

func (hm *HandlerManager) RegisterRoutes(mux *http.ServeMux) {
    for _, route := range hm.config {
        if !route.Enabled {
            continue
        }
        
        handler, exists := hm.handlers[route.Handler]
        if !exists {
            continue
        }
        
        // 可选:添加中间件包装
        wrappedHandler := hm.wrapHandler(handler, route)
        mux.Handle(route.Path, wrappedHandler)
    }
}

func (hm *HandlerManager) wrapHandler(h http.HandlerFunc, route RouteConfig) http.HandlerFunc {
    return func(w http.ResponseWriter, r *http.Request) {
        // 这里可以添加通用的中间件逻辑
        // 例如:日志记录、认证、指标收集等
        h(w, r)
    }
}

// 使用示例
func main() {
    config := []RouteConfig{
        {Path: "/api/a", Enabled: true, Handler: "a"},
        {Path: "/api/b", Enabled: false, Handler: "b"},
        {Path: "/api/c", Enabled: true, Handler: "c"},
    }
    
    hm := NewHandlerManager(config)
    mux := http.NewServeMux()
    hm.RegisterRoutes(mux)
    
    http.ListenAndServe(":8080", mux)
}

另一种更灵活的方式是使用函数注册:

type Route struct {
    Path    string
    Handler http.HandlerFunc
    Enabled bool
}

func RegisterDynamicRoutes(mux *http.ServeMux, routes []Route) {
    for _, route := range routes {
        if route.Enabled {
            mux.Handle(route.Path, route.Handler)
        }
    }
}

// 使用闭包动态创建处理器
func createHandler(name string) http.HandlerFunc {
    return func(w http.ResponseWriter, r *http.Request) {
        fmt.Fprintf(w, "Handler %s is serving", name)
    }
}

func main() {
    mux := http.NewServeMux()
    
    routes := []Route{
        {Path: "/a", Handler: createHandler("a"), Enabled: true},
        {Path: "/b", Handler: createHandler("b"), Enabled: false},
        {Path: "/c", Handler: createHandler("c"), Enabled: true},
    }
    
    RegisterDynamicRoutes(mux, routes)
    http.ListenAndServe(":8080", mux)
}

如果需要更细粒度的控制,可以使用条件注册模式:

type HandlerRegistry struct {
    mu       sync.RWMutex
    handlers map[string]http.HandlerFunc
    enabled  map[string]bool
}

func (r *HandlerRegistry) Register(name string, handler http.HandlerFunc) {
    r.mu.Lock()
    defer r.mu.Unlock()
    r.handlers[name] = handler
}

func (r *HandlerRegistry) SetEnabled(name string, enabled bool) {
    r.mu.Lock()
    defer r.mu.Unlock()
    r.enabled[name] = enabled
}

func (r *HandlerRegistry) ApplyToMux(mux *http.ServeMux, pathPrefix string) {
    r.mu.RLock()
    defer r.mu.RUnlock()
    
    for name, handler := range r.handlers {
        if enabled, ok := r.enabled[name]; ok && enabled {
            mux.Handle(pathPrefix+name, handler)
        }
    }
}

这些模式都支持基于配置动态启用或禁用处理器,同时保持了代码的清晰性和可维护性。

回到顶部