mobile app performance: getting the most from apis (mbl203) | aws re:invent 2013
DESCRIPTION
(Presented by New Relic) Too often, developers think of a mobile app as simply code running on the device. A mobile app is much more than that. Every web API used by an app becomes as much a part of the app as the code running on the device. But while mobile developers have control over their code, they don't always have control over the APIs they use. Web APIs and their infrastructure impact app performance and ultimately the user experience. This presentation covers some of the essential aspects of app performance management when web APIs are present, including: -HTTP headers are your friend--stop ignoring all they have to tell you -Control your network connections on the device—don't just leave things to the OS -Configure all your caches and use them -Whatever you do, measure early and often The session includes a customer story from the CTO of Mirego, and demos of New Relic mobile app performance monitoring, where you see how to drill down into specific requests to see performance by response time, throughput, and data transfer size.TRANSCRIPT
© 2013 Amazon.com, Inc. and its affiliates. All rights reserved. May not be copied, modified, or distributed in whole or in part without the express consent of Amazon.com, Inc.
Pierre-Luc Simard, Mirego
November 14, 2013
Mobile Application Performance:
Getting The Most From APIs
Who’s that guy?
Pierre-Luc Simard: CTO @
@pierrelucsimard
Software Development
Web Applications
Mobile Applications
Software Project Management
Programming Security
Application Security
Agile
Software Engineering
Embedded Systems
Information Security Ruby
Android
Enterprise Software Java Objective-C Client Counseling iOS
Scrum Rails Perl REST
Linux Helping Clients Succeed
Object Oriented Design Mobile
Mobile application performance.
Mobile application session.
1 4
minutes
Average response time on mobile.
Does it mean...
seconds per session second per request requests per session
150 1.24 ÷ = 120
Some informal testing.
Check my balance 43 requests ~70 seconds
Check-in 46 requests ~30 seconds
Open a message 24 requests ~40 seconds
AVERAGE
37.6 requests/action
The lifespan of an HTTP request.
Time spent on the network Time spent
processing
Sessions might not get longer but...
Here Better world
Fewer requests Smaller payload
How to get there?
Be specific about caching using HTTP headers.
Cache on the device as much as you
can.
Optimize the remaining requests.
Be specific with HTTP headers.
The conversation with the browser.
Here’s what you
asked for. Thank you. I will
keep it for a while.
The conversation with a mobile app.
Here’s what you
asked for.
Thank you. I will
discard this right away.
Be declarative.
Here’s what you
asked for. Please
keep it for a while.
Thank you. I’ll keep it
for a while.
For cache to work:
ONE ONE = object URL
GET / HTTP/1.1
Host: flipbit.dev
If-Modified-Since: Thu, 10 Oct 2013 20:14:48 GMT
If-None-Match: "a1846ac92492d2347c6235b4d2611184"
HTTP/1.1 200 OK
Date: Thu, 17 Oct 2013 01:42:00 GMT
Last-Modified: Thu, 17 Oct 2013 01:38:57 GMT
ETag: "b79f914dde3ab5e3095372de46591b46"
Expires: Thu, 24 Oct 2013 01:38:57 GMT
Cache-Control: max-age=604800, private
Accept-Ranges: bytes
Content-Type: application/octet-stream
Content-Length: 42
Connection: keep-alive
Declare using HTTP headers.
Server controls the cache.
• ETag describes dynamic content where modification
date are impractical.
• Last-Modified for everything else.
• Cache-Control controls when the client should
revalidate.
• Expires is the easy way to dictate validation.
Client validates.
• If-None-Match to match against the object’s ETag in
cache.
• If-Modified to match against last version received.
Example: caching data from
Amazon S3.
GET /recurringvoid.PublicFiles/welcome.json HTTP/1.1
host: s3.amazonaws.com
HTTP/1.1 200 OK
x-amz-id-2: UleVE3wbOPZa2EW+RpWj834yy5OOMLNmi/w+JbnbBN8rhSg1Bw9682c7XuAgmI38
x-amz-request-id: 4ADBB2EBF80F5D46
Date: Thu, 17 Oct 2013 01:42:00 GMT
Last-Modified: Thu, 17 Oct 2013 01:38:57 GMT
ETag: "b79f914dde3ab5e3095372de46591b46"
Accept-Ranges: bytes
Content-Type: application/octet-stream
Content-Length: 23
Connection: keep-alive
Server: AmazonS3
Is it going to be cached on mobile?
It depends!
• There’s no cache-control or expires header so it’s left
to the client to decide.
• iOS most likely will cache between 6 hours and 1 day
and will always revalidate.
• Android it depends...
PUT /bucket/flipbit.json HTTP/1.1
Host: s3.amazonaws.com
x-amz-meta-Cache-Control : max-age=604800, public
[...]
x-amz-meta-Cache-Control : max-age=<value in seconds>,
<public | private>,
<no-chache / no-store no-transform>,
<must-revalidate>
Don’t let the client decide. Specify!
Example: Ruby on Rails application.
Rails is just an example.
• Ruby on Rails is easy and widespread.
• Setting it up on AWS Elastic Beanstalk is easy.
• There’s a free tier to test out and play with and it
grows if you like it.
• https://github.com/p-l/flipbit as my example code.
$ rails new flipbit
$ git init .
$ git add .; git commit -m "brand new rails 4 application"
$ eb init
$ git add .; git commit -m "exclude .elasticbeanstalk from git"
$ eb start
$ git aws.push
Rails on AWS Elastic Beanstalk:
Cache-Control: max-age=0, private, must-revalidate
Content-Type: application/json; charset=utf-8
ETag: "4a200c9d8fb14925e7461d63b59f4e82"
Server: nginx/1.2.3 + Phusion Passenger 3.0.17 (mod_rails/mod_rack)
Set-Cookie: request_method=GET; path=/
Status: 200
X-Content-Type-Options: nosniff
X-Frame-Options: SAMEORIGIN
X-Powered-By: Phusion Passenger (mod_rails/mod_rack) 3.0.17
X-Request-Id: 2555daec-d632-44ee-9add-d33821aa9218
X-Runtime: 0.004714
X-UA-Compatible: chrome=1
X-XSS-Protection: 1; mode=block
Content-Length: 144
Connection: Close
Is it going to be cached on mobile?
Cache-Control: max-age=0, private, must-revalidate
Not likely.
class FlipsController < ApplicationController
# ...
def index
# ...
# Specify Last-Modified and ETag
flipbit_etag = Digest::MD5.digest(@flipbit.to_s).to_s
fresh_when last_modified: DateTime.now, etag: flipbit_etag
# Specify Cache-Control header
expires_in 3.days, public: true, must_revalidate: false
# ...
end
Trying to fix it:
HTTP/1.1 200 OK
Cache-Control: max-age=86400, public
Content-Type: application/json; charset=utf-8
ETag: "7d4230001179d5852b567c238dbb8eb3"
Server: nginx/1.2.3 + Phusion Passenger 3.0.17 (mod_rails/mod_rack)
Set-Cookie: request_method=GET; path=/
Status: 200
X-Content-Type-Options: nosniff
X-Frame-Options: SAMEORIGIN
X-Powered-By: Phusion Passenger (mod_rails/mod_rack) 3.0.17
X-Request-Id: 870dd748-ba39-4ea6-afc0-6d14c4ac36ed
X-Runtime: 0.005363
X-UA-Compatible: chrome=1
X-XSS-Protection: 1; mode=block
Content-Length: 144
Now, will it be cached?
It depends!
• There’s no cache-control or expires header so it’s left
to the client to decide.
• iOS will likely cache between 6 hours and 1 day and
will always revalidate.
• Android it depends...
Caching on the device.
There’s a cache on the device?
Here’s what you
asked for. Please
keep it for a while.
Where am I going to
keep it?
Configure your caches.
Make sure you allocate enough space for caching.
Cache unique request.
Do not hard code cache logic.
Example: Caching data on iOS.
iOS is just an example.
• iOS is widespread.
• Setting up a Mac with XCode is easy.
• There’s a couple of ways to do it. But they mostly
depend on the same base API.
- (BOOL)application:(UIApplication *)application
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
NSUInteger mb = 1024*1024;
// Configure NSURLCache with larget storage
[NSURLCache setSharedURLCache:
[[NSURLCache alloc] initWithMemoryCapacity:(50*mb)
diskCapacity:(100*mb)
diskPath:nil]
];
// ...
// ...
return YES;
}
Make sure your cache is big enough.
NSMutableURLRequest* getRequest = [NSMutableURLRequest requestWithURL:getURL];
[getRequest setCachePolicy:NSURLRequestUseProtocolCachePolicy];
/*
Constant Meaning
-------------------------------------------------------------------------------
UseProtocolCachePolicy Default behavior. Respect what the server says.
ReloadIgnoringLocalCacheData Don't use the cache at all.
ReturnCacheDataElseLoad Use the cache no matter how much it’s out of
date. If there’s no cached response exists then
load from the network.
ReturnCacheDataDontLoad Offline mode. Use the cache and never load from
the network.
*/
iOS caching policies.
NSURL* getURL = [NSURL URLWithString:@"http://flipbit.dev/"];
NSMutableURLRequest* getRequest = [NSMutableURLRequest requestWithURL:getURL];
// Add If-None-Match header manually
[getRequest addValue:@"<Object’s ETag>" forHTTPHeaderField:@"If-None-Match"];
Use ETag.
Yes.
• There’s no cache-control or expires header so it’s left
to the client to decide.
• iOS will likely cache between 6 hours and 1 day and
will always revalidate.
• Android it depends...
Do more with less network requests.
No need. I’ve got this
cached already
Measure early, measure often.
What to measure.
Does your application meet its objectives?
How fast and how smoothly?
When to start measuring?
Before the feature / user-story is completed.
Measuring means knowing.
What’s going on.
If you’re meeting your objectives.
What maters.
How to make things better.
Making things better.
NewRelic demo.
Please give us your feedback on this
presentation
As a thank you, we will select prize
winners daily for completed surveys!
MBL203