7 round trips
HPack => 1 Packet
HTTP/2 | HTTP/1 |
---|---|
binary | textual |
fully multiplexed | ordered and blocking |
one connection with parallelism | multiple connections |
header compression | lots of overhead |
push from server to client | polling |
TLS by default | TLS optional |
23% of websites already served with HTTP/2
https://w3techs.com/technologies/details/ce-http2/all/all
func EncodeVarint(x uint64) []byte {
var buf [10]byte
var n int
for n = 0; x > 127; n++ {
buf[n] = 0x80 | uint8(x&0x7F)
x >>= 7
}
buf[n] = uint8(x)
n++
return buf[0:n]
}
10101100 00000010
more
done
32+8+4
256
= 300
func DecodeVarint(buf []byte) (uint64, int) {
var x uint64
var n int
for shift := uint(0); ; shift += 7 {
b := buf[n]
n++
x |= (uint64(b) & 0x7F) << shift
if b < 0x80 {
break
}
}
return x, n
}
01010100
done
64+16+4
= 84
length|bytes
varint encoded length
varint | 0 |
fixed32 | 5 |
fixed64 | 1 |
length delimited | 2 |
field number|wire type
string myfieldname = 4;
encodeVarint(fieldNumber << 3 | wireType)
encodeVarint(4 << 3 | 2 )
00100010
done
4<<3
| 2
message MyMessage {
string myfieldname = 4;
repeated int64 values = 5;
MyMessage recursive = 6;
}
message MyMessage {
string myfieldname = 4;
repeated int64 values = 5;
MyMessage recursive = 6;
}
{
"myfieldname": "",
"values": [300,84],
"recursive": {
"myfieldname": "cde"
}
}
message MyMessage {
string myfieldname = 4;
repeated int64 values = 5;
MyMessage recursive = 6;
}
{
"myfieldname": "",
"values": [300,84],
"recursive": {
"myfieldname": "cde"
}
}
00100010
c:01100011
d:01100100
e:01100101
00000011
message MyMessage {
string myfieldname = 4;
repeated int64 values = 5;
MyMessage recursive = 6;
}
{
"myfieldname": "",
"values": [300,84],
"recursive": {
"myfieldname": "cde"
}
}
00110010
00100010
c:01100011
d:01100100
e:01100101
00000101
00000011
message MyMessage {
string myfieldname = 4;
repeated int64 values = 5;
MyMessage recursive = 6;
}
{
"myfieldname": "",
"values": [300,84],
"recursive": {
"myfieldname": "cde"
}
}
00110010
00100010
c:01100011
d:01100100
e:01100101
00000101
00000011
10101100 00000010
01010100
00101010
00000011
message MyMessage {
string myfieldname = 4;
repeated int64 values = 5;
MyMessage recursive = 6;
}
{
"myfieldname": "",
"values": [300,84],
"recursive": {
"myfieldname": "cde"
}
}
00110010
00100010
c:01100011
d:01100100
e:01100101
00000101
00000011
10101100 00000010
01010100
00101010
00000011
message MyMessage {
string myfieldname = 4;
repeated int64 values = 5;
MyMessage recursive = 6;
}
message MyMessage {
string myfieldname = 4;
repeated int64 values = 5;
MyMessage recursive = 6;
double myNewField = 7;
}
Old
New
message MyMessage {
string myfieldname = 4;
repeated int64 values = 5;
MyMessage recursive = 6;
}
message MyMessage {
string myfieldname = 4;
repeated int64 values = 5;
MyMessage recursive = 6;
double myNewField = 7;
}
Old
New
message MyMessage {
string myfieldname = 4;
repeated int64 values = 5;
MyMessage recursive = 6;
}
message MyMessage {
string newName = 4;
repeated int64 values = 5;
MyMessage recursive = 6;
}
Old
New
message MyMessage {
string myfieldname = 4;
repeated int64 values = 5;
MyMessage recursive = 6;
}
Old
New
message MyMessage {
string newName = 4;
repeated int64 values = 5;
MyMessage recursive = 6;
}
message MyMessage {
string myfieldname = 4;
repeated int64 values = 5;
MyMessage recursive = 6;
}
message MyMessage {
string myfieldname = 4;
repeated fixed64 values = 5;
MyMessage recursive = 6;
}
Old
New
message MyMessage {
string myfieldname = 4;
repeated int64 values = 5;
MyMessage recursive = 6;
}
message MyMessage {
string myfieldname = 4;
repeated fixed64 values = 5;
MyMessage recursive = 6;
}
Old
New
message MyMessage {
string myfieldname = 4;
repeated int64 values = 5;
MyMessage recursive = 6;
}
message MyMessage {
string myfieldname = 4;
MyMessage recursive = 6;
}
Old
New
message MyMessage {
string myfieldname = 4;
repeated int64 values = 5;
MyMessage recursive = 6;
}
message MyMessage {
string myfieldname = 4;
// used to be values
reserved 5;
MyMessage recursive = 6;
}
Old
New
message MyMessage {
string myfieldname = 4;
repeated int64 values = 5;
MyMessage recursive = 6;
}
message MyMessage {
string myfieldname = 4;
MyMessage recursive = 6;
bool mybool = 5;
}
Old
New
message MyMessage {
string myfieldname = 4;
repeated int64 values = 5;
MyMessage recursive = 6;
}
message MyMessage {
string myfieldname = 4;
MyMessage recursive = 6;
bool mybool = 5;
}
Old
New
message MyMessage {
string myfieldname = 4;
repeated int64 values = 5;
MyMessage recursive = 6;
}
message MyMessage {
string myfieldname = 4;
repeated bool mybool = 5;
MyMessage recursive = 6;
}
Old
New
message MyMessage {
string myfieldname = 4;
repeated int64 values = 5;
MyMessage recursive = 6;
}
message MyMessage {
string myfieldname = 4;
repeated bool mybool = 5;
MyMessage recursive = 6;
}
Old
New
message MapFieldEntry {
key_type key = 1;
value_type value = 2;
}
repeated MapFieldEntry map_field = N;
message Timestamp {
// Represents seconds of UTC time since Unix epoch
// 1970-01-01T00:00:00Z. Must be from 0001-01-01T00:00:00Z to
// 9999-12-31T23:59:59Z inclusive.
int64 seconds = 1;
// Non-negative fractions of a second at nanosecond resolution. Negative
// second values with fractions must still have non-negative nanos values
// that count forward in time. Must be from 0 to 999,999,999
// inclusive.
int32 nanos = 2;
}
message Duration {
int64 seconds = 1;
int32 nanos = 2;
}
service Greeter {
rpc SayHello (HelloRequest) returns (HelloReply) {}
}
message HelloRequest {
string name = 1;
}
message HelloReply {
string message = 1;
}
Java, Python, Go, C++, Node.js, Ruby, C#, Android Java, Objective-C, PHP, Swift, etc.
type GreeterClient interface {
SayHello(ctx context.Context, in *HelloRequest,
opts ...grpc.CallOption) (*HelloReply, error)
}
type greeterClient struct {
cc *grpc.ClientConn
}
func NewGreeterClient(cc *grpc.ClientConn) GreeterClient {
return &greeterClient{cc}
}
func (c *greeterClient) SayHello(ctx context.Context, in *HelloRequest,
opts ...grpc.CallOption) (*HelloReply, error) {
out := new(HelloReply)
err := grpc.Invoke(ctx, "/helloworld.Greeter/SayHello", in, out, c.cc, opts...)
return out, err
}
type HelloRequest struct {
Name string `protobuf:"bytes,1,opt,name=name" json:"name,omitempty"`
}
type HelloReply struct {
Message string `protobuf:"bytes,1,opt,name=message" json:"message,omitempty"`
}
protoc --go_out=plugins=grpc:. helloworld.proto
func main() {
conn, err := grpc.Dial("localhost:50051", grpc.WithInsecure())
if err != nil {
log.Fatalf("did not connect: %v", err)
}
defer conn.Close()
c := pb.NewGreeterClient(conn)
r, err := c.SayHello(context.Background(), &pb.HelloRequest{Name: "World"})
if err != nil {
log.Fatalf("could not greet: %v", err)
}
log.Printf("Greeting: %s", r.Message)
}
func main() {
enc := json.NewEncoder(bytes.NewBuffer())
if err := enc.Encode(&pb.HelloRequest{Name: "World"}); err != nil {
log.Fatalf("could not marshal: %v", err) // dynamically typed
}
resp, err := http.Post("localhost:50051/helloworld.Greeter/SayHello",
"application/json", enc)
if err != nil {
log.Fatalf("post failed: %v", err) // stringly typed
}
defer resp.Body.Close()
if resp.StatusCode != 200 {
log.Fatalf("not ok")
}
dec := json.NewDecoder(resp.Body)
r := &pb.HelloResponse{}
if err := dec.Decode(r); err != nil {
log.Fatalf("count not unmarshal: %v", err) // dynamically typed
}
log.Printf("Greeting: %s", r.Message)
}
def add_GreeterServicer_to_server(servicer, server):
rpc_method_handlers = {
'SayHello': grpc.unary_unary_rpc_method_handler(
servicer.SayHello,
request_deserializer=helloworld__pb2.HelloRequest.FromString,
response_serializer=helloworld__pb2.HelloReply.SerializeToString,
),
}
generic_handler = grpc.method_handlers_generic_handler(
'helloworld.Greeter', rpc_method_handlers)
server.add_generic_rpc_handlers((generic_handler,))
HelloReply = _reflection.GeneratedProtocolMessageType('HelloReply', (_message.Message,), dict(
DESCRIPTOR = _HELLOREPLY,
__module__ = 'helloworld_pb2'
# @@protoc_insertion_point(class_scope:helloworld.HelloReply)
))
_sym_db.RegisterMessage(HelloReply)
_HELLOREPLY = _descriptor.Descriptor(
name='HelloReply',
full_name='helloworld.HelloReply',
filename=None,
file=DESCRIPTOR,
containing_type=None,
fields=[
...
python -m grpc_tools.protoc --python_out=. --grpc_python_out=. helloworld.proto
class Greeter(helloworld_pb2_grpc.GreeterServicer):
def SayHello(self, request, context):
return helloworld_pb2.HelloReply(message='Hello, %s!' % request.name)\
if __name__ == '__main__':
server = grpc.server(futures.ThreadPoolExecutor(max_workers=10))
helloworld_pb2_grpc.add_GreeterServicer_to_server(Greeter(), server)
server.add_insecure_port('[::]:50051')
server.start()
service BuySide {
rpc Search (Query) returns (stream ItemSummary) {}
rpc Item (ItemId) returns (Item) {}
}
service Chat {
rpc Bidi (stream Chat) returns (stream Update) {}
}
protoc \
--descriptor_set_out=parseTree.pb \
--proto_path=.
$ echo <json-request> | java -jar polyglot.jar \
--command=call \
--endpoint=<host>:<port> \
--full_method=<some.package.Service/doSomething>
package example;
+
+import "google/api/annotations.proto";
+
message StringMessage {
string value = 1;
}
service YourService {
- rpc Echo(StringMessage) returns (StringMessage) {}
+ rpc Echo(StringMessage) returns (StringMessage) {
+ option (google.api.http) = {
+ post: "/v1/example/echo"
+ body: "*"
+ };
+ }
}
protoc -I/usr/local/include -I. \
-I$GOPATH/src \
-I$GOPATH/src/github.com/grpc-ecosystem/grpc-gateway/third_party/googleapis \
--grpc-gateway_out=logtostderr=true:. \
path/to/your_service.proto
protoc -I/usr/local/include -I. \
-I$GOPATH/src \
-I$GOPATH/src/github.com/grpc-ecosystem/grpc-gateway/third_party/googleapis \
--swagger_out=logtostderr=true:. \
path/to/your_service.proto
enum Instrument {
Voice = 0;
...
}
enum Genre {
Pop = 0;
...
}
message Artist {
//Pick something original
string Name = 1;
Instrument Role = 2;
}
message Song {
string Name = 1;
uint64 Track = 2;
double Duration = 3;
repeated Artist Composer = 4;
}
message Album {
string Name = 1;
repeated Song Song = 2;
Genre Genre = 3;
string Year = 4;
repeated string Producer = 5;
bool Mediocre = 6;
bool Rated = 7;
string Epilogue = 8;
repeated bool Likes = 9;
}
service Label {
rpc Produce(Album) returns (Album);
}
/**
* Represents the status of a vehicle booking.
*/
message BookingStatus {
int32 id = 1; /// Unique booking status ID.
string description = 2; /// Booking status description. E.g. "Active".
}
/**
* Represents the booking of a vehicle.
*
* Vehicles are some cool shit. But drive carefully!
*/
message Booking {
int32 vehicle_id = 1; /// ID of booked vehicle.
int32 customer_id = 2; /// Customer that booked the vehicle.
BookingStatus status = 3; /// Status of the booking.
/** Has booking confirmation been sent? */
bool confirmation_sent = 4;
/** Has payment been received? */
bool payment_received = 5;
}
func main() {
// Create Consul client
cli, err := api.NewClient(api.DefaultConfig())
if err != nil {
log.Fatal(err)
}
// Create a resolver for the "echo" service
r, err := lb.NewConsulResolver(cli, "echo", "")
if err != nil {
log.Fatal(err)
}
// Notice you can use a blank address here
conn, err := grpc.Dial("", grpc.WithBalancer(grpc.RoundRobin(r)))
if err != nil {
log.Fatal(err)
}
conn, err := grpc.Dial("localhost:9001", grpc.WithInsecure(),
grpc.WithUnaryInterceptor(
openTracingGrpc.OpenTracingClientInterceptor(
tracer, openTracingGrpc.LogPayloads())))
import "github.com/grpc-ecosystem/go-grpc-prometheus"
...
// Initialize your gRPC server's interceptor.
myServer := grpc.NewServer(
grpc.StreamInterceptor(grpc_prometheus.StreamServerInterceptor),
grpc.UnaryInterceptor(grpc_prometheus.UnaryServerInterceptor),
)
// Register your gRPC service implementations.
myservice.RegisterMyServiceServer(s.server, &myServiceImpl{})
// After all your registrations, make sure all of the Prometheus metrics are initialized.
grpc_prometheus.Register(myServer)
// Register Prometheus metrics handler.
http.Handle("/metrics", promhttp.Handler())
...
I'm a former Google engineer working at another company now, and we use http/json rpc here. This RPC is the single highest consumer of cpu in our clusters, and our scale isn't all that large. I'm moving over to gRPC asap, for performance reasons.
https://news.ycombinator.com/item?id=12344995
BenchmarkGRPCProtobuf 197919 ns/op
BenchmarkJSONHTTP 1720124 ns/op
http://pliutau.com/benchmark-grpc-protobuf-vs-http-json/
gRPC/HTTP2/Protobuf | REST/HTTP1/JSON |
---|---|
statically typed | error prone |
fully multiplexed | ordered and blocking |
backwards compatible | forced to version |
fast (binary, hpack) | slow (textual, overhead) |
push from server to client | polling |
generated code | development time |
cost saving | consumes all the cpus |