Fnlb was moved to its own repo: fnproject/lb (#702)

* Fnlb was moved to its own repo: fnproject/lb

* Clean up fnlb leftovers

* Newer deps
This commit is contained in:
Denis Makogon
2018-01-23 00:17:29 +02:00
committed by Reed Allman
parent 4ffa3d5005
commit d3be603e54
8310 changed files with 457462 additions and 1749312 deletions

View File

@@ -278,16 +278,16 @@ func TestMultiLog(t *testing.T) {
if err != nil {
t.Errorf("Unexpected error: %s", err)
}
if logObj.Payload.Log.Log == "" {
if logObj.Payload == "" {
t.Errorf("Log entry must not be empty!")
}
if !strings.Contains(logObj.Payload.Log.Log, "First line") {
if !strings.Contains(logObj.Payload, "First line") {
t.Errorf("Log entry must contain `First line` "+
"string, but got: %v", logObj.Payload.Log.Log)
"string, but got: %v", logObj.Payload)
}
if !strings.Contains(logObj.Payload.Log.Log, "Second line") {
if !strings.Contains(logObj.Payload, "Second line") {
t.Errorf("Log entry must contain `Second line` "+
"string, but got: %v", logObj.Payload.Log.Log)
"string, but got: %v", logObj.Payload)
}
}
@@ -407,9 +407,9 @@ func TestOversizedLog(t *testing.T) {
if err != nil {
t.Errorf("Unexpected error: %s", err)
}
if len(logObj.Payload.Log.Log) >= size {
if len(logObj.Payload) >= size {
t.Errorf("Log entry suppose to be truncated up to expected size %v, got %v",
size/1024, len(logObj.Payload.Log.Log))
size/1024, len(logObj.Payload))
}
}

View File

@@ -157,7 +157,7 @@ func ListRoutes(t *testing.T, ctx context.Context, fnclient *client.Fn, appName
func GetRoute(t *testing.T, ctx context.Context, fnclient *client.Fn, appName, routePath string) *models.Route {
cfg := &routes.GetAppsAppRoutesRouteParams{
App: appName,
Route: routePath,
Route: routePath[1:],
Context: ctx,
}

View File

@@ -1,88 +0,0 @@
# fnlb-test-harness
Test harness that exercises the fnlb load balancer in order to verify that it works properly.
## How it works
This is a test harness that makes calls to an Fn route through the fnlb load balancer, which routes traffic to multiple Fn nodes.
The test harness keeps track of which node each request was routed to so we can assess how the requests are being distributed across the nodes. The functionality
of fnlb is to normally route traffic to the same small number of nodes so that efficiences can be achieved and to support reuse of hot functions.
### Primes function
The test harness utilizes the "primes" function, which calculates prime numbers as an excuse for consuming CPU resources. The function is invoked as follows:
```
curl http://host:8080/r/primesapp/primes?max=1000000&loops=1
```
where:
- *max*: calculate all primes <= max (increasing max will increase memory usage, due to the Sieve of Eratosthenes algorithm)
- *loops*: number of times to calculate the primes (repeating the count consumes additional CPU without consuming additional memory)
## How to use it
The test harness requires running one or more Fn nodes and one instance of fnlb. The list of nodes must be provided both to fnlb and to the test harness
because the test harness must call each node directly one time in order to discover the node's container id.
After it has run, examine the results to see how the requests were distributed across the nodes.
### How to run it locally
Each of the Fn nodes needs to connect to the same database.
STEP 1: Create a route for the primes function. Example:
```
fn apps create primesapp
fn routes create primesapp /primes jconning/primes:0.0.1
```
STEP 2: Run five Fn nodes locally. Example (runs five nodes in the background using Docker):
```
sudo docker run -d -it --name functions-8082 --privileged -v ${HOME}/data-8082:/app/data -p 8082:8080 -e "DB_URL=postgres://dbUser:dbPassword@dbHost:5432/dbName" fnproject/fnserver
sudo docker run -d -it --name functions-8083 --privileged -v ${HOME}/data-8083:/app/data -p 8083:8080 -e "DB_URL=postgres://dbUser:dbPassword@dbHost:5432/dbName" fnproject/fnserver
sudo docker run -d -it --name functions-8084 --privileged -v ${HOME}/data-8084:/app/data -p 8084:8080 -e "DB_URL=postgres://dbUser:dbPassword@dbHost:5432/dbName" fnproject/fnserver
sudo docker run -d -it --name functions-8085 --privileged -v ${HOME}/data-8085:/app/data -p 8085:8080 -e "DB_URL=postgres://dbUser:dbPassword@dbHost:5432/dbName" fnproject/fnserver
sudo docker run -d -it --name functions-8086 --privileged -v ${HOME}/data-8086:/app/data -p 8086:8080 -e "DB_URL=postgres://dbUser:dbPassword@dbHost:5432/dbName" fnproject/fnserver
```
STEP 3: Run fnlb locally. Example (runs fnlb on the default port 8081):
```
fnlb -nodes localhost:8082,localhost:8083,localhost:8084,localhost:8085,localhost:8086
```
STEP 4: Run the test harness. Note that the 'nodes' parameter should be the same that was used with fnlb. Example:
```
cd functions/test/fnlb-test-harness
go run main.go -nodes localhost:8082,localhost:8083,localhost:8084,localhost:8085,localhost:8086 -calls 10 -v
```
STEP 5: Examine the output to determine how many times fnlb called each node. Assess whether it is working properly.
### Usage
go run main.go -help
<i>Command line parameters:</i>
- *-calls*: number of times to call the route (default 100)
- *-lb*: host and port of load balancer (default "localhost:8081")
- *-loops*: number of times to execute the primes calculation (ex: '-loops 2' means run the primes calculation twice) (default 1)
- *-max*: maximum number to search for primes (higher number consumes more memory) (default 1000000)
- *-nodes*: comma-delimited list of nodes (host:port) balanced by the load balancer (needed to discover container id of each) (default "localhost:8080")
- *-route*: path representing the route to the primes function (default "/r/primesapp/primes")
- *-v*: flag indicating verbose output
### Examples: quick vs long running
**Quick function:**: calculate primes up to 1000
```
go run main.go -nodes localhost:8082,localhost:8083,localhost:8084,localhost:8085,localhost:8086 -max 1000 -v
```
where *-max* is default of 1M, *-calls* is default of 100, *-route* is default of "/r/primesapp/primes", *-lb* is default localhost:8081
**Normal function**: calculate primes up to 1M
```
go run main.go -nodes localhost:8082,localhost:8083,localhost:8084,localhost:8085,localhost:8086 -v
```
where *-max* is default of 1M, *-calls* is default of 100, *-route* is default of "/r/primesapp/primes", *-lb* is default localhost:8081
**Longer running function**: calculate primes up to 1M and perform the calculation ten times
```
go run main.go -nodes localhost:8082,localhost:8083,localhost:8084,localhost:8085,localhost:8086 -loops 10 -v
```
where *-max* is default of 1M, *-calls* is default of 100, *-route* is default of "/r/primesapp/primes", *-lb* is default localhost:8081
**1000 calls to the route**: send 1000 requests through the load balancer
```
go run main.go -nodes localhost:8082,localhost:8083,localhost:8084,localhost:8085,localhost:8086 -calls 1000 -v
```
where *-max* is default of 1M, *-calls* is default of 100, *-route* is default of "/r/primesapp/primes", *-lb* is default localhost:8081
## Planned Enhancements
- Create 1000 routes and distribute calls amongst them.
- Use concurrent programming to enable the test harness to call multiple routes at the same time.

View File

@@ -1,126 +0,0 @@
package main
import (
"encoding/json"
"flag"
"fmt"
"io/ioutil"
"log"
"net/http"
"strings"
"time"
)
type execution struct {
DurationSeconds float64
Hostname string
node string
body string
responseSeconds float64
}
var (
lbHostPort, nodesStr, route string
numExecutions, maxPrime, numLoops int
nodes []string
nodesByContainerId map[string]string = make(map[string]string)
verbose bool
)
func init() {
flag.StringVar(&lbHostPort, "lb", "localhost:8081", "host and port of load balancer")
flag.StringVar(&nodesStr, "nodes", "localhost:8080", "comma-delimited list of nodes (host:port) balanced by the load balancer (needed to discover container id of each)")
flag.StringVar(&route, "route", "/r/primesapp/primes", "path representing the route to the primes function")
flag.IntVar(&numExecutions, "calls", 100, "number of times to call the route")
flag.IntVar(&maxPrime, "max", 1000000, "maximum number to search for primes (higher number consumes more memory)")
flag.IntVar(&numLoops, "loops", 1, "number of times to execute the primes calculation (ex: 'loops=2' means run the primes calculation twice)")
flag.BoolVar(&verbose, "v", false, "true for more verbose output")
flag.Parse()
if maxPrime < 3 {
log.Fatal("-max must be 3 or greater")
}
if numLoops < 1 {
log.Fatal("-loops must be 1 or greater")
}
nodes = strings.Split(nodesStr, ",")
}
func executeFunction(hostPort, path string, max, loops int) (execution, error) {
var e execution
start := time.Now()
resp, err := http.Get(fmt.Sprintf("http://%s%s?max=%d&loops=%d", hostPort, path, max, loops))
e.responseSeconds = time.Since(start).Seconds()
if err != nil {
return e, err
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
return e, fmt.Errorf("function returned status code: %d", resp.StatusCode)
}
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
return e, err
}
err = json.Unmarshal(body, &e)
if err != nil {
e.body = string(body) // set the body in the execution so that it is available for logging
return e, err
}
e.node = nodesByContainerId[e.Hostname]
return e, nil
}
func invokeLoadBalancer(hostPort, path string, numExecutions, max, loops int) {
executionsByNode := make(map[string][]execution)
fmt.Printf("All primes will be calculated up to %d, a total of %d time(s)\n", maxPrime, numLoops)
fmt.Printf("Calling route %s %d times (through the load balancer)...\n", route, numExecutions)
for i := 0; i < numExecutions; i++ {
e, err := executeFunction(hostPort, path, max, loops)
if err == nil {
if ex, ok := executionsByNode[e.node]; ok {
executionsByNode[e.node] = append(ex, e)
} else {
// Create a slice to contain the list of executions for this host
executionsByNode[e.node] = []execution{e}
}
if verbose {
fmt.Printf(" %s in-function duration: %fsec, response time: %fsec\n", e.node, e.DurationSeconds, e.responseSeconds)
}
} else {
fmt.Printf(" Ignoring failed execution on node %s: %v\n", e.node, err)
fmt.Printf(" JSON: %s\n", e.body)
}
}
fmt.Println("Results (executions per node):")
for node, ex := range executionsByNode {
fmt.Printf(" %s %d\n", node, len(ex))
}
}
func discoverContainerIds() {
// Discover the Docker hostname of each node; create a mapping of hostnames to host/port.
// This is needed because FN doesn't make the host/port available to the function (as of Mar 2017).
fmt.Println("Discovering container ids for every node (use Docker's HOSTNAME env var as a container id)...")
for _, s := range nodes {
if e, err := executeFunction(s, route, 100, 1); err == nil {
nodesByContainerId[e.Hostname] = s
fmt.Printf(" %s %s\n", s, e.Hostname)
} else {
fmt.Printf(" Ignoring host %s which returned error: %v\n", s, err)
fmt.Printf(" JSON: %s\n", e.body)
}
}
}
func main() {
discoverContainerIds()
invokeLoadBalancer(lbHostPort, route, numExecutions, maxPrime, numLoops)
}

View File

@@ -1,58 +0,0 @@
package main
import (
"fmt"
"os"
"strconv"
"strings"
"time"
)
// return list of primes less than N
// source: http://stackoverflow.com/a/21923233
func sieveOfEratosthenes(N int) (primes []int) {
b := make([]bool, N)
for i := 2; i < N; i++ {
if b[i] == true {
continue
}
primes = append(primes, i)
for k := i * i; k < N; k += i {
b[k] = true
}
}
return
}
func main() {
start := time.Now()
maxPrime := 1000000
numLoops := 1
// Parse the query string
s := strings.Split(os.Getenv("FN_REQUEST_URL"), "?")
if len(s) > 1 {
for _, pair := range strings.Split(s[1], "&") {
kv := strings.Split(pair, "=")
if len(kv) > 1 {
key, value := kv[0], kv[1]
if key == "max" {
maxPrime, _ = strconv.Atoi(value)
}
if key == "loops" {
numLoops, _ = strconv.Atoi(value)
}
}
}
}
// Repeat the calculation of primes simply to give the CPU more work to do without consuming additional memory
for i := 0; i < numLoops; i++ {
primes := sieveOfEratosthenes(maxPrime)
_ = primes
if i == numLoops-1 {
//fmt.Printf("Highest three primes: %d %d %d\n", primes[len(primes) - 1], primes[len(primes) - 2], primes[len(primes) - 3])
}
}
fmt.Printf("{\"durationSeconds\": %f, \"hostname\": \"%s\", \"max\": %d, \"loops\": %d}", time.Since(start).Seconds(), os.Getenv("HOSTNAME"), maxPrime, numLoops)
}

View File

@@ -1,5 +0,0 @@
name: jconning/primes
version: 0.0.1
runtime: go
entrypoint: ./func
path: /primes