by Michał Pawlik
code::dive 2019
sounds similar to...
REpresentational State Transfer
paths:
/pet:
post:
tags:
- "pet"
summary: "Add a new pet to the store"
description: ""
operationId: "addPet"
consumes:
- "application/json"
- "application/xml"
produces:
- "application/xml"
- "application/json"
parameters:
- in: "body"
name: "body"
description: "Pet object that needs to be added to the store"
required: true
schema:
$ref: "#/definitions/Pet"
responses:
405:
description: "Invalid input"
security:
- petstore_auth:
- "write:pets"
- "read:pets"
put:
tags:
- "pet"
summary: "Update an existing pet"
description: ""
operationId: "updatePet"
consumes:
- "application/json"
- "application/xml"
produces:
- "application/xml"
- "application/json"
parameters:
- in: "body"
name: "body"
description: "Pet object that needs to be added to the store"
required: true
schema:
$ref: "#/definitions/Pet"
responses:
400:
description: "Invalid ID supplied"
404:
description: "Pet not found"
405:
description: "Validation exception"
security:
- petstore_auth:
- "write:pets"
- "read:pets"
Sample from https://editor.swagger.io/
Protocol buffers are Google's language-neutral, platform-neutral, extensible mechanism for serializing structured data – think XML, but smaller, faster, and simpler.
~developers.google.com
syntax = "proto3";
package example;
message Person {
string name = 1;
int32 id = 2;
string email = 3;
}
message Product {
int64 id = 1;
string name = 2;
double price = 3;
map<string,string> properties = 4;
}
message EmptyMessage {}
service ExampleService {
rpc RequestResponse(MyRequestMsg) returns (MyResponseMsg) {}
rpc ClientStreaming(stream MyRequestMsg) returns (MyResponseMsg) {}
rpc ServerStreaming(MyRequestMsg) returns (stream MyResponseMsg) {}
rpc BiDiStreaming(stream MyRequestMsgg) returns (stream MyResponseMsg) {}
}
Message definition
Tag numbers
Service definition
// file name: shop.proto
syntax = "proto3";
package grpc_introduction;
message Product {
int64 id = 1;
string name = 2;
string description = 3;
double price = 4;
map<string,string> properties = 5;
}
message ProductsListRequest {}
service ShopServer {
rpc GetProducts (ProductsListRequest) returns (stream Product);
}
Example in Go
$ sudo dnf -y install protoc # or alike for your system
$ go get -u github.com/golang/protobuf/protoc-gen-go
$ protoc --go_out=. ./grpc_introduction/shop.proto
package main
import (
pb "grpc_introduction"
"google.golang.org/grpc"
)
/* Rest of the code */
package grpc_introduction
import (
fmt "fmt"
proto "github.com/golang/protobuf/proto"
math "math"
)
/*
... skipped ...
*/
type Product struct {
Id int64 `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"`
Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"`
Description string `protobuf:"bytes,3,opt,name=description,proto3" json:"description,omitempty"`
Price float64 `protobuf:"fixed64,4,opt,name=price,proto3" json:"price,omitempty"`
Properties map[string]string `protobuf:"bytes,5,rep,name=properties,proto3" json:"properties,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
type shopServer struct {
products []*pb.Product
}
func (s *shopServer) registerProduct(p *pb.Product) {
s.products = append(s.products, p)
}
func (s *shopServer) GetProducts(req *pb.ProductsListRequest, stream pb.ShopServer_GetProductsServer) error {
for _, p := range s.products {
if err := stream.Send(p); err != nil {
return err
}
}
log.Println("Products delivered")
return nil
}
func main() {
lis, _ := net.Listen("tcp", fmt.Sprintf(":%d", 9090))
log.Println("Listening on", lis.Addr())
grpcServer := grpc.NewServer()
svr := &shopServer{}
for i := 0; i < 20; i++ {
p := pb.Product{
Name: fmt.Sprintf("Product-%d", i),
Id: int64(i),
}
svr.registerProduct(&p)
}
pb.RegisterShopServerServer(grpcServer, svr)
grpcServer.Serve(lis)
}
func main() {
conn, _ := grpc.Dial("localhost:9090", grpc.WithInsecure())
client := pb.NewShopServerClient(conn)
ctx, _ := context.WithTimeout(context.Background(), 30*time.Second)
stream, _ := client.GetProducts(ctx, &pb.ProductsListRequest{})
products := make([]*pb.Product, 0)
for {
product, err := stream.Recv()
if err == io.EOF {
break
}
if err != nil {
log.Fatal(err)
}
products := append(products, product)
}
log.Println(products)
}
$ go build
$ ./server
2019/09/17 11:06:36 Listening on [::]:9090
$ go build
$ ./client
2019/09/17 11:09:47 [name:"Product-0" id:1 name:"Product-1" id:2 name:"Product-2" id:3 name:"Product-3" id:4 name:"Product-4" id:5 name:"Product-5" id:6 name:"Product-6" id:7 name:"Product-7" id:8 name:"Product-8" id:9 name:"Product-9" id:10 name:"Product-10" id:11 name:"Product-11" id:12 name:"Product-12" id:13 name:"Product-13" id:14 name:"Product-14" id:15 name:"Product-15" id:16 name:"Product-16" id:17 name:"Product-17" id:18 name:"Product-18" id:19 name:"Product-19" ]
$ go build
$ ./server
2019/09/17 11:06:36 Listening on [::]:9090
2019/09/17 11:06:38 Products delivered
import grpc
import shop_pb2
import shop_pb2_grpc
channel = grpc.insecure_channel('localhost:9090')
stub = shop_pb2_grpc.ShopServerStub(channel)
products = stub.GetProducts(shop_pb2.ProductsListRequest())
for product in products:
print(product.name, product.id)
$ mkdir python-client
$ cp shop.proto python-client
$ cd python-client
$ virtualenv -p python3 .venv
$ pip install grpcio grpcio-tools
$ python -m grpc_tools.protoc -I=. --python_out=. --grpc_python_out=. shop.proto
# shop_pb2_grpc.py and shop_pb2.py are now generated in current directory
$ python main.py
Product-0 0
Product-1 1
Product-2 2
Product-3 3
Product-4 4
Product-5 5
Product-6 6
Product-7 7
Product-8 8
Product-9 9
Product-10 10
Product-11 11
Product-12 12
Product-13 13
Product-14 14
Product-15 15
Product-16 16
Product-17 17
Product-18 18
Product-19 19
Supported officially
C++
Java
Python
Dart
Objective C
Go
Ruby
Node.js
Dart
C#
PHP
And many more community driven