2012-07-31 30 views
5

我正在寫一個Web服務器,其中我需要在運行時註冊處理程序。例如。 「/ create」會爲所有網址創建一個新的處理程序,如「/ 123/*」等等。我需要一個相應的「/ destroy/123」來取消註冊「/ 123/*」的處理程序。如何註銷net/http中的處理程序?

下面是處理代碼「/創建」

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

回答

13

我會做的是創建一個自定義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 
} 

爲了使用這種新的多路複用器,你會做這樣的事情:通過從其他的goroutine調用deregister()

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

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

修改複用是完全安全的,修改ListenAndServe()路由消息的方式。

0

其模式與處理器映射也許註銷可以通過註冊一個不返回任何內容的處理程序(不向任何ResponseWriter寫入任何內容)或生成「未找到」類型的響應來「完成」。取決於您先前註冊的處理程序取消註冊的要求和/或目的/效果。

+2

令人擔憂的是,處理程序註冊表將隨着時間的推移不斷增長,並且由於處理器查找日益昂貴而導致請求週轉速度放慢。 – 2012-07-31 12:01:21

6

看來你已經接受了一個答案,但我想提出一個備用解決方案。

我質疑是否需要添加自定義複用器。在這個例子中,我使用的是大猩猩複用器,但這只是因爲我熟悉它的模式匹配。從理論上講,您可以匹配傳入URL中的模式,而無需更換默認的複用器。

我的代碼維護處理函數在一個映射(字符串:處理程序名稱=>函數文字)...這適合使用默認的muxers HandleFunc方法。

樣品輸入/輸出:

GET /註冊/ 123

GET/123
hello from123.

GET /破壞/ 123

GET/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 
} 
+0

您的解決方案非常好。如果基本的URL結構解釋是標準庫的一部分,這可能是我所做的。 我的要求目前還不夠複雜,無法引入新軟件包,但我懷疑它不需要太多迭代,因此我當時可能會去大猩猩。 – 2012-08-29 02:05:11