2017-08-26 2 views
2

Ich habe eine Struktur, die sync.Pool verwendet.Ist es sicher, sync.Pool Referenz als Kontextwert zu verwenden?

Ist es sicher, diese Referenz als Kontextwert zu verwenden?

type User struct { 
    ID string 
} 

var userPool = sync.Pool{ 
    New: func() interface{} { 
     return &User{} 
    }, 
} 

func getUser() *User { 
    return userPool.Get().(*User) 
} 

func recycleUser(user *User) { 
    userPool.Put(user) 
} 

Die Benutzerstruktur wird aus dem Pool in Middleware abgerufen.

func middleware(next http.Handler) http.Handler { 
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 
     // get user from pool 
     user := getUser() 
     // user must be recycled 
     ctx := context.WithValue(r.Context(), "user", user) 
    } 
} 

Und in Handler recycelt.

func getUser(w http.ResponseWriter, r *http.Request) { 
    user := r.Context().Value("user").(*User) 

    // TODO: do something with user 

    // put user struct back into pool 
    recycleUser(user) 
} 

Edit:

Meine Frage ist, mehr darüber, wie Kontext befasst sich mit Zeiger auf mein Objekt. Macht es eine Kopie? Ist es sicher, nicht-primitive Objekte mit Kontext zu verwenden?

Antwort

0

Es gibt drei Punkte zu machen:

Verwendung von sync.Pool

Ihre Nutzung des Pools ist wie vorgesehen. Von golang Documentation

Ein Pool ist sicher für die gleichzeitige Verwendung durch mehrere goroutines.

Abrufen von Benutzer

getUser verwendet func (p *Pool) Get() interface{}, die die zurückgegebenen Artikel aus dem Pool entfernt, danach können Sie tun, was Sie mit diesem Wert gefallen. In Ihrem Fall ist das ein *User. Ob die zugehörige Ressource sicher zu verwenden ist, hängt davon ab, wie Sie diese Werte im Rest des Programms verwenden.

Recycling Benutzer

recycleUser im Hander Aufruf ist potentiell gefährlich, abhängig von Ihrer Umgebung.

Was könnte möglicherweise schiefgehen?

Nach recycleUser zurückgegebene *User zurück in den Pool, könnte es abgerufen und von einer anderen Goroutine sofort verwendet werden. Gleichzeitig ist *User jedoch immer noch in dem Kontext gespeichert, der der Anfrage zugeordnet ist. Es kommt also darauf an, ob eine der Funktionen Ihrer Middleware den *User aus dem Kontext verwendet und ob sie den Wert *User speichern. Oder wenn Sie später einen Code nach dem recycleUser hinzufügen, der den Wert *User verwendet. Nach dem recycleUser Aufruf funktionieren alle diese Verwendungen wahrscheinlich auf dem falschen Benutzer, der bereits von einer anderen Anforderung verwandt wird.

Wie diese Probleme

  1. Codierung Konvention

    • lösen, wenn der Benutzer in Middleware-Funktionen abrufen, kann es nicht
    • speichern, wenn der Benutzer in Middleware-Funktionen abrufen, Verwenden Sie es nicht nach dem Aufruf des nächsten Handlers in der Kette.
    • nicht nennen recycleUser in Middleware-Funktionen
    • die Benutzer in dem Blatt Handler abrufen (letzten Handler in der Kette, ruft keine weiteren Handler den Kontext propagieren und damit die *User) und defer recycleUser(user) verwenden die zurück zu setzen *User im Pool und machen es unmöglich, dass später hinzugefügt Code verwendet *User nach dem Aufruf an recycleUser(user).
    • in der Blatt-Handler, verwenden Sie nicht *User in Code, der von einigen defer Verwendung im Blatt Handler aufgerufen wird, außer recycleUser.
  2. die Kodierung Konvention mit technischen Mitteln zu gewährleisten. Meine einzige Idee hier wäre, die *User innerhalb einer Struktur zu platzieren und nur diese Struktur für eine Anfrage zu verwenden und sie als leer zu markieren, sobald *User in den Pool zurückgelegt wird. Machen Sie alle Zugriffsmethoden dieser Struktur auf Leerheit und Panik/log/was auch immer prüfen, wenn auf eine leere Struktur zugegriffen wird. Platzieren Sie im Kontext einen Zeiger auf diese Struktur. Aber das ist wahrscheinlich sinnlos, da Sie diese Struktur nun für jede Anfrage zuweisen, anstatt eine User, die wahrscheinlich war, was Sie versucht haben zu vermeiden.

Eine geringfügige Bemerkung

Sie wahrscheinlich Ihre Middleware-Funktion wie zu lesen bedeutet:

func middleware(next http.Handler) http.Handler { 
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 
     // get user from pool 
     user := getUser() 
     // user must be recycled 
     ctx := context.WithValue(r.Context(), "user", user) 
     // probably do something more 
     next(w, r.WithContext(ctx)) 
    } 
} 
Verwandte Themen