Earning an "A" in YSlow

  • 747 views
Uploaded on

In which we examine common website performance optimization techniques, and how you can implement them in your Ruby on Rails application.

In which we examine common website performance optimization techniques, and how you can implement them in your Ruby on Rails application.

  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Be the first to comment
    Be the first to like this
No Downloads

Views

Total Views
747
On Slideshare
0
From Embeds
0
Number of Embeds
0

Actions

Shares
Downloads
4
Comments
0
Likes
0

Embeds 0

No embeds

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
    No notes for slide
  • \n \n
  • \n \n
  • \n \n
  • \n \n
  • \n \n
  • \n \n
  • \n \n
  • \n \n
  • \n \n
  • \n \n
  • \n \n
  • \n \n
  • \n \n
  • \n \n
  • \n \n
  • \n \n
  • \n \n
  • \n \n
  • \n \n
  • \n \n
  • \n \n
  • \n \n
  • \n \n
  • \n \n
  • \n \n
  • \n \n
  • \n \n
  • \n \n
  • \n \n
  • \n \n
  • \n \n
  • \n \n
  • \n \n
  • \n \n
  • \n \n
  • \n \n
  • \n \n
  • \n \n
  • \n \n
  • \n \n
  • \n \n
  • \n \n
  • \n \n
  • \n \n
  • \n \n

Transcript

  • 1. GETTING AN “A” IN YSLOW Web Performance Optimization 101 Nick Zadrozny <nick@beyondthepath.com>
  • 2. HI, MY NAME IS NICK. I MAKE WEBSITES.
  • 3. I’M EMBARRASSED MY WEBSITES ARE SLOW
  • 4. <METAPHOR>
  • 5. You’re driving a brand new carIt’s moving really slowlyYou can smell smokePeople are giving you looks
  • 6. You’re driving a brand new carIt’s moving really slowly Return the carYou can smell smoke Rebuild the carPeople are giving you looks Disengage the hand brake
  • 7. D’oh!
  • 8. </METAPHOR>
  • 9. THE POINTLOW-HANGING FRUIT…
  • 10. YAHOO!THEY MAKE WEBSITES
  • 11. Yahoo! 34 “Best Practices” for Exceptional Performance1. Minimize HTTP 9. Reduce DNS Lookups 19. Reduce the Number of 27. Choose <link> over Requests DOM Elements @import 10. Minify JavaScript and2. Use a Content CSS 20. Split Components 28. Avoid Filters Delivery Network Across Domains 11. Avoid Redirects 29. Optimize Images3. Add an Expires or a 21. Minimize the Number Cache-Control 12. Remove Duplicate of iframes 30. Optimize CSS Sprites Header Scripts 22. No 404s 31. Don’t Scale Images in4. Gzip Components 13. Configure ETags HTML 23. Reduce Cookie Size5. Put Stylesheets at the 14. Make Ajax Cacheable 32. Make favicon.ico Top 24. Use Cookie-free Small and Cacheable 15. Flush the Buffer Early Domains for6. Put Scripts at the Components 33. Keep Components 16. Use GET for AJAX under 25K (mobile) Bottom Requests 25. Minimize DOM7. Avoid CSS Access 34. Pack Components 17. Post-load into a Multipart Expressions Components 26. Develop Smart Event Document (mobile)8. Make Javascript and 18. Preload Components Handlers CSS External
  • 12. YSLOWPLUGIN FOR FIREBUG
  • 13. YSlow Tests Your Site on 13 RulesMinimize HTTP Put Stylesheets at Reduce DNSRequests the Top LookupsUse a Content Put Scripts at the Minify JavaScriptDelivery Network Bottom and CSSAdd an Expires Avoid CSS Avoid Redirectsor a Cache- ExpressionsControl Header Remove Make Javascript Duplicate ScriptsGzip and CSS ExternalComponents Configure ETags
  • 14. MINIMIZE HTTP REQUESTS
  • 15. Minimize HTTP Requests HTTP Requests are Expensive! Net work latency + server latency + net work latency + download time. This rule is fundamental, the Catch-All. Everything else is based on it. Asset packaging reduces overall number of requests. Compression & minification reduce the size of responses. Reordering includes lets important stuff go first. ETags and Last-Modified checks reduce the content of an HTTP Request. Browser caching completely eliminates subsequent requests. Major win.
  • 16. MAKE JAVASCRIPTAND CSS EXTERNAL
  • 17. Make Javascript and CSS ExternalEasier to maintain, less HTML to sendBrowser caching prevents later requestsHelp order requests by priorityRails makes this pretty easy,stylesheet_link_tagjavascript_include_tag
  • 18. MINIFY JAVASCRIPT AND CSS
  • 19. Minify Javascript and CSS≈20% size reduction html,body,div,span,applet,object,iframe,h1,h2 ,h3,h4,h5,h6,p,blockquote,pre,a,abbr,acronym, address,big,cite,code,del,dfn,em,font,img,insUsing a framework? You’re ,kbd,q,s,samp,small,strike,strong,sub,sup,tt, var,b,u,i,center,dl,dt,dd,ol,ul,li,fieldset,f orm,label,legend,table,caption,tbody,tfoot,thprobably halfway there. ead,tr,th,td{margin:0;padding:0;border: 0;outline:0;font-size:100%;vertical- align:baseline;background:transparent} body{line-height:1}ol,ul{list-style:none}YUI Compressor + Rake blockquote,q{quotes:none}blockquote:before,bl ockquote:after,q:before,q:after{content:;co ntent:none}:focus{outline:0}ins{text- decoration:none}del{text-decoration:line- through}table{border-collapse:collapse; border-spacing:0}
  • 20. STYLESHEETS AT THE TOPSCRIPTS AT THE BOTTOM
  • 21. Stylesheets at the top Scripts at the bottom <!DOCTYPE html>Javascript shouldn’t block <html> <head>more important rendering <title>Your Awesome Site</title> <%= stylesheet_link_tag application %> <%= yield(:head) %> </head> <body>Pass blocks to the head and <div id="page"> <header>foot from within your views: <h1>Awesome Site</h1> </header> <article> <%= yield %><% content_for(:foot) do %> </article> <%= javascript_tag "…" %> </div> <%= javascript_include_tag application %><% end %> <%= yield(:foot) %> </body> </html>
  • 22. GZIP COMPONENTS
  • 23. GZip ComponentsAnother nice size reductionMost devices support itCSS, Javascript, text/html,text/plain
  • 24. SOME REALLY EASY ONES…
  • 25. AVOID CSS EXPRESSIONS
  • 26. AVOID REDIRECTS
  • 27. REMOVE DUPLICATE SCRIPTS
  • 28. REDUCE DNS LOOKUPS
  • 29. Apache gives this for free for most of yourassets.Rails gives this for free for all of theresponses it sends back. It also has somehelpers that will let you potentially skip dbcalls and expensive view rendering. CONFIGURE ETAGS
  • 30. Configure ETagsWeb servers likely give you a lot for freeRails gives you some ETagging for freeRails ETag and Last-Modified helperdef show @article = Article.find(params[:id]) if stale?(:etag => @article, :last_modified => @article.created_at.utc) @statistics = @article.really_expensive_call respond_to do |format| # etc end endend
  • 31. ADD AN EXPIRES ORCACHE-CONTROL HEADER
  • 32. Add an Expires HeaderCheck out mod_expires for ApacheRails already does “soft” cache bustingHard cache busting (for CDNs)…config.action_controller.asset_host ="http://assets.example.com/#{REVISION}"
  • 33. USE A CDN
  • 34. #!/usr/bin/env rubyrequire File.dirname(__FILE__) + /../config/environmentrequire aws/s3require progressbar# Set up the S3 configuration and connectionconfig = YAML.load(File.open(File.join(RAILS_ROOT, config/amazon_s3.yml)))[RAILS_ENV]bucket = config[bucket_name]AWS::S3::Base.establish_connection!( :access_key_id => config[access_key_id], :secret_access_key => config[secret_access_key])# Grab everything in the public directoryfiles = Dir.glob(File.join(RAILS_ROOT, public/**/*/**/*.{css,js,gif,jpg,png,…}))# Set up the progressbarprogressbar = ProgressBar.new("Uploading...", files.size)# Iterate through the filesfiles.each do |file| # Skip directories themselves - were only interested in files unless File.directory?(file) # Create an object key from the file name and revision key = file.gsub(/.*public//,"#{REVISION}/") # Upload the object to S3 # TODO: make sure were setting the right headers for gzipped objects AWS::S3::S3Object.store( key, open(file), bucket, :access => :public_read, :expires => 1.year.from_now ) # Increment the progressbar progressbar.inc endend
  • 35. #!/usr/bin/env rubyrequire File.dirname(__FILE__) + /../config/environmentrequire aws/s3require progressbar# Set up the S3 configuration and connectionconfig = YAML.load(File.open(File.join(RAILS_ROOT, config/amazon_s3.yml)))[RAILS_ENV]bucket = config[bucket_name]AWS::S3::Base.establish_connection!( :access_key_id => config[access_key_id], :secret_access_key => config[secret_access_key])# Grab everything in the public directoryfiles = Dir.glob(File.join(RAILS_ROOT, public/**/*/**/*.{css,js,gif,jpg,png,…}))# Set up the progressbarprogressbar = ProgressBar.new("Uploading...", files.size)# Iterate through the filesfiles.each do |file| # Skip directories themselves - were only interested in files unless File.directory?(file) # Create an object key from the file name and revision key = file.gsub(/.*public//,"#{REVISION}/") # Upload the object to S3 # TODO: make sure were setting the right headers for gzipped objects AWS::S3::S3Object.store( key, open(file), bucket, :access => :public_read, :expires => 1.year.from_now ) # Increment the progressbar progressbar.inc endend
  • 36. #!/usr/bin/env rubyrequire File.dirname(__FILE__) + /../config/environmentrequire aws/s3require progressbar# Set up the S3 configuration and connectionconfig = YAML.load(File.open(File.join(RAILS_ROOT, config/amazon_s3.yml)))[RAILS_ENV]bucket = config[bucket_name]AWS::S3::Base.establish_connection!( :access_key_id => config[access_key_id], :secret_access_key => config[secret_access_key])# Grab everything in the public directoryfiles = Dir.glob(File.join(RAILS_ROOT, public/**/*/**/*.{css,js,gif,jpg,png,…}))# Set up the progressbarprogressbar = ProgressBar.new("Uploading...", files.size)# Iterate through the filesfiles.each do |file| # Skip directories themselves - were only interested in files unless File.directory?(file) # Create an object key from the file name and revision key = file.gsub(/.*public//,"#{REVISION}/") # Upload the object to S3 # TODO: make sure were setting the right headers for gzipped objects AWS::S3::S3Object.store( key, open(file), bucket, :access => :public_read, :expires => 1.year.from_now ) # Increment the progressbar progressbar.inc endend
  • 37. #!/usr/bin/env rubyrequire File.dirname(__FILE__) + /../config/environmentrequire aws/s3require progressbar# Set up the S3 configuration and connectionconfig = YAML.load(File.open(File.join(RAILS_ROOT, config/amazon_s3.yml)))[RAILS_ENV]bucket = config[bucket_name]AWS::S3::Base.establish_connection!( :access_key_id => config[access_key_id], :secret_access_key => config[secret_access_key])# Grab everything in the public directoryfiles = Dir.glob(File.join(RAILS_ROOT, public/**/*/**/*.{css,js,gif,jpg,png,…}))# Set up the progressbarprogressbar = ProgressBar.new("Uploading...", files.size)# Iterate through the filesfiles.each do |file| # Skip directories themselves - were only interested in files unless File.directory?(file) # Create an object key from the file name and revision key = file.gsub(/.*public//,"#{REVISION}/") # Upload the object to S3 # TODO: make sure were setting the right headers for gzipped objects AWS::S3::S3Object.store( key, open(file), bucket, :access => :public_read, :expires => 1.year.from_now ) # Increment the progressbar progressbar.inc endend
  • 38. #!/usr/bin/env rubyrequire File.dirname(__FILE__) + /../config/environmentrequire aws/s3require progressbar# Set up the S3 configuration and connectionconfig = YAML.load(File.open(File.join(RAILS_ROOT, config/amazon_s3.yml)))[RAILS_ENV]bucket = config[bucket_name]AWS::S3::Base.establish_connection!( :access_key_id => config[access_key_id], :secret_access_key => config[secret_access_key])# Grab everything in the public directoryfiles = Dir.glob(File.join(RAILS_ROOT, public/**/*/**/*.{css,js,gif,jpg,png,…}))# Set up the progressbarprogressbar = ProgressBar.new("Uploading...", files.size)# Iterate through the filesfiles.each do |file| # Skip directories themselves - were only interested in files unless File.directory?(file) # Create an object key from the file name and revision key = file.gsub(/.*public//,"#{REVISION}/") # Upload the object to S3 # TODO: make sure were setting the right headers for gzipped objects AWS::S3::S3Object.store( key, open(file), bucket, :access => :public_read, :expires => 1.year.from_now ) # Increment the progressbar progressbar.inc endend
  • 39. #!/usr/bin/env rubyrequire File.dirname(__FILE__) + /../config/environmentrequire aws/s3require progressbar# Set up the S3 configuration and connectionconfig = YAML.load(File.open(File.join(RAILS_ROOT, config/amazon_s3.yml)))[RAILS_ENV]bucket = config[bucket_name]AWS::S3::Base.establish_connection!( :access_key_id => config[access_key_id], :secret_access_key => config[secret_access_key])# Grab everything in the public directoryfiles = Dir.glob(File.join(RAILS_ROOT, public/**/*/**/*.{css,js,gif,jpg,png,…}))# Set up the progressbarprogressbar = ProgressBar.new("Uploading...", files.size)# Iterate through the filesfiles.each do |file| # Skip directories themselves - were only interested in files unless File.directory?(file) # Create an object key from the file name and revision key = file.gsub(/.*public//,"#{REVISION}/") # Upload the object to S3 # TODO: make sure were setting the right headers for gzipped objects AWS::S3::S3Object.store( key, open(file), bucket, :access => :public_read, :expires => 1.year.from_now ) # Increment the progressbar progressbar.inc endend
  • 40. #!/usr/bin/env rubyrequire File.dirname(__FILE__) + /../config/environmentrequire aws/s3require progressbar# Set up the S3 configuration and connectionconfig = YAML.load(File.open(File.join(RAILS_ROOT, config/amazon_s3.yml)))[RAILS_ENV]bucket = config[bucket_name]AWS::S3::Base.establish_connection!( :access_key_id => config[access_key_id], :secret_access_key => config[secret_access_key])# Grab everything in the public directoryfiles = Dir.glob(File.join(RAILS_ROOT, public/**/*/**/*.{css,js,gif,jpg,png,…}))# Set up the progressbarprogressbar = ProgressBar.new("Uploading...", files.size)# Iterate through the filesfiles.each do |file| # Skip directories themselves - were only interested in files unless File.directory?(file) # Create an object key from the file name and revision key = file.gsub(/.*public//,"#{REVISION}/") # Upload the object to S3 # TODO: make sure were setting the right headers for gzipped objects AWS::S3::S3Object.store( key, open(file), bucket, :access => :public_read, :expires => 1.year.from_now ) # Increment the progressbar progressbar.inc endend
  • 41. #!/usr/bin/env rubyrequire File.dirname(__FILE__) + /../config/environmentrequire aws/s3require progressbar# Set up the S3 configuration and connectionconfig = YAML.load(File.open(File.join(RAILS_ROOT, config/amazon_s3.yml)))[RAILS_ENV]bucket = config[bucket_name]AWS::S3::Base.establish_connection!( :access_key_id => config[access_key_id], :secret_access_key => config[secret_access_key])# Grab everything in the public directoryfiles = Dir.glob(File.join(RAILS_ROOT, public/**/*/**/*.{css,js,gif,jpg,png,…}))# Set up the progressbarprogressbar = ProgressBar.new("Uploading...", files.size)# Iterate through the filesfiles.each do |file| # Skip directories themselves - were only interested in files unless File.directory?(file) # Create an object key from the file name and revision key = file.gsub(/.*public//,"#{REVISION}/") # Upload the object to S3 # TODO: set the right headers for gzipped objects AWS::S3::S3Object.store( key, open(file), bucket, :access => :public_read, :expires => 1.year.from_now ) # Increment the progressbar progressbar.inc endend
  • 42. #!/usr/bin/env rubyrequire File.dirname(__FILE__) + /../config/environmentrequire aws/s3require progressbar# Set up the S3 configuration and connectionconfig = YAML.load(File.open(File.join(RAILS_ROOT, config/amazon_s3.yml)))[RAILS_ENV]bucket = config[bucket_name]AWS::S3::Base.establish_connection!( :access_key_id => config[access_key_id], :secret_access_key => config[secret_access_key])# Grab everything in the public directoryfiles = Dir.glob(File.join(RAILS_ROOT, public/**/*/**/*.{css,js,gif,jpg,png,…}))# Set up the progressbarprogressbar = ProgressBar.new("Uploading...", files.size)# Iterate through the filesfiles.each do |file| # Skip directories themselves - were only interested in files unless File.directory?(file) # Create an object key from the file name and revision key = file.gsub(/.*public//,"#{REVISION}/") # Upload the object to S3 # TODO: make sure were setting the right headers for gzipped objects AWS::S3::S3Object.store( key, open(file), bucket, :access => :public_read, :expires => 1.year.from_now ) # Increment the progressbar progressbar.inc endend
  • 43. THAT’S IT!
  • 44. ARTICLE FORTHCOMING BEYOND THE PATH.COM