Round Tripper of Fission Router: parameters be configurable (#713)

The Round Tripper parameters of timeout, keep alive time and Max retry
times is configured in router system environment.

And they are set to Round Tripper when router service initializing.

This setup new nested structure `tsRoundTripperParams` to transfer them.
This commit is contained in:
xiekeyang
2018-07-12 12:08:52 -07:00
committed by Soam Vasani
parent 2982efebb8
commit 3707b95edb
7 changed files with 103 additions and 33 deletions
@@ -175,6 +175,14 @@ spec:
valueFrom: valueFrom:
fieldRef: fieldRef:
fieldPath: metadata.namespace fieldPath: metadata.namespace
- name: ROUTER_ROUND_TRIP_TIMEOUT
value: {{ .Values.routerRoundTripTimeout | default "50ms" | quote }}
- name: ROUTER_ROUNDTRIP_TIMEOUT_EXPONENT
value: {{ .Values.routerRoundTripTimeoutExponent | default 2 | quote }}
- name: ROUTER_ROUND_TRIP_KEEP_ALIVE_TIME
value: {{ .Values.routerRoundTripKeepAliveTime | default "30s" | quote }}
- name: ROUTER_ROUND_TRIP_MAX_RETRIES
value: {{ .Values.routerRoundTripMaxRetries | default 10 | quote }}
readinessProbe: readinessProbe:
httpGet: httpGet:
path: "/router-healthz" path: "/router-healthz"
@@ -172,6 +172,14 @@ spec:
valueFrom: valueFrom:
fieldRef: fieldRef:
fieldPath: metadata.namespace fieldPath: metadata.namespace
- name: ROUTER_ROUND_TRIP_TIMEOUT
value: {{ .Values.routerRoundTripTimeout | default "50ms" | quote }}
- name: ROUTER_ROUNDTRIP_TIMEOUT_EXPONENT
value: {{ .Values.routerRoundTripTimeoutExponent | default 2 | quote }}
- name: ROUTER_ROUND_TRIP_KEEP_ALIVE_TIME
value: {{ .Values.routerRoundTripKeepAliveTime | default "30s" | quote }}
- name: ROUTER_ROUND_TRIP_MAX_RETRIES
value: {{ .Values.routerRoundTripMaxRetries | default 10 | quote }}
readinessProbe: readinessProbe:
httpGet: httpGet:
path: "/router-healthz" path: "/router-healthz"
+24 -17
View File
@@ -33,18 +33,24 @@ import (
executorClient "github.com/fission/fission/executor/client" executorClient "github.com/fission/fission/executor/client"
) )
type tsRoundTripperParams struct {
timeout time.Duration
timeoutExponent int
keepAlive time.Duration
maxRetries int
}
type functionHandler struct { type functionHandler struct {
fmap *functionServiceMap fmap *functionServiceMap
executor *executorClient.Client executor *executorClient.Client
function *metav1.ObjectMeta function *metav1.ObjectMeta
httpTrigger *crd.HTTPTrigger httpTrigger *crd.HTTPTrigger
tsRoundTripperParams *tsRoundTripperParams
} }
// A layer on top of http.DefaultTransport, with retries. // A layer on top of http.DefaultTransport, with retries.
type RetryingRoundTripper struct { type RetryingRoundTripper struct {
maxRetries int funcHandler *functionHandler
initialTimeout time.Duration
funcHandler *functionHandler
} }
// RoundTrip is a custom transport with retries for http requests that forwards the request to the right serviceUrl, obtained // RoundTrip is a custom transport with retries for http requests that forwards the request to the right serviceUrl, obtained
@@ -92,7 +98,6 @@ func (roundTripper RetryingRoundTripper) RoundTrip(req *http.Request) (resp *htt
} }
// set the timeout for transport context // set the timeout for transport context
timeout := roundTripper.initialTimeout
transport := http.DefaultTransport.(*http.Transport) transport := http.DefaultTransport.(*http.Transport)
// Disables caching, Please refer to issue and specifically comment: https://github.com/fission/fission/issues/723#issuecomment-398781995 // Disables caching, Please refer to issue and specifically comment: https://github.com/fission/fission/issues/723#issuecomment-398781995
transport.DisableKeepAlives = true transport.DisableKeepAlives = true
@@ -104,7 +109,9 @@ func (roundTripper RetryingRoundTripper) RoundTrip(req *http.Request) (resp *htt
needExecutor = true needExecutor = true
} }
for i := 0; i < roundTripper.maxRetries-1; i++ { executingTimeout := roundTripper.funcHandler.tsRoundTripperParams.timeout
for i := 0; i < roundTripper.funcHandler.tsRoundTripperParams.maxRetries-1; i++ {
if needExecutor { if needExecutor {
log.Printf("Calling getServiceForFunction for function: %s", roundTripper.funcHandler.function.Name) log.Printf("Calling getServiceForFunction for function: %s", roundTripper.funcHandler.function.Name)
@@ -149,8 +156,8 @@ func (roundTripper RetryingRoundTripper) RoundTrip(req *http.Request) (resp *htt
// over-riding default settings. // over-riding default settings.
transport.DialContext = (&net.Dialer{ transport.DialContext = (&net.Dialer{
Timeout: timeout, Timeout: executingTimeout,
KeepAlive: 30 * time.Second, KeepAlive: roundTripper.funcHandler.tsRoundTripperParams.keepAlive,
}).DialContext }).DialContext
overhead := time.Since(startTime) overhead := time.Since(startTime)
@@ -183,9 +190,11 @@ func (roundTripper RetryingRoundTripper) RoundTrip(req *http.Request) (resp *htt
// just retry after backing off for timeout period. // just retry after backing off for timeout period.
if serviceUrlFromExecutor { if serviceUrlFromExecutor {
log.Printf("request to %s errored out. backing off for %v before retrying", log.Printf("request to %s errored out. backing off for %v before retrying",
req.URL.Host, timeout) req.URL.Host, executingTimeout)
timeout *= time.Duration(2)
time.Sleep(timeout) executingTimeout = executingTimeout * time.Duration(roundTripper.funcHandler.tsRoundTripperParams.timeoutExponent)
time.Sleep(executingTimeout)
needExecutor = false needExecutor = false
continue continue
} else { } else {
@@ -231,9 +240,7 @@ func (fh *functionHandler) handler(responseWriter http.ResponseWriter, request *
proxy := &httputil.ReverseProxy{ proxy := &httputil.ReverseProxy{
Director: director, Director: director,
Transport: &RetryingRoundTripper{ Transport: &RetryingRoundTripper{
initialTimeout: 50 * time.Millisecond, funcHandler: fh,
maxRetries: 10,
funcHandler: fh,
}, },
} }
+7
View File
@@ -22,6 +22,7 @@ import (
"net/http/httptest" "net/http/httptest"
"net/url" "net/url"
"testing" "testing"
"time"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
) )
@@ -56,6 +57,12 @@ func TestFunctionProxying(t *testing.T) {
fh := &functionHandler{fmap: fmap, fh := &functionHandler{fmap: fmap,
function: fn, function: fn,
tsRoundTripperParams: &tsRoundTripperParams{
timeout: 50 * time.Millisecond,
timeoutExponent: 2,
keepAlive: 30 * time.Second,
maxRetries: 10,
},
} }
functionHandlerServer := httptest.NewServer(http.HandlerFunc(fh.handler)) functionHandlerServer := httptest.NewServer(http.HandlerFunc(fh.handler))
fhURL := functionHandlerServer.URL fhURL := functionHandlerServer.URL
+19 -14
View File
@@ -49,17 +49,20 @@ type HTTPTriggerSet struct {
functions []crd.Function functions []crd.Function
funcStore k8sCache.Store funcStore k8sCache.Store
funcController k8sCache.Controller funcController k8sCache.Controller
tsRoundTripperParams *tsRoundTripperParams
} }
func makeHTTPTriggerSet(fmap *functionServiceMap, fissionClient *crd.FissionClient, func makeHTTPTriggerSet(fmap *functionServiceMap, fissionClient *crd.FissionClient,
kubeClient *kubernetes.Clientset, executor *executorClient.Client, crdClient *rest.RESTClient) (*HTTPTriggerSet, k8sCache.Store, k8sCache.Store) { kubeClient *kubernetes.Clientset, executor *executorClient.Client, crdClient *rest.RESTClient, params *tsRoundTripperParams) (*HTTPTriggerSet, k8sCache.Store, k8sCache.Store) {
httpTriggerSet := &HTTPTriggerSet{ httpTriggerSet := &HTTPTriggerSet{
functionServiceMap: fmap, functionServiceMap: fmap,
triggers: []crd.HTTPTrigger{}, triggers: []crd.HTTPTrigger{},
fissionClient: fissionClient, fissionClient: fissionClient,
kubeClient: kubeClient, kubeClient: kubeClient,
executor: executor, executor: executor,
crdClient: crdClient, crdClient: crdClient,
tsRoundTripperParams: params,
} }
var tStore, fnStore k8sCache.Store var tStore, fnStore k8sCache.Store
var tController, fnController k8sCache.Controller var tController, fnController k8sCache.Controller
@@ -121,10 +124,11 @@ func (ts *HTTPTriggerSet) getRouter() *mux.Router {
} }
fh := &functionHandler{ fh := &functionHandler{
fmap: ts.functionServiceMap, fmap: ts.functionServiceMap,
function: rr.functionMetadata, function: rr.functionMetadata,
executor: ts.executor, executor: ts.executor,
httpTrigger: &trigger, httpTrigger: &trigger,
tsRoundTripperParams: ts.tsRoundTripperParams,
} }
ht := muxRouter.HandleFunc(trigger.Spec.RelativeURL, fh.handler) ht := muxRouter.HandleFunc(trigger.Spec.RelativeURL, fh.handler)
@@ -152,9 +156,10 @@ func (ts *HTTPTriggerSet) getRouter() *mux.Router {
for _, function := range ts.functions { for _, function := range ts.functions {
m := function.Metadata m := function.Metadata
fh := &functionHandler{ fh := &functionHandler{
fmap: ts.functionServiceMap, fmap: ts.functionServiceMap,
function: &m, function: &m,
executor: ts.executor, executor: ts.executor,
tsRoundTripperParams: ts.tsRoundTripperParams,
} }
muxRouter.HandleFunc(fission.UrlForFunction(function.Metadata.Name, function.Metadata.Namespace), fh.handler) muxRouter.HandleFunc(fission.UrlForFunction(function.Metadata.Name, function.Metadata.Namespace), fh.handler)
} }
+30 -1
View File
@@ -44,6 +44,8 @@ import (
"fmt" "fmt"
"log" "log"
"net/http" "net/http"
"os"
"strconv"
"time" "time"
"github.com/gorilla/mux" "github.com/gorilla/mux"
@@ -97,7 +99,34 @@ func Start(port int, executorUrl string) {
restClient := fissionClient.GetCrdClient() restClient := fissionClient.GetCrdClient()
executor := executorClient.MakeClient(executorUrl) executor := executorClient.MakeClient(executorUrl)
triggers, _, fnStore := makeHTTPTriggerSet(fmap, fissionClient, kubeClient, executor, restClient)
timeout, err := time.ParseDuration(os.Getenv("ROUTER_ROUND_TRIP_TIMEOUT"))
if err != nil {
log.Fatalf("Failed to parse timeout: %v", err)
}
timeoutExponent, err := strconv.Atoi(os.Getenv("ROUTER_ROUNDTRIP_TIMEOUT_EXPONENT"))
if err != nil {
log.Fatalf("Failed to parse timeout exponent: %v", err)
}
keepAlive, err := time.ParseDuration(os.Getenv("ROUTER_ROUND_TRIP_KEEP_ALIVE_TIME"))
if err != nil {
log.Fatalf("Failed to parse keep alive time: %v", err)
}
maxRetries, err := strconv.Atoi(os.Getenv("ROUTER_ROUND_TRIP_MAX_RETRIES"))
if err != nil {
log.Fatalf("Failed to parse max retry times: %v", err)
}
triggers, _, fnStore := makeHTTPTriggerSet(fmap, fissionClient, kubeClient, executor, restClient,
&tsRoundTripperParams{
timeout: timeout,
timeoutExponent: timeoutExponent,
keepAlive: keepAlive,
maxRetries: maxRetries,
})
resolver := makeFunctionReferenceResolver(fnStore) resolver := makeFunctionReferenceResolver(fnStore)
go serveMetric() go serveMetric()
+7 -1
View File
@@ -59,7 +59,13 @@ func TestRouter(t *testing.T) {
frr.refCache.Set(nfr, rr) frr.refCache.Set(nfr, rr)
// HTTP trigger set with a trigger for this function // HTTP trigger set with a trigger for this function
triggers, _, _ := makeHTTPTriggerSet(fmap, nil, nil, nil, nil) triggers, _, _ := makeHTTPTriggerSet(fmap, nil, nil, nil, nil,
&tsRoundTripperParams{
timeout: 50 * time.Millisecond,
timeoutExponent: 2,
keepAlive: 30 * time.Second,
maxRetries: 10,
})
triggerUrl := "/foo" triggerUrl := "/foo"
triggers.triggers = append(triggers.triggers, triggers.triggers = append(triggers.triggers,
crd.HTTPTrigger{ crd.HTTPTrigger{