good points. this thread should be pinned. Or includen in the book, wiki or similar.
On Sunday, September 13, 2015 at 5:44:01 PM UTC-5, Louis Amon wrote: > > I'm opening a thread dedicated to website performance issues in web2py, > giving the answers I've got so far so that other may contribute as well. > > > Static assets (fonts, vendor CSS & JS) > > How do you manage & distribute these ? > > Fonts are easiest to manage using Google Fonts > <https://www.google.com/fonts>. This tool will generate an optimized file > load management through Google's CDNs. > > If at all possible, your static files need to be compiled into one big > bundle per page (main.css & main.js) so that you can optimize its delivery. > This can be done using Bower to download the source code and Gulp or Grunt > to concatenate it into one file. > > Otherwise, I'd recommend using the minified version of your vendors and > using your own CDN for delivery, as publicly available ones aren't very > reliable in terms of speed. > > > Dynamic assets (fast-changing CSS & JS, e.g : your main.css & main.js) > > The challenge here is minification, variable replacement for JS (if you're > not using AngularJS), compression & versioning. > > Grunt & Gulp can take care of all this through a deployment task system, > and you can get a manifest in JSON format giving you the versioned filename > for each of your generated files. > See gulp-rev-all <https://github.com/smysnk/gulp-rev-all> for the > versioning / manifest generation part. > > Personally, I'm using Gulp to generate the minified bundles and then I'm > using Django's collectstatic combined with Whitenoise to generate a gzipped > & versioned file for all of my assets (I can go into details on how to > combine Django's collectstatic with Web2py in another thread if someone is > interested) > > > Static images > > Google PageSpeed Insights > <https://developers.google.com/speed/pagespeed/insights> recommends that > you resize & compress your images as much as can be done. > > Compressing is a matter of using higher compression indices when > generating your output files in .png or .jpg formats (using Photoshop, > GIMP...), as these algorithmically include a compression. > In case you would need to use a format different than these, I'd recommend > gzipping... but really you don't wanna stray too much from PNG & JPG. > > As far as I know, resizing has to be done manually since no machine can > know what the maximum viewable size of your content may be (especially with > responsive websites). It is a sensible step that requires a lot of human > interaction from your webdesigner. > > Compression, on the other hand, can be done automatically with a gulp task > <https://www.npmjs.com/package/gulp-image-optimization>. > > > Dynamic images (uploads) > > Again, they need to be optimized in terms of size & compression. > > Resizing can be easilly done in web2py with a computed field (i.e. > something like Field('image_resized', 'upload', compute=THUMB(200,200), > uploadfolder='uploads/resized')). > > Compression can be done in the compute fonction I guess, but I confess I > haven't spent much time on this yet. > The Python Imaging Library, forked as Pillow > <https://python-pillow.github.io/> these days, has all sorts of functions > to achieve that. > > > HTML responses > > Once you've compressed static assets, Google PageSpeed will (rightfully) > nag you about compressing your HTML response itself. > > Web2py includes a contribution for html minification > (contrib/minify/html_minify.py) which can be used easilly with a decorator. > > I've created a small decorator than handles compression: > > import zlib > > def deflate(func): > > def _f(): > > out = func() > > render = response.render(out) if isinstance(out, dict) else out > > if 'deflate' in request.env.http_accept_encoding: > > response.headers['Content-Encoding'] = 'deflate' > > return zlib.compress(render) > > else: > > return render > > return _f > > > Using these decorators has to be done in the right order (minification, > then compression) otherwise you'll run into trouble :) > > Here's how to do it : > > @deflate > @minify > def index(): > return dict() > > > I've managed to reduce the size of my homepage's HTML code from 36.6KB to > 7.6KB using both minification & compression. > > Please note that these decorators do mean a CPU overhead for your > webserver. > It is up to you to use one or both depending on what kind of hardware your > server runs on. > There is also the possibility of using a caching strategy to reduce > processing time. I'd recommend web2py's @cache.action(...) decorator for > that. > > > HTML response headers > > Now for the most important part : settings the right headers on your > responses. > > Most of the performance-oriented websites nowadays will rely on CDNs to > act as proxies and cache resources. > This is especially useful for static assets, as these can be served > through your webserver and then cached in your CDN. > > If you use Amazon Cloudfront for instance, you can set your website ( > http://your-website.com) as origin of the distribution, which means > Cloudfront will look for the resource on your webserver once and then > basically cache it "forever", serving it to your client from whichever > Cloudfront endpoint is closest to him. > > I say "forever" because this behavior can and should be controlled with > specific headers. > If you're versioning your files (following the above-mentioned advice), > then you can set far-future cache headers without any risk of stale data. > Otherwise, you need to be very careful about how long you want to cache > your data. > > Now basically, your want to set 3 headers: > > > 1. Content-Encoding: how did you compress your data ? (e.g. : 'gzip', > 'deflate'). You need to check the Accept-Encoding header in your client's > request beforehand to make sure its browser supports the proposed > compression algorithm (all browers nowadays support on-the-fly > decompression so this is mainly for old browsers) > > 2. Content-Type: if you did use a compression algorithm, then you need > to specify this header so that your client's browser will interpret your > data correctly (and not based on it's content, which otherwise would be > interpreted as 'octet/stream'). Amazon S3 will suggest you default values > based on the file extension when setting this manually, otherwise web2py > has an amazing solution for that : > http://web2py.readthedocs.org/en/latest/contenttype.html > > 3. Cache-Control: this will determine how long you want your client's > browser to keep the data, avoiding unnecessary requests to your webserver. > Web2py will implicitly set it if you use the @cache.action(...) decorator. > I'd recommend this article > > <https://developers.google.com/web/fundamentals/performance/optimizing-content-efficiency/http-caching?hl=en#defining-optimal-cache-control-policy> > > to understand the basics of browser caching leverage. > > > > Possible improvements > > My main issue with web2py so far is serving static files with custom > headers. > Web2py's wsgi implements a very basic caching strategy: > if static_file: > if eget('QUERY_STRING', '').startswith('attachment'): > response.headers['Content-Disposition'] \ > = 'attachment' > if version: > response.headers['Cache-Control'] = > ... -- Resources: - http://web2py.com - http://web2py.com/book (Documentation) - http://github.com/web2py/web2py (Source code) - https://code.google.com/p/web2py/issues/list (Report Issues) --- You received this message because you are subscribed to the Google Groups "web2py-users" group. To unsubscribe from this group and stop receiving emails from it, send an email to [email protected]. For more options, visit https://groups.google.com/d/optout.

