Nginx: Accelerate Rails, HTTP Tricks

Post on 15-Jan-2015

15.085 views 1 download

Tags:

description

Adam Wiggins' Railsconf 2008 session talk.

Transcript of Nginx: Accelerate Rails, HTTP Tricks

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_tngx_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_tngx_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