Medium-Scale Dancer, Part 4: Front-End Matters
This will be a short article because the way you write your client-side JavaScript code and serve it to your users has almost nothing to do with Dancer. Nevertheless, there are a few things we can say here that tie into the prior articles in this series.
Consolidate JavaScript Files
Because each HTTP round-trip over the Internet takes human-scale time — tens to hundreds of milliseconds — an important part of making a fast web site is to consolidate your JavaScript code into as few files as possible.
Ideally, you will end up with just one JavaScript file, perhaps called
public/javascripts/app.js
.
This seems to go against the whole thrust of this article series so far, which has been about factoring monolithic structures into smaller, purpose-focused ones. But, there's a trick that lets you have your cake and eat it, too: tools like RequireJS allow you to "compile" multiple JavaScript files down to a single file. Coupled with minification, compression, and aggressive caching rules in your front-end web server, your app will feel a lot snappier to your users, even though the size of the web app on disk is no smaller than before.
You might not choose to compile all of your JavaScript down to a single
file. If it makes more sense for your purposes to use several files, I
suggest that you name those files after the top-level URL that uses it.
For example, the /mf
URLs we've been using in examples so far should
be served by a public/javascripts/mf.js
file. You might still wish to
develop mf.js
in several different purpose-driven JS files, compiled
down to mf.js
by RequireJS or a similar tool.
Serve Static Assets Via a Front-End Reverse Proxy Server
The
Dancer2 Deployment Guide
recommends running your app behind a front-end reverse proxy server
such as nginx or Apache's mod_proxy
. I strongly encourage you to do
this, even in development.
The server setup given in the deployment guide will cause the front-end proxy server to handle all requests for static content files itself, passing URLs down to Dancer only when it cannot find a static asset with the requested name. This takes a huge load off of Dancer, which, you must remember, is dynamic Perl code, so it will naturally be slower than the highly optimized C code in the proxy server.
To make sure the front-end proxy is doing what it ought to, you can
disable Dancer's built-in handling of static resources by adding this to
your config.yml
file:
static_handler: 0
Reload the web app, then work through all of the app's routes to make sure you don't get any new 404 errors. It may be helpful to open the Network tab in your browser's developer tools alongside the browser window you're testing your web app in so that you're more likely to notice any new errors that happen in background Ajax calls.
Running a typical web app this way might reduce the number of hits that get down to the Dancer level by a factor of 10 to 100 since so much of a modern web app is composed of static asset files: bitmaps, fonts, CSS files, JavaScript files, etc.
You might want to take this a step further, in fact. Instead of merely
disabling Dancer's static file handler in your config.yml
file for
the duration of a single test session, you could instead disable it
permanently in your environments/development.yml
file to ensure that
you find and fix such problems in development, before you deploy to
production.
I cannot recommend doing this permanently at the config.yml
or
environments/production.yml
levels, because I have occasionally found
it useful to be able to put random files into my app's public/
directory on production systems so that I can have HTTP access to them
via the Dancer app without restarting anything. Case in point: your
Dancer app is running on a Linux server at a remote site, and you need
to send a file too big to email to someone at that site, but that person
runs Windows on their PC, and the Linux box doesn't run Samba, so HTTP
is the only easy way they have to get the file from your server onto
their PC. (Can you tell that I'm writing from personal experience? Yes,
indeed, I am.) Annnnyway, the point is, you don't want to be forced to
adjust the front-end proxy settings in such a case to make it handle the
ad hoc file transfer. You want the Dancer app to backstop the
front-end proxy in such cases, serving up any file the proxy server
isn't preconfigured to serve.
I can offer a related bit of advice here: if you must change the
log_level
setting of a Dancer app running in a public-facing
production setting, never raise it to core
, at least not for very
long. Dancer logs all URLs retrieved at that level, and the default
Dancer production.yml
file is configured to write log messages to a
file file, rather than the console. On today's Internet, it is common to
encounter bots that do nothing but request nonsense URLs from every web
server they can find. These URLs may make sense to certain vulnerable
applications, or they may simply be random, as the bot is searching for
hidden resources on your server. You don't want a long series of such
hits to fill your disk with log messages.
Meta-advice: on production systems, configure your OS's log rotation
system to keep your Dancer app's log file trimmed to a reasonable size.
Dancer doesn't do that on its own. The Dancer2 docs recommend using
logrotate
in copytruncate
mode.
In the next part of this article series, we will discover the REST API that was hidden inside this monolithic web app the whole time.
Author
This article has been written by Warren Young for the Perl Dancer Advent Calendar 2016.
Copyright
© 2015, 2016 by Educational Technology Resources, licensed under Creative Commons Attribution-ShareAlike 4.0