gRPC
Also known as: rpc protobuf protocol buffers microservice proto service stub remote call streaming bidirectional bidi client stream server stream chat conversation duplex realtime
● Intermediate5 minBefore this: HTTP, RecordsSpec 14.14
gRPC services from your own Kavach types: no .proto file to write first, no code generator, and any gRPC library can call them.
use std.grpc
use std.net
type Order {
id: Int
item: String
qty: Int = 1
}
type Placed {
id: Int
total: Float
}
fn orders(call: GrpcCall) -> GrpcReply {
const order = grpc.read<Order>(call) catch _ => Order(id: 0, item: "")
if order.id == 0 {
return grpc.failed(3, "not an order")
}
grpc.reply(Placed(id: order.id, total: 2.5 * Float(order.qty)))
}
fn main() ! Error {
const listener = try net.listen("127.0.0.1:0")
const _server = spawn grpc.serve(listener, orders)
const target = "http://{listener.address()}"
const placed = try grpc.call<Order, Placed>(target, "/shop.Orders/Place", Order(id: 7, item: "pen", qty: 2))
print(placed.total)
const bad = grpc.call<Order, Placed>(target, "/shop.Orders/Place", Order(id: 0, item: "")) catch e => {
print("failed: {e}")
return
}
print(bad.total)
}The program ran and printed:
kavach run call.kav
5.0failed: Status(code: 3, message: "not an order")
- The handler reads the request into an
Orderand answers with aPlaced. The types are the messages. grpc.failed(3, …)answers with a gRPC status; 3 isINVALID_ARGUMENT. The client gets it asStatus(code, message).http://means HTTP/2 without encryption, as inside a company's network;https://uses TLS.
Every function
| Function | Gives |
|---|---|
grpc.call<Req, Res>(target, method, request) -> Res ! GrpcError | Calls method (/shop.Orders/Place) on the server at target with request, and decodes its answer into a Res |
grpc.call_stream<Req, Res>(target, method, requests) -> Res ! GrpcError | Client streaming: sends each of requests as its own message on one call, and decodes the one answer into a Res |
grpc.each<Req, Res>(target, method, request, on) ! GrpcError | Server streaming: runs on: fn(Res) for each message of the answer as it arrives |
grpc.each_stream<Req, Res>(target, method, requests, on) ! GrpcError | Two-way streaming: sends each of requests, then runs on for each message of the answer as it arrives |
grpc.serve(listener, handler) ! NetError | Serves gRPC on listener: each call is answered by handler: fn(GrpcCall) -> GrpcReply, each connection in a task of its own |
grpc.serve_talks(listener, handler, talks) ! NetError | Serves like serve, but holds a conversation on each method in talks: [String]: handler answers each of its messages as soon as it arrives |
grpc.talk_with<Req, Res>(target, method, first, on) ! GrpcError | A conversation: sends first, then runs on: fn(Res) -> [Req]? for each answer as it arrives, sending what it gives; none ends the client's side |
grpc.read<T>(call) -> T ! GrpcError | A call's message decoded into a T (the first, when the client sent several) |
grpc.read_all<T>(call) -> [T] ! GrpcError | Every message the client sent on the call, in order, each decoded into a T |
grpc.reply<T>(value) -> GrpcReply | An answer with value |
grpc.reply_stream<T>(values) -> GrpcReply | An answer that streams values, one message each |
grpc.failed(code, message) -> GrpcReply | An answer with a gRPC status other than OK (3 is INVALID_ARGUMENT, 5 NOT_FOUND, …) |
grpc.encode<T>(value) -> Bytes | value as a Protocol Buffers message |
grpc.decode<T>(data) -> T ! GrpcError | A Protocol Buffers message decoded into a T |
One page for each form
Go deeper the why, for readers who want it
Messages are Protocol Buffers worked out from each type: field n of a record is its nth declared field. Calls are HTTP/2 POSTs with the status in a trailer, as the gRPC standard says, so a Go, Java or Python service can call a Kavach one, and the other way round.