2014-03-25 10 views

Antwort

5

Der Trick dabei ist, Ihre eigene net.Listener zu implementieren. Ich habe ein Beispiel für einen Listener here (siehe waitConn und WaitListener), der Verbindungen verfolgt (aber nicht beschränkt), die Sie als Inspiration für eine Implementierung verwenden können. Es wird in etwa so geformt sein:

type LimitedListener struct { 
    sync.Mutex 
    net.Listener 
    sem chan bool 
} 

func NewLimitedListener(count int, l net.Listener) *net.LimitedListener { 
    sem := make(chan bool, count) 
    for i := 0; i < count; i++ { 
     sem <- true 
    } 
    return &net.LimitedListener{ 
     Listener: l, 
     sem:  sem, 
    } 
} 

func (l *LimitedListener) Addr() net.Addr { /* ... */ } 
func (l *LimitedListener) Close() error { /* ... */ } 
func (l *LimitedListener) Accept() (net.Conn, err) { 
    <-l.sem // acquire 
    // l.Listener.Accept (on error, release before returning) 
    // wrap LimitedConn 
    return c, nil 
} 

type LimitedConn struct { /* ... */ } 

func (c *LimitedConn) Close() error { 
    /* ... */ 
    c.sem <- true // release 
} 

Wesentlichen, was dies tut, ist eine eigene Implementierung von net.Listener zu schaffen, die Sie Serve geben können, die nur Anrufe der zugrunde liegenden Accept, wenn es den Semaphor erwerben kann; Der so erworbene Semaphor wird erst freigegeben, wenn der (entsprechend verpackte) net.Conn geschlossen ist. Beachten Sie, dass diese Verwendung des Semaphors technisch in Bezug auf die go1.2 memory model; ein einfacherer Semaphor wird in future versions von Go legal sein.

+0

Vielen Dank für Ihre Antwort. Können Sie mir sagen, wie man den Server überwacht (Anzahl der aktiven Leerlaufverbindungen anzeigen) ??? –

+0

Speichern Sie die ursprüngliche Anzahl, und subtrahieren Sie len (l.sem) von diesem, um (sofort) zu erhalten, wie viele Verbindungen auf das Schließen warten. –

+0

Dies ist möglicherweise nicht sicher. Ich habe gelegentlich mehrere Aufrufe von Close() auf der gleichen Verbindung von net/http/Server gesehen. Dies ist ein undefiniertes Verhalten gemäß der io.Closer-Schnittstelle, und bei dieser Implementierung führt dies dazu, dass Close() -Aufrufe blockieren, wenn versucht wird, zu release (gelesen von chan). Erwägen Sie stattdessen, netutil.LimitListener zu verwenden, wie in einer anderen Antwort beschrieben; Es verfügt über einen Wrapper, um sicherzustellen, dass der Kanal nur einmal pro Verbindung gelesen wird. (Es übergibt jedoch immer noch die wiederholten Close-Aufrufe an den zugrunde liegenden Listener, was immer noch ein undefiniertes Verhalten ist.) –

1

Mit Hilfe des Channels können Sie die Anzahl der aktiven Verbindungen begrenzen.

1.Bei der Startzeit des Servers einen Kanal erstellen und die gleiche Anzahl an Grenzwerten (in Ihrem Fall 20) in den Kanal eingeben.

2.Remove ein Wert aus dem Kanal, während eine Anforderung dient.

Ein Beispiel aus dem Web

type limitHandler struct { 
    connc chan struct{} 
    handler http.Handler 
} 

func (h *limitHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) { 
    select { 
    case <-connc: 
     h.handler.ServeHTTP(w, req) 
     connc <- struct{}{} 
    default: 
     http.Error(w, "503 too busy", StatusServiceUnavailable) 
    } 
} 

func NewLimitHandler(maxConns int, handler http.Handler) http.Handler { 
    h := &limitHandler{ 
     connc: make(chan struct{}, maxConns), 
     handler: handler, 
    } 
    for i := 0; i < maxConns; i++ { 
     connc <- struct{}{} 
    } 
    return h 
} 
+0

'connc <- struct {} {}' sollte wahrscheinlich 'defer'-ed sein. Im Falle einer Panik (die von anderswo wiederhergestellt wird), würde der Verbindungs- "Schlitz" immer noch in den Pool zurückkehren. – justinas

+4

Technisch begrenzt dies nicht eingehende Verbindungen, es beschränkt nur, wie viele Antworten Sie gleichzeitig senden. Es ist ein nützliches Muster, obwohl es wahrscheinlich auf z.B. laden statt einer absoluten Anzahl von Verbindungen. –

9

können Sie verwenden, um die Funktion netutil.LimitListener herum zu wickeln net.Listener wenn Sie wollen Ihre eigenen Wrapper nicht implementieren: -

connectionCount := 20 

l, err := net.Listen("tcp", ":8000") 

if err != nil { 
    log.Fatalf("Listen: %v", err) 
} 

defer l.Close() 

l = netutil.LimitListener(l, connectionCount) 

log.Fatal(http.Serve(l, nil)) 
Verwandte Themen