Diese Präsentation wurde erfolgreich gemeldet.
Wir verwenden Ihre LinkedIn Profilangaben und Informationen zu Ihren Aktivitäten, um Anzeigen zu personalisieren und Ihnen relevantere Inhalte anzuzeigen. Sie können Ihre Anzeigeneinstellungen jederzeit ändern.

Nginx + Lua

12.176 Aufrufe

Veröffentlicht am

Veröffentlicht in: Technologie

Nginx + Lua

  1. 1. NGINX + LUA.When you need it to be fast.
  2. 2. • Something different • Not to replace your PHP app • But a valuable add-on for specific use cases • Another tool for your development toolbox THE NEXT HOUR.
  3. 3. • Victor Welling • ~9 years as a Developer and Architect at Coolblue • Then I took an error to the knee, now a Developer Evangelist ME.
  4. 4. • One of the largest e-commerce companies in the Benelux • 1,1k employees, 332 webshops, 7 physical stores and 2 warehouses • All mission-critical software developed in-house • 100% Growth ever 20 months, so scalability matters COOLBLUE.
  5. 5. • Built to address the C10k challenge • Web server, reverse proxy, load balancer and HTTP cache • High concurrency and low, predictable memory consumption • Uses asynchronous, event-driven architecture NGINX.
  6. 6. • Lightweight scripting language, designed to be embedded • Fast execution and low memory consumption • Gentle learning curve • Been around since ’93, currently at version 5.2 LUA.
  7. 7. -- FizzBuzz in Lua for i = 1, 100 do local text = i if i % 3 == 0 and i % 5 == 0 then text = "FizzBuzz" elseif i % 3 == 0 then text = "Fizz" elseif i % 5 == 0 then text = "Buzz" end print(text) end fizzbuzz.lua - 1/1
  8. 8. • PHP-FPM provides pool of workers • Shared nothing architecture • Synchronous code, blocking I/O PHP-FPM.
  9. 9. • Ngx_lua module embeds Lua VM in nginx event loop • Allows for shared data between requests • Synchronous code, but non-blocking I/O NGINX + LUA.
  10. 10. PERFORMANCE.
  11. 11. • There’s lies, damned lies and benchmarks • This is not scientific • Your mileage may vary SIDENOTE.
  12. 12. • “Hello World” in Lua and PHP • VirtualBox with 2 CPU's • PHP-FPM (PHP 5.6.4) with static pool of 2 workers • OPcache enabled • Nginx 1.7.9 with two workers HELLO WORLD.
  13. 13. # ab -n 250 -c 3 -k http://127.0.0.1/hello.php [...] Document Path: /hello.php Document Length: 12 bytes Concurrency Level: 3 Time taken for tests: 0.225 seconds Complete requests: 250 Failed requests: 0 Write errors: 0 Keep-Alive requests: 250 Total transferred: 41750 bytes HTML transferred: 3000 bytes Requests per second: 1112.97 [#/sec] (mean) Time per request: 2.695 [ms] (mean) Time per request: 0.898 [ms] (mean, across all concurrent requests) Transfer rate: 181.51 [Kbytes/sec] received [...] shell - 1/1
  14. 14. # ab -n 250 -c 3 -k http://127.0.0.1/hello.lua [...] Document Path: /hello.lua Document Length: 12 bytes Concurrency Level: 3 Time taken for tests: 0.056 seconds Complete requests: 250 Failed requests: 0 Write errors: 0 Keep-Alive requests: 250 Total transferred: 41750 bytes HTML transferred: 3000 bytes Requests per second: 4491.56 [#/sec] (mean) Time per request: 0.668 [ms] (mean) Time per request: 0.223 [ms] (mean, across all concurrent requests) Transfer rate: 732.51 [Kbytes/sec] received [...] shell - 1/1
  15. 15. • Lua outperforms PHP 4 to 1 in req/sec • Not a real-world scenario • Shows impact of processing overhead RESULTS.
  16. 16. • Same set-up • Now with blocking I/O • Redis BLPOP with 1 second timeout BLOCKING I/O.
  17. 17. # ab -n 10 -c 10 -k http://127.0.0.1/blocking-io.php [...] Document Path: /blocking-io.php Document Length: 12 bytes Concurrency Level: 10 Time taken for tests: 9.968 seconds Complete requests: 10 Failed requests: 0 Write errors: 0 Keep-Alive requests: 10 Total transferred: 1670 bytes HTML transferred: 120 bytes Requests per second: 1.00 [#/sec] (mean) Time per request: 9967.910 [ms] (mean) Time per request: 996.791 [ms] (mean, across all concurrent requests) Transfer rate: 0.16 [Kbytes/sec] received [...] shell - 1/1
  18. 18. # ab -n 10 -c 10 -k http://127.0.0.1/blocking-io.lua [...] Document Path: /blocking-io.lua Document Length: 12 bytes Concurrency Level: 10 Time taken for tests: 1.142 seconds Complete requests: 10 Failed requests: 0 Write errors: 0 Keep-Alive requests: 10 Total transferred: 1670 bytes HTML transferred: 120 bytes Requests per second: 8.76 [#/sec] (mean) Time per request: 1142.199 [ms] (mean) Time per request: 114.220 [ms] (mean, across all concurrent requests) Transfer rate: 1.43 [Kbytes/sec] received [...] shell - 1/1
  19. 19. • Clearly shows concurrency restriction by worker pool size • Lua benefits from non-blocking I/O • Handles all requests (almost) simultaneously RESULTS.
  20. 20. # ab -n 10 -c 10 -k http://127.0.0.1/blocking-io.php [...] Document Path: /blocking-io.php Document Length: 12 bytes Concurrency Level: 10 Time taken for tests: 1.231 seconds Complete requests: 10 Failed requests: 0 Write errors: 0 Keep-Alive requests: 10 Total transferred: 1670 bytes HTML transferred: 120 bytes Requests per second: 8.13 [#/sec] (mean) Time per request: 1230.761 [ms] (mean) Time per request: 123.076 [ms] (mean, across all concurrent requests) Transfer rate: 1.33 [Kbytes/sec] received [...] shell - 1/1
  21. 21. • Larger worker pool removes concurrency restriction • Req/sec difference becomes negligible • Eventually you’ll still run out of workers RESULTS.
  22. 22. GETTING STARTED.
  23. 23. • Use OpenResty to get started right away • Or do it yourself • nginx • ngx_devel_kit • ngx_lua • LuaJIT • Lua CJSON BUILD IT.
  24. 24. • Rewrite • Access • Content • Log • …and a few others PROCESS PHASES.
  25. 25. PROCESS PHASES. Access Content Log Process Request Respond FastCGI Nginx PHP-FPM
  26. 26. • Nginx environment accessible through API • Control request flow • Modify request and response messages • Perform sub-requests • Create log entries NGINX API.
  27. 27. • Redis • Memcached • MySQL • WebSockets MODULES.
  28. 28. • Foreign Function Interface • Call C functions and use C data structures in pure Lua • With great power comes great responsibility LUAJIT FFI.
  29. 29. • Shared key-value store, e.g. Redis • Headers or environment variables • Serialise your session data through json_encode() instead of serialize() PHP + LUA.
  30. 30. PUT IT TO USE.
  31. 31. • Application meets infrastructure • I/O intensive tasks • Highly cacheable operations USE CASES.
  32. 32. • Front controller, request routing • URL rewriting • Exposing back-end API’s to AJAX calls • WebSockets APPLICATION.
  33. 33. • Intelligent load balancing • A/B testing, canary releases • Logging and metrics • Abuse protection • SSI/ESI alternative INFRASTRUCTURE.
  34. 34. CODE.
  35. 35. ABUSE PROTECTION.
  36. 36. http { lua_package_path '/usr/lib/lua/?.lua;;'; lua_shared_dict 'blacklist' 1m; server { access_by_lua_file 'blacklist.lua'; } } nginx.conf - 1/1
  37. 37. local blacklist = ngx.shared.blacklist local last_modified = blacklist:get("last_modified") if not last_modified or last_modified < (ngx.now() - 10) then local redis_client = require "resty.redis" local redis = redis_client:new() redis:set_timeout(100) local ok = redis:connect("127.0.0.1", 6379) if ok then local updated_blacklist = redis:smembers("blacklist") if updated_blacklist then blacklist:flush_all() for _, ip in ipairs(updated_blacklist) do blacklist:set(ip, true) end [...] blacklist.lua - 1/2
  38. 38. [...] blacklist:set("last_modified", ngx.now()) end end end if blacklist:get(ngx.var.remote_addr) then ngx.log(ngx.WARN, "Blacklist match, blocked access to " .. ngx.var.remote_addr) return ngx.exit(ngx.HTTP_FORBIDDEN) end blacklist.lua - 2/2
  39. 39. # curl -sI 127.0.0.1 | grep ^HTTP HTTP/1.1 200 OK # redis-cli sadd blacklist 127.0.0.1 (integer) 1 # curl -sI 127.0.0.1 | grep ^HTTP HTTP/1.1 403 Forbidden # redis-cli del blacklist (integer) 1 # curl -sI 127.0.0.1 | grep ^HTTP HTTP/1.1 200 OK # grep "Blacklist match" error.log 2015/01/01 00:00:00 [...] Blacklist match, blocked access to 127.0.0.1 [...] shell - 1/1
  40. 40. <?php if ($userIsMisbehaving) { $redis = new Redis(); $redis->connect("127.0.0.1", 6379); $redis->sadd("blacklist", $_SERVER["REMOTE_ADDR"]); } banhammer.php - 1/1
  41. 41. • Prevent abusive clients from reaching your PHP worker pool • Filter out malformed or harmful requests BENEFITS.
  42. 42. LOGGING & METRICS.
  43. 43. http { log_format 'json' $log_line; server { set $log_line ''; access_log 'access.json' 'json'; log_by_lua_file 'logger.lua'; } } nginx.conf - 1/1
  44. 44. local cjson = require "cjson" local json = cjson.new() local log_line = {} log_line.request = {} log_line.request.timestamp = ngx.var.time_iso8601 log_line.request.remoteAddress = ngx.var.remote_addr log_line.request.protocol = ngx.var.server_protocol log_line.request.method = ngx.req.get_method() log_line.request.url = {} log_line.request.url.host = ngx.var.host log_line.request.url.uri = ngx.var.request_uri log_line.request.url.query = ngx.req.get_uri_args() log_line.request.headers = ngx.req.get_headers() log_line.request.cookies = {} if log_line.request.headers.cookie then local cookies = log_line.request.headers.cookie [...] logger.lua - 1/2
  45. 45. [...] if type(cookies) ~= "table" then cookies = {cookies} end for _, cookie in ipairs(cookies) do for name, value in string.gmatch(cookie, "([%w_-]+)=([^;]+)") do log_line.request.cookies[name] = value end end end log_line.response = {} log_line.response.status = ngx.status log_line.response.bytesSent = tonumber(ngx.var.bytes_sent) log_line.response.processingTime = tonumber(ngx.var.request_time) log_line.response.headers = ngx.resp.get_headers() ngx.var.log_line = json.encode(log_line) logger.lua - 2/2
  46. 46. { "request": { "timestamp": "2015-01-01T00:00:00+01:00", "method": "GET", "cookies": { "cookie": "value" }, "url": { "host": "localhost", "uri": "/?parameter=value", "query": { "parameter": "value" } }, "protocol": "HTTP/1.1", "headers": { "host": "localhost", "accept-language": "en-us", "cache-control": "max-age=0", "connection": "keep-alive", [...] access.json - 1/2
  47. 47. [...] "accept": “text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8", "accept-encoding": "gzip, deflate", "dnt": “1", "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_1) AppleWebKit/600.2.5
 (KHTML, like Gecko) Version/8.0.2 Safari/600.2.5" }, "remoteAddress": "127.0.0.1" }, "response": { "status": 200, "bytesSent": 310, "processingTime": 0.002, "headers": { "content-length": "162", "content-type": "text/html", "connection": "close" } } } access.json - 2/2
  48. 48. • Capture all request and response headers • Users of ELK will love this • Optionally send it straight to Redis • Expand your log with custom metrics BENEFITS.
  49. 49. WRAPPING UP.
  50. 50. • High performance, highly scalable • Fits well in a DevOps culture • Already using nginx? Might as well try it! SUMMARY.
  51. 51. • Online book “Programming in Lua” at lua.org is a good place to start • Also, plenty of examples and tutorials to be found at
 lua-users.org LUA 101.
  52. 52. • Nginx • OpenResty • HttpLuaModule • LuaJIT • Lua CJSON GOOGLE IT.
  53. 53. THANK YOU. Any questions?
  54. 54. • victor@coolblue.nl • @victorwelling • linkedin.com/in/victorwelling • slideshare.net/victorwelling CALL ME MAYBE.

×