Functions as a service and json datastore.
- Run
http functionsas individual services (see examples/method1) - Run
Async / background httpfunctions over Nats (see examples/async) - Run functions triggered on changes to the
Json datastorelikenew,updated,deleted,get(see examples/database-events) - Run a
combinationof above as a service (see examples/database-events) Observablefunctions with built-in OpenTelemetry tracing. (using this trace-span in a function examples/methodOtel)- Enable
autoscalingof functions or manually scale functions up/down with round-robin routing
1.Define a golang function -> see examples/helloWorld/hello.go
std Handler (w http.ResponseWriter, r *http.Request)gin Handler (c *gin.Context)
package hello
import (
"fmt"
"net/http"
"github.com/gin-gonic/gin"
)
// HelloWorldStd is the function to be deployed
// Note - function should be exported & have correct params
func HelloWorldStd(w http.ResponseWriter, r *http.Request) {
fmt.Fprint(w, "Hello, World!\n")
}
// OR
func HelloWorldGin(c *gin.Context) {
c.JSON(http.StatusOK, "Hello, World!!!!\n")
}- Create its definition File -
deploy.json
{
"deploy": [
{
"name" : "HelloWorldStd",
"filePath": "full/path/to/example/hello/helloWorld.go"
}
]
}name can alternatively be HelloWorldGin
3.Deploy
./proxyCli deploy deploy.jsonThe function gets deployed as its own container - configured with OpenTelemetry traces and connected to the internal services (nats, database & other deployments)
- Trigger the endpoint and view the traces collected by the default exporter - Jaeger running at http://localhost:16686
curl http://localhost:9999/functions/helloworldstd
open http://localhost:16686Add flag --async to deploy as an async function. Check out example
./proxyCli deploy --async deploy.jsonor a combination with HTTP/ASYNC Functions
Add --main to deploy a combination of http, async & events
./proxyCli deploy --main deployMain.jsonShould export a single Init() method that registers the required triggers, http & async functions. Check out example
// NOTE THE PACKAGE NAME, IT SHOULD NOT BE A MAIN PACKAGE
package notMain
import (
"fmt"
"net/http"
FuncFw "github.com/Ishan27g/ryo-Faas/funcFw"
"github.com/Ishan27g/ryo-Faas/store"
)
func HttpMethod(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusAccepted)
fmt.Fprint(w, "Accepted at method - HttpMethod ..."+"\n")
}
func HttpAsyncMethod(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusAccepted)
fmt.Fprint(w, "Accepted at method - HttpAsyncMethod..."+"\n")
}
func documentTrigger(document store.Doc) {
fmt.Println(document.CreatedAt + " " + document.Id + " ---- at GenericCb()")
}
func main() {
// Http
// register a http method
FuncFw.Export.Http("HttpMethod", "/method1", HttpMethod)
//Async
// register your http async method over Nats
FuncFw.Export.NatsAsync("HttpAsyncMethod-Nats", "/asyncNats", HttpAsyncMethod)
// or register your http async method over Http
FuncFw.Export.HttpAsync("HttpAsyncMethod", "/async", HttpAsyncMethod)
//DataStore events
// register a function to be called when a new `payments` document is created
FuncFw.Export.EventsFor("payments").On(store.DocumentCREATE, documentTrigger)
// register a function to be called when some existing `bills` document is updated
FuncFw.Export.EventsFor("bills").On(store.DocumentUPDATE, documentTrigger)
// register a function to be called when a known `payments` document (by its ID) is retrieved
FuncFw.Export.EventsFor("payments").OnIds(store.DocumentGET, documentTrigger, "some-known-id")
// register a function to be called when a known `bills` document (by its ID) is retrieved
FuncFw.Export.EventsFor("bills").OnIds(store.DocumentGET, documentTrigger, "some-known-id")
}{
"deploy": [
{
"name" : "Database-events",
"filePath": "/Users/ishan/Desktop/multi/database-events/main.go"
}
]
}./proxyCli initCreates a directory - $HOME/.ry-faas/ and pulls relevant docker images
May take a few minutes to download the following docker images
-
proxy running at
localhost:9999 -
database running at
localhost:5000/5001 -
functionBase attached to internal docker network
-
nats:alpine3.15running atlocalhost:4222/8222 -
openzipkin/zipkin:2.23.15running atlocalhost:9411
# Ensure docker is running
./proxyCli startFaas./proxyCli deploy deploy.json./proxyCli details# stop a function, optional --prune flag
./proxyCli stop [functionName]
# or, stop ryo Faas
./proxyCli stopFaas
# optionally prune all images
./proxyCli pruneFunctions are run in a manner similar to Google's functions-framework-go. It simply registers the http-functions and then starts an HTTP server serving that function. (not considering cloudEvents).
Functions are run in a manner similar to OpenFaas.
The incoming request is serialised and sent to Nats allowing immediate response for the request.
The Nats message is received, deserialized into the http request and then acted upon.
The result is sent to a X-Callback-Url that is expected in the original request.
The store publishes events to Nats on each CRUD operation to the database, allowing subscribers to act on relevant changes
The function to be deployed along with its directory are copied to $HOME/.ry-faas/deployments/tmp/. Using the ast package, the cli
- Verifies the signature
- of the exported
http-function, or - of the exported
main-service
- of the exported
- Generates a new
exported_{function}.gofile (based on template.go that registers the provided function with the framework before starting an Http server. - The generated
serviceis then built into a Docker image and run as its own container accessible via the proxy
Proxy maps each function instance to a urlPrefix and routes incoming requests to each instance in a round-robin manner
Manually scale a function up/down by re-deploying/stopping it via the cli.
# deploy a function
./proxyCli deploy deploy.json
# scale up a previous deployment
./proxyCli deploy deploy.json
# scale it up again
./proxyCli deploy deploy.json
# scale it down
./proxyCli stop {first function-name from deploy.json} To enable autoscaling, deploy the scale function.
# deploy a function
./proxyCli deploy pkg/scale/deploy-scale.jsonThe proxy exports function invocation counts to this deployed function.
which in turn scales the running deployments based on the periodic invocation counts received by it. To scale, it queries the proxy
in the same way as the cli does.
Inject custom dependency like pointers, clients, dbConn etc into handlers
which can then be extracted inside handler pointers, clients, dbConn etc into handlers
package helloWorld
import (
"fmt"
"net/http"
FuncFw "github.com/Ishan27g/ryo-Faas/funcFw"
)
type srv struct {
val string
}
func init() {
s := &srv{"some data"}
// inject into funcFw
FuncFw.InjectCtx(s)
}
func Hello(w http.ResponseWriter, r *http.Request) {
fmt.Println("Hello hit....")
// get ctx from funcFw
s := FuncFw.ExtractCtx[*srv]()
if s != nil {
w.Write([]byte(s.val))
}
w.WriteHeader(http.StatusAccepted)
}