Nginx: Accelerate Rails, HTTP Tricks

18,995 views

Published on

Adam Wiggins' Railsconf 2008 session talk.

Published in: Technology
0 Comments
37 Likes
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total views
18,995
On SlideShare
0
From Embeds
0
Number of Embeds
80
Actions
Shares
0
Downloads
336
Comments
0
Likes
37
Embeds 0
No embeds

No notes for slide

Nginx: Accelerate Rails, HTTP Tricks

  1. Nginx: Accelerate Rails, HTTP Tricks Adam Wiggins Railsconf 2008
  2. Nginx is a webserver
  3. Which means: Nginx replaces Apache
  4. Nginx replaces Apache ‣Faster ‣Smaller memory footprint ‣More stable under load ‣More secure
  5. But more importantly:
  6. But more importantly: Nginx is a better fit for Rails
  7. Apache is the right tool: ‣mod_php ‣owning your own server hardware
  8. Apache is the right tool: ‣mod_php ‣owning your own server hardware The era now coming to a close.
  9. The era now upon us: ‣Rails ‣cloud computing
  10. The era now upon us: ‣Rails ‣cloud computing Which both work best with proxying.
  11. Proxying is Nginx’s primary mechanism for serving dynamic content
  12. “One thing”
  13. Embrace the constraint of keeping application VMs out of the front-end webserver
  14. Ok cool.
  15. Ok cool. Wait, there’s something funny going on here...
  16. Nginx is not a webserver?
  17. Nginx is an HTTP router
  18. Enough abstraction; let’s see some code
  19. Proxy balancing example upstream myapp_mongrels { 127.0.0.1:3000; 127.0.0.1:3001; } location / { proxy_pass http://myapp_mongrels; }
  20. 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; }
  21. 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; }
  22. Filtering
  23. Filtering is reaching in and tinkering with requests and responses
  24. Separate concerns:
  25. Separate concerns: ‣Filters in your app for business logic
  26. Separate concerns: ‣Filters in your app for business logic ‣Filters in your http router for server infrastructure
  27. Separate concerns: ‣Filters in your app for business logic ‣Filters in your http router for server infrastructure ‣Filters in either for application infrastructure
  28. Separate concerns: ‣Filters in your app for business logic ‣Filters in your http router for server infrastructure ‣Filters in either for application infrastructure
  29. Time to get hardcore: Custom Nginx modules
  30. http://emiller.info/nginx-modules-guide.html
  31. The problem: Granular user access control
  32. The Rails solution: before_filter
  33. 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
  34. Can we do this without touching the Rails app’s code?
  35. The Nginx solution: input filter module
  36. The Nginx solution: input filter module ngx_heroku_gate
  37. before_filter :authorize
  38. 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; }
  39. def authorize @user = User.find(session[:user_id]) @resource = request.env['REQUEST_URI']
  40. 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);
  41. redirect_to '/login' unless @user
  42. redirect_to '/login' unless @user if (!user) { redirect_to(req, '/login'); return NGX_HTTP_MOVED_TEMPORARILY; }
  43. redirect_to '/access_denied' unless @user.can_access(@resource)
  44. 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; }
  45. #define X_HEROKU_USER quot;X-Heroku-Userquot; 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; }
  46. 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
  47. The Nginx solution: input filter 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; } }
  48. A word on performance
  49. Closing thoughts / the future
  50. HTTP is the enabling protocol for the era of cloud computing
  51. http://nginx.ru http://emiller.info/nginx-modules-guide.html http://igvita.com/2008/02/11/nginx-and-memcached Adam Wiggins http://adam.blog.heroku.com

×