Slide 1

Slide 1 text

BEYOND JSON @aq / Full Stack Fest / Sept 2015

Slide 2

Slide 2 text

I’m @aq HELLO!

Slide 3

Slide 3 text

beatsryetypes.com @beatsryetypes

Slide 4

Slide 4 text

CatskillsConf.com Oct 23-25, 2015

Slide 5

Slide 5 text

Ruby, JS, Go Performance Consulting quirkey.com/hireme

Slide 6

Slide 6 text

No content

Slide 7

Slide 7 text

Because there are more apps Inter-app Communication is more important than ever

Slide 8

Slide 8 text

HONEY, I SHRUNK THE SERVICE

Slide 9

Slide 9 text

To avoid misunderstanding An introduction to terms

Slide 10

Slide 10 text

The transportation mechanism for delivering information from one application to another. Protocol

Slide 11

Slide 11 text

Shorthand for Serialization Format, the specific way data is encoded and structured for delivery between applications. Format

Slide 12

Slide 12 text

The shape of the data and how it is delivered. Format over Protocol

Slide 13

Slide 13 text

Format over Protocol JSON over HTTP

Slide 14

Slide 14 text

A very Brief History of Inter-App Communication

Slide 15

Slide 15 text

Pre-Internet Days

Slide 16

Slide 16 text

Punchcards over Sneakernet

Slide 17

Slide 17 text

Early Internet Days

Slide 18

Slide 18 text

XML over HTTP

Slide 19

Slide 19 text

Nowish

Slide 20

Slide 20 text

JSON over HTTP

Slide 21

Slide 21 text

The New Default JSON over HTTP for ERRRRYTHING

Slide 22

Slide 22 text

Because. But, WHY?

Slide 23

Slide 23 text

It’s EZ

Slide 24

Slide 24 text

Break it down • Human Readable/Writable (Well certain humans) • Parsers/Clients for every Language • Relatively Fast • Has a few explicit types (String, #, Bool) • Compared to XML/SOAP, Relatively Compact

Slide 25

Slide 25 text

Like most things in our world. Well, It’s EZ at first.

Slide 26

Slide 26 text

It always starts small.

Slide 27

Slide 27 text

{ "id": "1234", "title": "Beyond JSON" }

Slide 28

Slide 28 text

{ "id": "1234", "title": "Beyond JSON”, "author": “Aaron Quint”, “created_at": “2015-08-01” }

Slide 29

Slide 29 text

{ "id": "1234", "title": "Beyond JSON”, "author": { "name": “Aaron Quint”, "title": “Chief Scientist” }, “favorites": [ { "id": “123”, “user”: “Kat Howard” } ], “created_at": “2015-08-01” }

Slide 30

Slide 30 text

{ "id": "1234", "title": "Beyond JSON”, "author": { "name": “Aaron Quint”, "title": “Chief Scientist” “company": { "id": "1334”, "name": “Quirkey NYC” } }, “favorites": [ { "id": “123”, “user”: { “id”: “152”, "name": “Kat Howard”, } } ], “created_at": “2015-08-01” }

Slide 31

Slide 31 text

No content

Slide 32

Slide 32 text

What we want • Works well in many languages • Relatively Fast • Relatively Compact • Has explicit types • +Handles Change Well

Slide 33

Slide 33 text

What if I told you this already exists?

Slide 34

Slide 34 text

that’s protobufs to you Protocol Buffers!

Slide 35

Slide 35 text

No content

Slide 36

Slide 36 text

Procotol?! Buffers?!

Slide 37

Slide 37 text

package app; message Meta { required string key = 1; required string value = 2; } message Image { optional string id = 1; optional bytes body = 2; optional string extension = 3; optional uint64 size = 4; optional uint32 width = 5; optional uint32 height = 6; optional string filename = 7; repeated Meta meta = 8; } message Request { required string command = 1; required Image image = 2; repeated Meta transform = 3; optional bool returnbody = 4; } message Response { required string status = 1; required string message = 2; optional Image image = 3; } app.proto app/*.pb.rb app.pb.go protoc —ruby_out protoc —go_out

Slide 38

Slide 38 text

image = App::Image.new(:id => "123") bytes = image.encode #=> bytes image = App::Image.decode(bytes) image.id #=> 123 package app; message Meta { required string key = 1; required string value = 2; } message Image { optional string id = 1; optional bytes body = 2; optional string extension = 3; optional uint64 size = 4; optional uint32 width = 5; optional uint32 height = 6; optional string filename = 7; repeated Meta meta = 8; } message Request { required string command = 1; required Image image = 2; repeated Meta transform = 3; optional bool returnbody = 4; } message Response { required string status = 1; required string message = 2; optional Image image = 3; } image := app.Image{Id: proto.String("123")} bytes, err := proto.Marshal(image) if err != nil { // ... } newImage := &app.Image{} err = proto.Unmarshal(bytes, newImage) if err != nil { // ... } newImage.GetId() #=> "123"

Slide 39

Slide 39 text

package app; message Meta { required string key = 1; required string value = 2; } message Image { optional string id = 1; optional bytes body = 2; optional string extension = 3; optional uint64 size = 4; optional uint32 width = 5; optional uint32 height = 6; optional string filename = 7; repeated Meta meta = 8; } message Request { required string command = 1; required Image image = 2; repeated Meta transform = 3; optional bool returnbody = 4; } message Response { required string status = 1; required string message = 2; optional Image image = 3; } explicit types repeated fields unique tags for backwards and forwards compatibility required fields nested types

Slide 40

Slide 40 text

==================== Sizes beefcake: 23b protobuf: 23b json: 71b yajl: 71b msgpack: 54b ------------------------------------------------- encode/beefcake 36.915k (± 5.1%) i/s - 184.808k encode/protobuf 34.186k (± 6.7%) i/s - 172.462k encode/json 75.959k (± 8.3%) i/s - 378.367k encode/yajl 81.132k (±11.4%) i/s - 407.199k encode/msgpack 74.240k (±38.1%) i/s - 316.250k Comparison: encode/yajl: 81132.2 i/s encode/json: 75959.2 i/s - 1.07x slower encode/msgpack: 74239.7 i/s - 1.09x slower encode/beefcake: 36915.1 i/s - 2.20x slower encode/protobuf: 34186.1 i/s - 2.37x slower ------------------------------------------------- decode/beefcake 33.048k (± 9.3%) i/s - 164.672k decode/protobuf 45.565k (± 8.7%) i/s - 226.838k decode/json 80.541k (±17.0%) i/s - 390.258k decode/yajl 92.441k (±18.2%) i/s - 442.662k decode/msgpack 126.193k (±12.7%) i/s - 624.618k Comparison: decode/msgpack: 126192.7 i/s decode/yajl: 92441.2 i/s - 1.37x slower decode/json: 80540.7 i/s - 1.57x slower decode/protobuf: 45565.1 i/s - 2.77x slower decode/beefcake: 33047.8 i/s - 3.82x slower https://github.com/quirkey/format_benchmark

Slide 41

Slide 41 text

Clear wins • Centralized Schema • Easy Serialization into objects • Handles removal/addition of fields easily • Real types + nesting • Compact (on the wire)

Slide 42

Slide 42 text

Downsides • Encoded output is not human readable (does that matter?) • Ruby encoding/decoding is not fast (it’s not that slow - wire matters more) • Ruby libraries are not the best (Support for other languages is great, though)

Slide 43

Slide 43 text

Protobufs over … But what about the Protocol?

Slide 44

Slide 44 text

Just stuff them in the body and call it a day. HTTP?

Slide 45

Slide 45 text

The simplest thing I could possibly think of within the bounds of NIH syndrome. TCPEZ http://github.com/paperlesspost/tcpez http://github.com/paperlesspost/tcpez-client

Slide 46

Slide 46 text

client server Header Body 4 PING Header Body 4 PONG

Slide 47

Slide 47 text

client server Header Body n Header Body n

Slide 48

Slide 48 text

TCPEZ • Client -> Server (Req/Resp) • So far Go server + Ruby client • Client is the load balancer • Request and Response encoding/decoding are not part of the protocol. • Each tcpez based project defines its own Request/ Response and these can be in any format • Built in by default: Good logging, Statsd, Protocol Buffers, Pipelined requests!

Slide 49

Slide 49 text

Multiple Apps, Requests in the 100M since 10/2013 TCPEZ is in production at Paperless Post

Slide 50

Slide 50 text

A quick aside …

Slide 51

Slide 51 text

http://research.google.com/pubs/pub36356.html Read Google’s Dapper Paper Building a distributed system without introspectability is a fools errand

Slide 52

Slide 52 text

No content

Slide 53

Slide 53 text

Clients for all (incl. Ruby) but server is Java only now GRPC = A layer above Protobufs over HTTP/2

Slide 54

Slide 54 text

New version of protobufs has new features: reserved tags, repeated compaction, importing, more … GRPC also relies on proto3

Slide 55

Slide 55 text

The Moral of the Story

Slide 56

Slide 56 text

Don’t accept the community defaults without checking out the alternatives

Slide 57

Slide 57 text

@aq quirkey.com github.com/quirkey THANKS