2. AGENDA
2
Shortcomings and no-frills
What Data-Serialization Frameworks do
Architectural considerations
API, Speed and Size Battle
Back to binary encoded messages?
4. Standard Interface
Definitions languages
or hand-crafted binary
Protocols
4
History of binary serialization frameworks
80s
String-encoded Interfaces,
String encoded verification
String-encoded Protocols
90s
Renaissance of binary
protocols as backend-
turbocharger.
Just with more frills.
2000
Binary Serialization to
Turbocharge embedded
Devices
2010
5. Standard Interface
Definitions languages
or hand-crafted binary
Protocols
5
History of binary serialization frameworks
80s
String-encoded Interfaces,
String encoded verification
String-encoded Protocols
90s
Renaissance of binary
protocols as backend-
turbocharger.
Just with more frills.
2000
Binary Serialization to
Turbocharge embedded
Devices
2010>
ASN-1
Thrift
Protobuf
JSON
Avro
CBOR
lots_of
{custom_protocol}
6. Message length considerations for IoT appliances
6
Cloud Application IoT Solution
Powerful, scalable Servers Finite number of constrained devices
High amount of influence on technology stack Mixed technology stacks, processor architectures
and languages
High connections speeds Serial links, Industrial bus connections, Cellular
networks.
No restrictions on data transfer Low bandwidth and duty cycle regulations
Nodes available all the time Power Saving, Mobile Data Saving, possible bad
signal level
Data transfer is (almost) free of charge (Literally) expensive data transfer when mobile data
is involved.
Processing power for data transfer is neglectible Sophisticated String processing or data transfer in
resource competition with actual functionality
16. Generated Builder
API
Builder API or types
serializers/deserializers
based on Interface
definition
Generic Builder
API
Field-By-Field API
Abstracted-away
Workhorse
Serving as a Serializer
Option for i.E. Jackson
16
API types
17. 17
..it is not
A uint32 is a uint32…
Message with low int values telegram.
.setMaximumTorque(32)
.setTotalWork(32)
.build();
telegram.
.setMaximumTorque(65535)
.setTotalWork(65535)
.build();
2 8 20 21 0 0 0 0 0 0 40 40
(13 bytes)
4 8 FF FF 3 21 0 0 0 0 E0 FF EF 40
(15 bytes)
Message with high int values
22. 22
Non-opiniated with regards to transport
-Just an array of bytes
-No encryption or compression by default
-No transport or remote method methods by default
-Except Add-Ons (gRPC, AVRO RPC)
-Integrateable in existing transport infrastructure
-Raw sockets
-MQTT, AMQP, Kafka, …
-Machine bus protocols
-XMPP
-Local memory
29. Serialize a engine telemetry data item
as fast as possible
With as little data transfer as possible
Only no-brainer optimizations are
allowed
Given situation
29
A fictional telemetry use-case
EngineID Uuid
(String encoding if no native type)
Observation Interval Unix timestamp from, to
Total amount of work Double
Fuel consumption curve 32 float tupels (0,0)..(32,32)
Oil Pressure curve 32 float tupels (0,0)..(32,32)
Serialization in Java
32. 32
public void serializeProtobuf() {
Common.Curve.Builder sampleCurve = Common.Curve.newBuilder();
for (int x = 0 ; x < 32 ; x++) {
sampleCurve.addPoints(Common.Point.newBuilder().setX(x).setY(x).build());
}
EngineTelemetry.Telegram message= EngineTelemetry.Telegram.newBuilder()
.setTimeFrom(Timestamp.newBuilder().setSeconds(Instant.now().getEpochSecond()).build())
.setTimeTo(Timestamp.newBuilder().setSeconds(Instant.now().getEpochSecond()-3600).build())
.setTotalWork(42.23)
.setEngineId(UUID.randomUUID().toString())
.setFuelConsumption(sampleCurve)
.setOilPressure(sampleCurve)
.build();
byte[] proto = message.toByteArray();
}
Google Protocol Buffers
33. Google Protocol Buffers
33
0 |24 30 37 37 64 33 39 36 64 2D 38 37 31 32 2D 34 |$077d396 d-8712-4
10 |61 62 34 2D 62 62 34 31 2D 35 62 66 65 62 31 65 |ab4-bb41 -5bfeb1e
20 |32 33 34 66 63 12 6 8 D6 DF E9 DD 5 1A 6 8 |234fc
30 |C6 C3 E9 DD 5 21 3D A D7 A3 70 1D 45 40 3A F6 | != p E@:
40 | 2 A 0 A A D 0 0 80 3F 15 0 0 80 3F A | ? ?
50 | A D 0 0 0 40 15 0 0 0 40 A A D 0 0 | @ @
60 |40 40 15 0 0 40 40 A A D 0 0 80 40 15 0 |@@ @@ @
70 | 0 80 40 A A D 0 0 A0 40 15 0 0 A0 40 A | @ @ @
80 | A D 0 0 C0 40 15 0 0 C0 40 A A D 0 0 | @ @
90 |E0 40 15 0 0 E0 40 A A D 0 0 0 41 15 0 | @ @ A
A0 | 0 0 41 A A D 0 0 10 41 15 0 0 10 41 A | A A A
B0 | A D 0 0 20 41 15 0 0 20 41 A A D 0 0 | A A
C0 |30 41 15 0 0 30 41 A A D 0 0 40 41 15 0 |0A 0A @A
D0 | 0 40 41 A A D 0 0 50 41 15 0 0 50 41 A | @A PA PA
E0 | A D 0 0 60 41 15 0 0 60 41 A A D 0 0 | `A `A
F0 |70 41 15 0 0 70 41 A A D 0 0 80 41 15 0 |pA pA A
100 | 0 80 41 A A D 0 0 88 41 15 0 0 88 41 A | A A A
110 | A D 0 0 90 41 15 0 0 90 41 A A D 0 0 | A A
120 |98 41 15 0 0 98 41 A A D 0 0 A0 41 15 0 | A A A
130 | 0 A0 41 A A D 0 0 A8 41 15 0 0 A8 41 A | A A A
140 | A D 0 0 B0 41 15 0 0 B0 41 A A D 0 0 | A A
150 |B8 41 15 0 0 B8 41 A A D 0 0 C0 41 15 0 | A A A
160 | 0 C0 41 A A D 0 0 C8 41 15 0 0 C8 41 A | A A A
170 | A D 0 0 D0 41 15 0 0 D0 41 A A D 0 0 | A A
180 |D8 41 15 0 0 D8 41 A A D 0 0 E0 41 15 0 | A A A
190 | 0 E0 41 A A D 0 0 E8 41 15 0 0 E8 41 A | A A A
1A0 | A D 0 0 F0 41 15 0 0 F0 41 A A D 0 0 | A A
1B0 |F8 41 15 0 0 F8 41 42 F6 2 A 0 A A D 0 | A AB
1C0 | 0 80 3F 15 0 0 80 3F A A D 0 0 0 40 15 | ? ? @
1D0 | 0 0 0 40 A A D 0 0 40 40 15 0 0 40 40 | @ @@ @@
1E0 | A A D 0 0 80 40 15 0 0 80 40 A A D 0 | @ @
1F0 | 0 A0 40 15 0 0 A0 40 A A D 0 0 C0 40 15 | @ @ @
200 | 0 0 C0 40 A A D 0 0 E0 40 15 0 0 E0 40 | @ @ @
210 | A A D 0 0 0 41 15 0 0 0 41 A A D 0 | A A
220 | 0 10 41 15 0 0 10 41 A A D 0 0 20 41 15 | A A A
230 | 0 0 20 41 A A D 0 0 30 41 15 0 0 30 41 | A 0A 0A
240 | A A D 0 0 40 41 15 0 0 40 41 A A D 0 | @A @A
250 | 0 50 41 15 0 0 50 41 A A D 0 0 60 41 15 | PA PA `A
260 | 0 0 60 41 A A D 0 0 70 41 15 0 0 70 41 | `A pA pA
270 | A A D 0 0 80 41 15 0 0 80 41 A A D 0 | A A
280 | 0 88 41 15 0 0 88 41 A A D 0 0 90 41 15 | A A A
290 | 0 0 90 41 A A D 0 0 98 41 15 0 0 98 41 | A A A
// (cut)
2700ns 817 bytes
34. 34
Captain Proto
@0xcfe6e6668c89c78f;
using Java = import "/java.capnp";
$Java.package("de.m9d.telemetry.engine");
$Java.outerClassname("Telemetry");
struct Telegram
{
code @0 :Text;
engineId @1 :Text;
timeFrom @2 :UInt32;
timeTo @3 :UInt32;
totalWork @4 :Float64;
engineSpeed @5: Curve;
temperature @6: Curve;
oilPressure @7: Curve;
fuelConsumption @8: Curve;
}
struct Curve
{
points @0: List(Point);
}
struct Point
{
x @0: Float32;
y @1: Float32;
}
35. 35
Captain Proto
public void serializeCaptainProto() throws IOException {
String engineId = UUID.randomUUID().toString();
org.capnproto.MessageBuilder message =
new org.capnproto.MessageBuilder();
de.m9d.telemetry.engine.Telemetry.Telegram.Builder builder = message.initRoot(Telemetry.Telegram.factory);
builder.setEngineId(engineId);
builder.setTimeFrom((int) Instant.now().getEpochSecond());
builder.setTimeTo((int) Instant.now().getEpochSecond());
builder.setTotalWork((float)42.561);
builder.initEngineSpeed();
de.m9d.telemetry.engine.Telemetry.Curve.Builder engineSpeedBuilder = builder.getEngineSpeed();
engineSpeedBuilder.initPoints(32);
for (int idx = 0 ; idx < 32 ; idx ++) {
engineSpeedBuilder.getPoints().get(idx).setX(idx);
engineSpeedBuilder.getPoints().get(idx).setY(idx);
}
// Same for oil pressure
ByteArrayOutputStream bos = new ByteArrayOutputStream();
WritableByteChannel wbc = Channels.newChannel(bos);
org.capnproto.Serialize.write(wbc, message);
byte[] proto = bos.toByteArray();
}