Replace router svcAddrUpdateLocks with new throttler package for code readability&reusability (#1047)
Rename svcAddrUpdateLock to Throttler and move it to the top-level directory as a package for code import.
This commit is contained in:
+75
-54
@@ -39,6 +39,7 @@ import (
|
||||
"github.com/fission/fission/crd"
|
||||
executorClient "github.com/fission/fission/executor/client"
|
||||
"github.com/fission/fission/redis"
|
||||
"github.com/fission/fission/throttler"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -46,56 +47,63 @@ const (
|
||||
X_FORWARDED_HOST = "X-Forwarded-Host"
|
||||
)
|
||||
|
||||
type tsRoundTripperParams struct {
|
||||
timeout time.Duration
|
||||
timeoutExponent int
|
||||
keepAlive time.Duration
|
||||
type (
|
||||
functionHandler struct {
|
||||
fmap *functionServiceMap
|
||||
frmap *functionRecorderMap
|
||||
trmap *triggerRecorderMap
|
||||
executor *executorClient.Client
|
||||
function *metav1.ObjectMeta
|
||||
httpTrigger *crd.HTTPTrigger
|
||||
functionMetadataMap map[string]*metav1.ObjectMeta
|
||||
fnWeightDistributionList []FunctionWeightDistribution
|
||||
tsRoundTripperParams *tsRoundTripperParams
|
||||
recorderName string
|
||||
isDebugEnv bool
|
||||
svcAddrUpdateThrottler *throttler.Throttler
|
||||
}
|
||||
|
||||
// maxRetires is the max times for RetryingRoundTripper to retry a request.
|
||||
// Default maxRetries is 10, which means router will retry for
|
||||
// up to 10 times and abort it if still not succeeded.
|
||||
maxRetries int
|
||||
tsRoundTripperParams struct {
|
||||
timeout time.Duration
|
||||
timeoutExponent int
|
||||
keepAlive time.Duration
|
||||
|
||||
// svcAddrRetryCount is the max times for RetryingRoundTripper to retry with a specific service address
|
||||
// Router sends requests to a specific service address for each function.
|
||||
// A service address is considered as an invalid one if amount of non-network
|
||||
// errors router received is higher than svcAddrRetryCount. In this situation,
|
||||
// remove it from cache and try to get a new one from executor.
|
||||
// Default svcAddrRetryCount is 5.
|
||||
svcAddrRetryCount int
|
||||
}
|
||||
// maxRetires is the max times for RetryingRoundTripper to retry a request.
|
||||
// Default maxRetries is 10, which means router will retry for
|
||||
// up to 10 times and abort it if still not succeeded.
|
||||
maxRetries int
|
||||
|
||||
type functionHandler struct {
|
||||
fmap *functionServiceMap
|
||||
frmap *functionRecorderMap
|
||||
trmap *triggerRecorderMap
|
||||
executor *executorClient.Client
|
||||
function *metav1.ObjectMeta
|
||||
httpTrigger *crd.HTTPTrigger
|
||||
functionMetadataMap map[string]*metav1.ObjectMeta
|
||||
fnWeightDistributionList []FunctionWeightDistribution
|
||||
tsRoundTripperParams *tsRoundTripperParams
|
||||
recorderName string
|
||||
isDebugEnv bool
|
||||
svcAddrUpdateLocks *svcAddrUpdateLocks
|
||||
}
|
||||
// svcAddrRetryCount is the max times for RetryingRoundTripper to retry with a specific service address
|
||||
// Router sends requests to a specific service address for each function.
|
||||
// A service address is considered as an invalid one if amount of non-network
|
||||
// errors router received is higher than svcAddrRetryCount. In this situation,
|
||||
// remove it from cache and try to get a new one from executor.
|
||||
// Default svcAddrRetryCount is 5.
|
||||
svcAddrRetryCount int
|
||||
}
|
||||
|
||||
// A layer on top of http.DefaultTransport, with retries.
|
||||
type RetryingRoundTripper struct {
|
||||
funcHandler *functionHandler
|
||||
}
|
||||
// A layer on top of http.DefaultTransport, with retries.
|
||||
RetryingRoundTripper struct {
|
||||
funcHandler *functionHandler
|
||||
}
|
||||
|
||||
// To keep the request body open during retries, we create an interface with Close operation being a no-op.
|
||||
// Details : https://github.com/flynn/flynn/pull/875
|
||||
fakeCloseReadCloser struct {
|
||||
io.ReadCloser
|
||||
}
|
||||
|
||||
svcEntryRecord struct {
|
||||
svcUrl *url.URL
|
||||
fromCache bool
|
||||
}
|
||||
)
|
||||
|
||||
func init() {
|
||||
// just seeding the random number for getting the canary function
|
||||
rand.Seed(time.Now().UnixNano())
|
||||
}
|
||||
|
||||
// To keep the request body open during retries, we create an interface with Close operation being a no-op.
|
||||
// Details : https://github.com/flynn/flynn/pull/875
|
||||
type fakeCloseReadCloser struct {
|
||||
io.ReadCloser
|
||||
}
|
||||
|
||||
func (w *fakeCloseReadCloser) Close() error {
|
||||
return nil
|
||||
}
|
||||
@@ -228,7 +236,8 @@ func (roundTripper RetryingRoundTripper) RoundTrip(req *http.Request) (resp *htt
|
||||
return nil, fission.MakeError(http.StatusInternalServerError, err.Error())
|
||||
}
|
||||
|
||||
// retry to get service url again
|
||||
// service url maybe nil if router cannot find one in cache,
|
||||
// so here we retry to get service url again
|
||||
if serviceUrl == nil {
|
||||
time.Sleep(executingTimeout)
|
||||
continue
|
||||
@@ -479,27 +488,34 @@ func (fh *functionHandler) getServiceEntry() (serviceUrl *url.URL, serviceUrlFro
|
||||
|
||||
// cache miss or nil entry in cache
|
||||
|
||||
// To prevent multiple update requests will be sent to executor and make executor overloaded,
|
||||
// the first goroutine is responsible to update the service url entry, all other goroutines
|
||||
// for the same function will wait until first goroutine finished.
|
||||
serviceUrl, serviceUrlFromCache, err = fh.svcAddrUpdateLocks.RunOnce(
|
||||
fh.function,
|
||||
func(firstToTheLock bool) (u *url.URL, fromCache bool, err error) {
|
||||
|
||||
// Use throttle to limit the total amount of requests sent
|
||||
// to the executor to prevent it from overloaded.
|
||||
recordObj, err := fh.svcAddrUpdateThrottler.RunOnce(
|
||||
crd.CacheKey(fh.function),
|
||||
func(firstToTheLock bool) (interface{}, error) {
|
||||
var u *url.URL
|
||||
// Get service entry from executor and update cache if its the first goroutine
|
||||
if firstToTheLock { // first to the service url
|
||||
log.Printf("Calling getServiceForFunction for function: %s", fh.function.Name)
|
||||
u, err = fh.getServiceEntryFromExecutor()
|
||||
if err == nil && u != nil {
|
||||
// add the address in router's cache
|
||||
log.Printf("Assigning service url: %s for function: %s", u, fh.function.Name)
|
||||
fh.fmap.assign(fh.function, u)
|
||||
if err != nil {
|
||||
log.Printf("Error getting service url from executor: %v", err)
|
||||
return nil, err
|
||||
}
|
||||
// add the address in router's cache
|
||||
log.Printf("Assigning service url: %s for function: %s", u, fh.function.Name)
|
||||
fh.fmap.assign(fh.function, u)
|
||||
} else {
|
||||
u, err = fh.getServiceEntryFromCache()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return u, firstToTheLock, err
|
||||
return svcEntryRecord{
|
||||
svcUrl: u,
|
||||
fromCache: firstToTheLock,
|
||||
}, err
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
@@ -508,7 +524,12 @@ func (fh *functionHandler) getServiceEntry() (serviceUrl *url.URL, serviceUrlFro
|
||||
return nil, false, err
|
||||
}
|
||||
|
||||
return serviceUrl, serviceUrlFromCache, err
|
||||
record, ok := recordObj.(svcEntryRecord)
|
||||
if !ok {
|
||||
return nil, false, errors.Errorf("Received unknown service record type")
|
||||
}
|
||||
|
||||
return record.svcUrl, record.fromCache, nil
|
||||
}
|
||||
|
||||
// getServiceEntryFromCache returns service url entry returns from cache
|
||||
|
||||
Reference in New Issue
Block a user