Nginx: Accelerate Rails, HTTP Tricks - O'Reilly Mediaassets.en.oreilly.com/1/event/6/Custom Nginx...
Transcript of Nginx: Accelerate Rails, HTTP Tricks - O'Reilly Mediaassets.en.oreilly.com/1/event/6/Custom Nginx...
Nginx:Accelerate Rails,HTTP Tricks
Adam WigginsRailsconf 2008
Nginx is a webserver
Nginx replaces ApacheWhich means:
!Faster!Smaller memory footprint!More stable under load!More secure
Nginx replaces Apache
But more importantly:
But more importantly:
Nginx is a better fitfor Rails
Apache is the right tool:
!mod_php!owning your own server hardware
Apache is the right tool:
!mod_php!owning your own server hardware
The era now coming to a close.
!Rails!cloud computing
The era now upon us:
Which both work best with proxying.
!Rails!cloud computing
The era now upon us:
Proxying is Nginx’sprimary mechanism forserving dynamic content
“One thing”
Embrace the constraintof keeping applicationVMs out of the front-endwebserver
Ok cool.
Wait, there’s something funnygoing on here...
Ok cool.
Nginx is not a webserver?
Nginx is an HTTP router
Enough abstraction;let’s see some code
Proxy balancing example
upstream myapp_mongrels {
127.0.0.1:3000;
127.0.0.1:3001;
}
location / {
proxy_pass http://myapp_mongrels;
}
Memcached in front
location / {
set $memcached_key $uri;
memcached_pass 127.0.0.1:11211;
error_page 404 502 = @myapp;
}
location @myapp {
internal;
proxy_pass http://myapp_mongrels;
}
Memcached method filter
location / {
if ($request_method = GET) {
set $memcached_key $uri;
memcached_pass 127.0.0.1:11211;
error_page 404 502 = @myapp;
break;
}
proxy_pass http://myapp_mongrels;
}
Filtering
Filtering is reaching inand tinkering withrequests and responses
Separate concerns:
Separate concerns:
!Filters in your app for business logic
Separate concerns:
!Filters in your app for business logic
!Filters in your http router for server infrastructure
Separate concerns:
!Filters in your app for business logic
!Filters in your http router for server infrastructure
!Filters in either for application infrastructure
application infrastructure
Separate concerns:
!Filters in your app for business logic
!Filters in your http router for server infrastructure
!Filters in either for
Custom Nginx modulesTime to get hardcore:
http://emiller.info/nginx-modules-guide.html
Granular user access control
The problem:
before_filterThe Rails solution:
The Rails solution: before_filter
before_filter :authorize
def authorize
@user = User.find(session[:user_id])
@resource = request.env['REQUEST_URI']
redirect_to '/login' unless @user
redirect_to '/access_denied' unless
@user.can_access(@resource)
end
Can we do this withouttouching the Rails app’scode?
input filter moduleThe Nginx solution:
input filter moduleThe Nginx solution:
ngx_heroku_gate
before_filter :authorize
static ngx_int_t
ngx_heroku_gate_init(ngx_conf_t *cf)
{
phase =
cmcf->phases[NGX_HTTP_ACCESS_PHASE];
h = ngx_array_push(&phase.handlers);
*h = ngx_heroku_gate_handler;
}
before_filter :authorize
def authorize
@user = User.find(session[:user_id])
@resource = request.env['REQUEST_URI']
static ngx_int_t ngx_heroku_gate_handler
(ngx_http_request_t *req)
{
user = get_logged_in_user(
req->headers_in.cookies);
app_name = get_app_name(
req->headers_in.host->value.data);
def authorize
@user = User.find(session[:user_id])
@resource = request.env['REQUEST_URI']
redirect_to '/login' unless @user
if (!user)
{
redirect_to(req, '/login');
return NGX_HTTP_MOVED_TEMPORARILY;
}
redirect_to '/login' unless @user
redirect_to '/access_denied' unless
@user.can_access(@resource)
if (!user_can_access(user, app))
{
redirect_to(req, '/access_denied');
return NGX_HTTP_MOVED_TEMPORARILY;
}
else
{
write_heroku_user_header(req, user);
return NGX_OK;
}
redirect_to '/access_denied' unless
@user.can_access(@resource)
#define X_HEROKU_USER "X-Heroku-User"
static void write_heroku_user_header
(ngx_http_request_t *r, u_char *user)
{
h = ngx_list_push(
&r->headers_in.headers);
h->hash = 1;
h->key.len = sizeof(X_HEROKU_USER) - 1;
h->key.data = (u_char *) X_HEROKU_USER;
h->value.len = strlen((char *)user);
h->value.data = user;
}
The Rails solution: before_filter
before_filter :authorize
def authorize
@user = User.find(session[:user_id])
@resource = request.env['REQUEST_URI']
redirect_to '/login' unless @user
redirect_to '/access_denied' unless
@user.can_access(@resource)
end
static ngx_int_t
ngx_heroku_gate_handler(ngx_http_request_t *req)
{
user = get_logged_in_user(req->headers_in.cookies);
app_name = get_app_name(req->headers_in.host->value.data);
if (!user) {
redirect_to(req, '/login');
return NGX_HTTP_MOVED_TEMPORARILY;
}
if (!user_can_access(user, app)) {
redirect_to(req, '/access_denied');
return NGX_HTTP_MOVED_TEMPORARILY;
}
else {
write_heroku_user_header(req, user);
return NGX_OK;
}
}
The Nginx solution: input filter
A word on performance
Closing thoughts /the future
HTTP is the enablingprotocol for the eraof cloud computing
http://igvita.com/2008/02/11/nginx-and-memcached
http://nginx.ru
Adam Wigginshttp://adam.blog.heroku.com
http://emiller.info/nginx-modules-guide.html