A function invoker must listen on port 8080 for request-response style interactions via a HTTP POST. The HTTP headers have a content-type that can be used to decode the request body.
A function invoker can also be implemented as a bi-directional stream (as opposed to request-response) in gRPC.
The carrier type is a generic function.Message
defined in the
Function Proto project.
There are language-specific bindings to the Message
type defined alongside the protobuffer declarations.
For HTTP you should be able to just POST some data to localhost:8080
and get a response.
It is advisable to include a Content-Type
header, but the function invoker is free to ignore it.
The function invoker can make all the decisions it likes about content negotiation, and marshalling the request
and response bodies. Any kind of sensible response with 200 OK should be fine - the sidecar takes care of headers in a
live Riff system, and copies the response body bytes verbatim.
For gRPC the invoker can still make any decision it likes about content negotiation, and even how many messages to
send in response. The function invoker can be tested by writing a tiny gRPC client and sending it a stream of
function.Message
. You would call it a success if you got any kind of messages back, depending on the semantics of the
function. If you are supporting request-response, precisely one of the response messages must contain the same
correlationId
header as the incoming messages.
Instead of writing custom gRPC test clients, you can run the gateway locally. You will need Kafka, the riff gateway, a
riff sidecar, and your Function Invoker. Then you can POST messages to the gateway and they will flow through to the
function invoker and back through Kafka. This can be useful for end-to-end testing, especially with gRPC function
invokers, since there is nothing as convenient as curl
for ad hoc testing.
One way to run kafka is to use Spring Cloud CLI, or you could use docker. Using docker would look like this
docker run -e ADVERTISED_HOST=localhost -p 9092:9092 -ti spotify/kafka
Then clone and run the gateway. This requires golang 1.9 or later, so check
your go env
and set GOPATH
and GOROOT
env vars accordingly. Then
do this:
go get github.com/projectriff/riff/
cd $(go env GOPATH)/src/github.com/projectriff/riff/http-gateway
export KAFKA_BROKERS=localhost:9092
go run cmd/http-gateway.go
Then get your function invoker running, and make sure it doesn’t try and listen on port 8080 (where the gateway is already running).
And, once you have the function invoker running, do the same with the sidecar:
go get github.com/projectriff/riff
cd $(go env GOPATH)/src/github.com/projectriff/riff/function-sidecar
go run cmd/function-sidecar.go -brokers=localhost:9092 -inputs=inputs -outputs=replies -group=default -protocol=grpc
yields
2018/02/20 16:33:27 Sidecar for function 'default' (inputs->outputs) using grpc dispatcher starting
2018/02/20 16:33:27 Rebalanced: &{Type:rebalance start Claimed:map[] Released:map[] Current:map[]}
2018/02/20 16:33:27 Rebalanced: &{Type:rebalance OK Claimed:map[inputs:[0]] Released:map[] Current:map[inputs:[0]]}
The name of the "inputs" channel is arbitrary, and will be used below in constructing the URI for addressing the gateway. The "ouputs" has to be "replies" for the request-reply pattern to work.
At this point you can POST messages into the gateway and they will flow through kafka to the sidecar and then into the function invoker and back. There are 2 endpoints on the gateway:
Endpoint |
Semantics |
POST /requests/{channel} |
Request-response. |
POST /messages/{channel} |
Message stream (bi-directional). |
For both endpoints remember to include a Content-Type
header. The "channel" name is the name of the "inputs" command
line option when you started the sidecar. Example:
curl localhost:8080/requests/inputs -H "Content-Type: text/plain" -d World
Hello World
You will see the messages flowing through the sidecar in logs, e.g.
2018/02/20 16:34:47 <<< Message{Hello World, map[Accept:[*/*] Content-Type:[text/plain] timestamp:[1519144487664] correlationId:[b1a97d11-c2e1-4eb5-8919-92a859dcbf43]]}