16

我正在编写一个 Web 服务器,其中我需要在运行时注册处理程序。例如,“/create”将为所有 URL 创建一个新的处理程序,如“/123/*”等。我需要一个相应的“/destroy/123”,它将取消注册“/123/*”的处理程序。

这是处理“/create”的代码

package main
import (
    "fmt"
    "net/http"
)

type MyHandler struct {
    id int
}
func (hf *MyHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintln(w, r.URL.Path)
}

// Creates MyHandler instances and registers them as handlers at runtime
type HandlerFactory struct {
    handler_id int
}
func (hf *HandlerFactory) ServeHTTP(w http.ResponseWriter, r *http.Request) {
    hf.handler_id++
    handler := MyHandler{hf.handler_id}
    handle := fmt.Sprintf("/%d/", hf.handler_id)
    http.Handle(handle, &handler)
}

func main() {
    factory := HandlerFactory{0}
    http.Handle("/create", &factory)
    http.ListenAndServe("localhost:8080", nil)
}

我尝试通过嵌入来实现我自己的多路复用器,http.ServeMux但它将其模式到处理程序的映射保存在一个私有变量(ServeMux.m)中

4

3 回答 3

15

我要做的是创建一个自定义ServerMux. 从 复制代码GOROOT/src/pkg/net/http/server.go。它从第 837 行开始,到 939 结束。

自定义 ServerMux 需要一种注销方法。这应该很容易实现。只需抓住锁和del()地图条目。例如(所有代码未经测试):

// TODO: check if registered and return error if not.
// TODO: possibly remove the automatic permanent link between /dir and /dir/.
func (mux *MyMux) Deregister(pattern string) error {
    mux.mu.Lock()
    defer mux.mu.Unlock()
    del(mux.m, pattern)
    return nil
}

为了使用这个新的多路复用器,你可以这样做:

mux := newMux()
mux.Handle("/create", &factory)

srv := &http.Server {
    Addr: localhost:8080
    Handler: mux,
}
srv.ListenAndServe()

通过从另一个 goroutine调用来修改 muxderegister()是完全安全的,并且会修改ListenAndServe()路由消息的方式。

于 2012-07-31T15:35:52.710 回答
8

看来您已经接受了答案,但我想提出一个替代解决方案。

我质疑添加自定义复用器的必要性。在这个例子中,我使用了 gorilla muxer,但这只是因为我熟悉它的模式匹配。理论上,您可以匹配传入 URL 中的模式,而无需替换默认的 muxer。

我的代码在映射中维护处理程序函数(字符串:处理程序名称 => 函数字面量)...这适合使用默认的 muxers HandleFunc 方法。

样本输入/输出:

获取/注册/123

获取 /123
hello from123.

获取/销毁/123

获取 /123
[nothing]

package main

import (
    "code.google.com/p/gorilla/mux"
    "flag"
    "log"
    "net/http"
)

// Wraps server muxer, dynamic map of handlers, and listen port.
type Server struct {
    Dispatcher *mux.Router
    Urls       map[string]func(w http.ResponseWriter, r *http.Request)
    Port       string
}

// May the signal never stop.
func main() {
    //Initialize Server
    server := &Server{Port: "3000", Dispatcher: mux.NewRouter(), Urls: make(map[string]func(w http.ResponseWriter, r *http.Request))}

    var port = flag.String("port", "3000", "Default: 3000; Set the port for the web-server to accept incoming requests")
    flag.Parse()

    server.Port = *port
    log.Printf("Starting server on port: %s \n", server.Port)

    server.InitDispatch()
    log.Printf("Initializing request routes...\n")

    server.Start() //Launch server; blocks goroutine.
}

func (s *Server) Start() {

    http.ListenAndServe(":"+s.Port, s.Dispatcher)
}

// Initialize Dispatcher's routes.
func (s *Server) InitDispatch() {
    d := s.Dispatcher

    // Add handler to server's map.
    d.HandleFunc("/register/{name}", func(w http.ResponseWriter, r *http.Request) {
        //somewhere somehow you create the handler to be used; i'll just make an echohandler
        vars := mux.Vars(r)
        name := vars["name"]

        s.AddFunction(w, r, name)
    }).Methods("GET")

    d.HandleFunc("/destroy/{name}", func(w http.ResponseWriter, r *http.Request) {
        vars := mux.Vars(r)
        name := vars["name"]
        s.Destroy(name)
    }).Methods("GET")

    d.HandleFunc("/{name}", func(w http.ResponseWriter, r *http.Request) {
        //Lookup handler in map and call it, proxying this writer and request
        vars := mux.Vars(r)
        name := vars["name"]

        s.ProxyCall(w, r, name)
    }).Methods("GET")
}

func (s *Server) Destroy(fName string) {
    s.Urls[fName] = nil //remove handler
}

func (s *Server) ProxyCall(w http.ResponseWriter, r *http.Request, fName string) {
    if s.Urls[fName] != nil {
        s.Urls[fName](w, r) //proxy the call
    }
}

func (s *Server) AddFunction(w http.ResponseWriter, r *http.Request, fName string) {
    f := func(w http.ResponseWriter, r *http.Request) {
        w.Write([]byte("hello from" + fName))
    }

    s.Urls[fName] = f // Add the handler to our map
}
于 2012-08-07T18:33:44.223 回答
0

也许可以通过注册一个不返回任何内容的处理程序(不向 ResponseWriter 写入任何内容)或生成“未找到”类型的响应来“完成”取消注册。取决于您的要求和/或取消注册先前注册的处理程序的目的/效果。

于 2012-07-31T10:57:50.500 回答