mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-08-13 13:55:41 +00:00
optimize requestcontext: use RWMutex and atomic.Value
This commit is contained in:
parent
328e3a8ab9
commit
564d53f71b
@ -543,6 +543,7 @@ staging/src/k8s.io/apiserver/pkg/endpoints/handlers
|
|||||||
staging/src/k8s.io/apiserver/pkg/endpoints/handlers/negotiation
|
staging/src/k8s.io/apiserver/pkg/endpoints/handlers/negotiation
|
||||||
staging/src/k8s.io/apiserver/pkg/endpoints/metrics
|
staging/src/k8s.io/apiserver/pkg/endpoints/metrics
|
||||||
staging/src/k8s.io/apiserver/pkg/endpoints/openapi/testing
|
staging/src/k8s.io/apiserver/pkg/endpoints/openapi/testing
|
||||||
|
staging/src/k8s.io/apiserver/pkg/endpoints/request
|
||||||
staging/src/k8s.io/apiserver/pkg/endpoints/testing
|
staging/src/k8s.io/apiserver/pkg/endpoints/testing
|
||||||
staging/src/k8s.io/apiserver/pkg/features
|
staging/src/k8s.io/apiserver/pkg/features
|
||||||
staging/src/k8s.io/apiserver/pkg/registry/generic
|
staging/src/k8s.io/apiserver/pkg/registry/generic
|
||||||
|
@ -18,7 +18,7 @@ package request
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
stderrs "errors"
|
"errors"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
@ -83,7 +83,7 @@ func NewDefaultContext() Context {
|
|||||||
func WithValue(parent Context, key interface{}, val interface{}) Context {
|
func WithValue(parent Context, key interface{}, val interface{}) Context {
|
||||||
internalCtx, ok := parent.(context.Context)
|
internalCtx, ok := parent.(context.Context)
|
||||||
if !ok {
|
if !ok {
|
||||||
panic(stderrs.New("Invalid context type"))
|
panic(errors.New("Invalid context type"))
|
||||||
}
|
}
|
||||||
return context.WithValue(internalCtx, key, val)
|
return context.WithValue(internalCtx, key, val)
|
||||||
}
|
}
|
||||||
|
@ -20,6 +20,7 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"net/http"
|
"net/http"
|
||||||
"sync"
|
"sync"
|
||||||
|
"sync/atomic"
|
||||||
|
|
||||||
"github.com/golang/glog"
|
"github.com/golang/glog"
|
||||||
)
|
)
|
||||||
@ -40,37 +41,62 @@ type RequestContextMapper interface {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type requestContextMap struct {
|
type requestContextMap struct {
|
||||||
contexts map[*http.Request]Context
|
// contexts contains a request Context map
|
||||||
lock sync.Mutex
|
// atomic.Value has a very good read performance compared to sync.RWMutex
|
||||||
|
// almost all requests have 3-4 context updates associated with them,
|
||||||
|
// and they can use only read lock to protect updating context, which is of higher performance with higher burst.
|
||||||
|
contexts map[*http.Request]*atomic.Value
|
||||||
|
lock sync.RWMutex
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewRequestContextMapper returns a new RequestContextMapper.
|
// NewRequestContextMapper returns a new RequestContextMapper.
|
||||||
// The returned mapper must be added as a request filter using NewRequestContextFilter.
|
// The returned mapper must be added as a request filter using NewRequestContextFilter.
|
||||||
func NewRequestContextMapper() RequestContextMapper {
|
func NewRequestContextMapper() RequestContextMapper {
|
||||||
return &requestContextMap{
|
return &requestContextMap{
|
||||||
contexts: make(map[*http.Request]Context),
|
contexts: make(map[*http.Request]*atomic.Value),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *requestContextMap) getValue(req *http.Request) (*atomic.Value, bool) {
|
||||||
|
c.lock.RLock()
|
||||||
|
defer c.lock.RUnlock()
|
||||||
|
value, ok := c.contexts[req]
|
||||||
|
return value, ok
|
||||||
|
}
|
||||||
|
|
||||||
|
// contextWrap is a wrapper of Context to prevent atomic.Value to be copied
|
||||||
|
type contextWrap struct {
|
||||||
|
Context
|
||||||
|
}
|
||||||
|
|
||||||
// Get returns the context associated with the given request (if any), and true if the request has an associated context, and false if it does not.
|
// Get returns the context associated with the given request (if any), and true if the request has an associated context, and false if it does not.
|
||||||
// Get will only return a valid context when called from inside the filter chain set up by NewRequestContextFilter()
|
// Get will only return a valid context when called from inside the filter chain set up by NewRequestContextFilter()
|
||||||
func (c *requestContextMap) Get(req *http.Request) (Context, bool) {
|
func (c *requestContextMap) Get(req *http.Request) (Context, bool) {
|
||||||
c.lock.Lock()
|
value, ok := c.getValue(req)
|
||||||
defer c.lock.Unlock()
|
if !ok {
|
||||||
context, ok := c.contexts[req]
|
return nil, false
|
||||||
return context, ok
|
}
|
||||||
|
|
||||||
|
if context, ok := value.Load().(contextWrap); ok {
|
||||||
|
return context.Context, ok
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, false
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update maps the request to the given context.
|
// Update maps the request to the given context.
|
||||||
// If no context was previously associated with the request, an error is returned and the context is ignored.
|
// If no context was previously associated with the request, an error is returned and the context is ignored.
|
||||||
func (c *requestContextMap) Update(req *http.Request, context Context) error {
|
func (c *requestContextMap) Update(req *http.Request, context Context) error {
|
||||||
c.lock.Lock()
|
value, ok := c.getValue(req)
|
||||||
defer c.lock.Unlock()
|
if !ok {
|
||||||
if _, ok := c.contexts[req]; !ok {
|
return errors.New("no context associated")
|
||||||
return errors.New("No context associated")
|
|
||||||
}
|
}
|
||||||
// TODO: ensure the new context is a descendant of the existing one
|
wrapper, ok := value.Load().(contextWrap)
|
||||||
c.contexts[req] = context
|
if !ok {
|
||||||
|
return errors.New("value type does not match")
|
||||||
|
}
|
||||||
|
wrapper.Context = context
|
||||||
|
value.Store(wrapper)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -83,7 +109,10 @@ func (c *requestContextMap) init(req *http.Request, context Context) bool {
|
|||||||
if _, exists := c.contexts[req]; exists {
|
if _, exists := c.contexts[req]; exists {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
c.contexts[req] = context
|
|
||||||
|
value := &atomic.Value{}
|
||||||
|
value.Store(contextWrap{context})
|
||||||
|
c.contexts[req] = value
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user