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:
Ta-Ching Chen
2019-01-16 12:37:46 +08:00
committed by GitHub
parent a21dbd182c
commit 273c6c002f
6 changed files with 320 additions and 261 deletions
+75 -54
View File
@@ -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