Date post: | 15-Apr-2017 |
Category: |
Software |
Upload: | alexey-tokar |
View: | 100 times |
Download: | 0 times |
One API to serve them allfor free. no SMS, no registration
Alexey Tokar
Head of Development @ WorldApp
What this talk is not about
2
HTTP method semantics
GET - for listPOST - for create
PATCH - for partial updateDELETE - for delete
PUT - for update3
Resources name convention
/programmer/42VS
/programmers/424
HTTP status code meaning
1xx Informational2xx Success3xx Redirection4xx Client Error5xx Server Error
5
HATEoAS architecture
Hypermedia
as the
Engine
of
Application
State
6
What we are going to coverDifferent devices and environmentsApplication and API designReal behavior of services and our expectations
7
What our goals are?Blazing fast processing and respondingAwesome stability and expected behaviorLimitless scalability Tremendous flexibility in operation and support
8
Two major API design
9
N device types : N APIs
10
Pros & consEasy to manageOptimized for deviceSimple codebase
11
Exponential diversityHard to deployLong way to fix
N device types : 1 API
12
Pros & consEasy to manageOptimized for deviceSimple codebaseEasy to deployShort way to fixLinear diversity :)
13
Reduced flexibility
One API to serve them all
14
We don’t need that sheet!
15
Please welcome our resources!GET /posts/1{
id : "/posts/1",
title : "title",
created: "2016-10-16T10:00:00Z",
author : {
href: "/users/2"
},
similar: {
href: "/posts/1/similar"
}
}
GET /users/2{
id : "/users/2",
name: "alexey",
dob: "1986-08-14"
}
16Looks like HATEoAS
We need only part of your Data
17
Did you know...1 Twitter post thought REST API takes about 5KiBTransferring 20KiB over 3G will last approximately
20ms
Average type speed is 120wpm for keyboards and 30wpm for mobile devices* (100ms and 400ms per character)
Even level 1 gzip could save up to x15 of your REST API traffic 18
* http://www.pocketables.com/archives/mobile-device-keyboard
?fields=<fieldsList>GET /posts/1?fields=title,id{
id : "/posts/1",
title : "title"
}
19http://drawingimage.com/files/1/Llama-Realistic-Drawing.jpg
Ordinary Service could look like
@Serviceclass UserService { public User find( String id, Set<Field> fields ) { //... }}
20
LimitationsViolation of the IoC principleHard to manage consistency of new servicesNeed to rewrite all the codebase
21
Possible data path
22
Easy to manage solutionList<String> fields = Arrays.asList( "id", "title" );
ObjectMapper mapper = new ObjectMapper();JsonNode tree = mapper.readTree( response );
Iterator<Map.Entry<String, JsonNode>> it = tree.fields();while ( it.hasNext() ) { Map.Entry<String, JsonNode> entry = it.next(); String key = entry.getKey(); if ( !fields.contains( key ) ) { it.remove(); }}
23
Give us all we need
24
Different data on a page
25
Optimal solutions
26
SPDY
HTTP/2With multiplexing
HTTP 1.1With keep-alive
How Big Guys do it?POST /batch HTTP/1.1Authorization: Bearer your_auth_tokenHost: www.googleapis.comContent-Type: multipart/mixed; boundary=batch_foobarbazContent-Length: total_content_length
--batch_foobarbazContent-Type: application/httpGET /farm/v1/animals/pony
--batch_foobarbazContent-Type: application/httpGET /farm/v1/animals--batch_foobarbaz-- 27
Semantically correctvs
Hard to use
28
/batchPOST /batch{
myProfile : "/users/me",
post : "/posts/1?fields=id,author,href"
}
{
myProfile : {
id : "/users/2",
name: "alexey",
dob: "1986-08-14"
},
post : {
id : "/posts/1",
author : {
href: "/users/2"
}
}
}29
Simple data proxyObjectNode tree = mapper.createObjectNode();
for ( Map.Entry<String, String> nameToUrl : map.entrySet() ) { tree.set( nameToUrl.getKey(), mapper.readTree( new URL( request.getScheme(), request.getServerName(), request.getServerPort(), nameToUrl.getValue() ) ) );}
30
Please, be so kind to include relations
31
Data related to main content
32
?include=<subObject>GET /posts/1?include=author{
id : "/posts/1",
title : "title",
created: "2016-10-16T10:00:00Z",
author : {
id : "/users/2",
name: "alexey",
dob: "1986-08-14"
},
similar: {
href: "/posts/1/similar"
}
} 33
GET /posts/1{
id : "/posts/1",
title : "title",
created: "2016-10-16T10:00:00Z",
author : {
href: "/users/2"
},
similar: {
href: "/posts/1/similar"
}
}
Altogether!POST /batch{
part1 : “/resource/24
? include=some,sub,resource
& fields=only,what,you,need”
}
34
Why it’s a good solution?Only one set of resourcesAsynchronization on the backendOnly one HTTP request per screen with only needed
dataScalable and manageable solutionSingle entrance and exit pointSimple services, dao and dtoCould be adapted to any* existing API
35
Right questionsWhy you are not using Protobuf or Flatbuffers?
Good json serialization library + gzip + etag negate the difference. Also JSON is still the most popular data format for APIs.
You are talking about performance but still make heavy DB queries.
Yes, but this is a tradeoff for simplification and ease of support. We still could serve 10K+ rps per node.
Why internal dispatching is more preferable than custom monster-join approach?
It is simpler to add more servers for our services and tweak performance that way instead of trying to fix each complex-query performance. But this is another discussable tradeoff :)
36
Questions?Alexey Tokar
Head of Development @ WorldApp
38