mirror of
https://github.com/openfaas/faas.git
synced 2025-06-18 03:56:37 +00:00
Refactor scaling to use existing code
Existing code has been used for scaling up and querying replicas. This meant the new code was deleted and there is less duplication now. The cache store a whole query response rather than just the available replica count and the tests were updated. This has been tested with Docker swarm and the image: openfaas/gateway:scale-17-07-2018 This feature now needs the env-var of scale_from_zero to be enabled in order to turn on the scaling behaviour. Signed-off-by: Alex Ellis (VMware) <alexellis2@gmail.com>
This commit is contained in:
committed by
Alex Ellis
parent
c58af8da56
commit
9512f09d2b
@ -4,16 +4,10 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"time"
|
||||
|
||||
"github.com/openfaas/faas/gateway/requests"
|
||||
)
|
||||
|
||||
// ScalingConfig for scaling behaviours
|
||||
@ -21,6 +15,7 @@ type ScalingConfig struct {
|
||||
MaxPollCount uint
|
||||
FunctionPollInterval time.Duration
|
||||
CacheExpiry time.Duration
|
||||
ServiceQuery ServiceQuery
|
||||
}
|
||||
|
||||
// MakeScalingHandler creates handler which can scale a function from
|
||||
@ -35,33 +30,31 @@ func MakeScalingHandler(next http.HandlerFunc, upstream http.HandlerFunc, config
|
||||
|
||||
functionName := getServiceName(r.URL.String())
|
||||
|
||||
if replicas, hit := cache.Get(functionName); hit && replicas > 0 {
|
||||
if serviceQueryResponse, hit := cache.Get(functionName); hit && serviceQueryResponse.AvailableReplicas > 0 {
|
||||
next.ServeHTTP(w, r)
|
||||
return
|
||||
}
|
||||
|
||||
replicas, code, err := getReplicas(functionName, upstream)
|
||||
cache.Set(functionName, replicas)
|
||||
queryResponse, err := config.ServiceQuery.GetReplicas(functionName)
|
||||
cache.Set(functionName, queryResponse)
|
||||
|
||||
if err != nil {
|
||||
var errStr string
|
||||
if code == http.StatusNotFound {
|
||||
errStr = fmt.Sprintf("unable to find function: %s", functionName)
|
||||
|
||||
} else {
|
||||
errStr = fmt.Sprintf("error finding function %s: %s", functionName, err.Error())
|
||||
}
|
||||
errStr = fmt.Sprintf("error finding function %s: %s", functionName, err.Error())
|
||||
|
||||
log.Printf(errStr)
|
||||
w.WriteHeader(code)
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
w.Write([]byte(errStr))
|
||||
return
|
||||
}
|
||||
|
||||
if replicas == 0 {
|
||||
if queryResponse.AvailableReplicas == 0 {
|
||||
minReplicas := uint64(1)
|
||||
if queryResponse.MinReplicas > 0 {
|
||||
minReplicas = queryResponse.MinReplicas
|
||||
}
|
||||
|
||||
err := scaleFunction(functionName, minReplicas, upstream)
|
||||
err := config.ServiceQuery.SetReplicas(functionName, minReplicas)
|
||||
if err != nil {
|
||||
errStr := fmt.Errorf("unable to scale function [%s], err: %s", functionName, err)
|
||||
log.Printf(errStr.Error())
|
||||
@ -72,8 +65,8 @@ func MakeScalingHandler(next http.HandlerFunc, upstream http.HandlerFunc, config
|
||||
}
|
||||
|
||||
for i := 0; i < int(config.MaxPollCount); i++ {
|
||||
replicas, _, err := getReplicas(functionName, upstream)
|
||||
cache.Set(functionName, replicas)
|
||||
queryResponse, err := config.ServiceQuery.GetReplicas(functionName)
|
||||
cache.Set(functionName, queryResponse)
|
||||
|
||||
if err != nil {
|
||||
errStr := fmt.Sprintf("error: %s", err.Error())
|
||||
@ -84,7 +77,7 @@ func MakeScalingHandler(next http.HandlerFunc, upstream http.HandlerFunc, config
|
||||
return
|
||||
}
|
||||
|
||||
if replicas > 0 {
|
||||
if queryResponse.AvailableReplicas > 0 {
|
||||
break
|
||||
}
|
||||
|
||||
@ -95,53 +88,3 @@ func MakeScalingHandler(next http.HandlerFunc, upstream http.HandlerFunc, config
|
||||
next.ServeHTTP(w, r)
|
||||
}
|
||||
}
|
||||
|
||||
func getReplicas(functionName string, upstream http.HandlerFunc) (uint64, int, error) {
|
||||
|
||||
replicasQuery, _ := http.NewRequest(http.MethodGet, fmt.Sprintf("/system/function/%s", functionName), nil)
|
||||
rr := httptest.NewRecorder()
|
||||
|
||||
upstream.ServeHTTP(rr, replicasQuery)
|
||||
if rr.Code != 200 {
|
||||
log.Printf("error, query replicas status: %d", rr.Code)
|
||||
|
||||
var errBody string
|
||||
if rr.Body != nil {
|
||||
errBody = string(rr.Body.String())
|
||||
}
|
||||
|
||||
return 0, rr.Code, fmt.Errorf("unable to query function: %s", string(errBody))
|
||||
}
|
||||
|
||||
replicaBytes, _ := ioutil.ReadAll(rr.Body)
|
||||
replicaResult := requests.Function{}
|
||||
json.Unmarshal(replicaBytes, &replicaResult)
|
||||
|
||||
return replicaResult.AvailableReplicas, rr.Code, nil
|
||||
}
|
||||
|
||||
func scaleFunction(functionName string, minReplicas uint64, upstream http.HandlerFunc) error {
|
||||
scaleReq := ScaleServiceRequest{
|
||||
Replicas: minReplicas,
|
||||
ServiceName: functionName,
|
||||
}
|
||||
|
||||
scaleBytesOut, _ := json.Marshal(scaleReq)
|
||||
scaleBytesOutBody := bytes.NewBuffer(scaleBytesOut)
|
||||
setReplicasReq, _ := http.NewRequest(http.MethodPost, fmt.Sprintf("/system/scale-function/%s", functionName), scaleBytesOutBody)
|
||||
|
||||
rr := httptest.NewRecorder()
|
||||
upstream.ServeHTTP(rr, setReplicasReq)
|
||||
|
||||
if rr.Code != 200 {
|
||||
return fmt.Errorf("scale to 1 replica status: %d", rr.Code)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ScaleServiceRequest request to scale a function
|
||||
type ScaleServiceRequest struct {
|
||||
ServiceName string `json:"serviceName"`
|
||||
Replicas uint64 `json:"replicas"`
|
||||
}
|
||||
|
Reference in New Issue
Block a user