staticshin

Top ten things about openresty

[generate barebones openresty applications from the command line]

  1. It's nginx we know and love

    Say you are the maintainer of one of those 23 millions websites [1] that use nginx as their webserver and you see this shiny new thing called openresty and you say to yourself

    "Bah! HUMBUG! I can't afford to break my pretty nginx configurations!! Humbug! BAH!"

    I understand the feeling pretty well,ah yes. It's a dilemma faced by many nginx enthusiasts who hear about openresty for the first time. "Should we migrate or should we stay with nginx?" But they miss the point. Openresty won't force you to rewrite and retest your existing configurations. Openresty is nginx. Or maybe I should say it's nginx +

    Bundled together with some well tested third party modules and integrated with luajit it gives the developers the ability to program nginx. Yes I said program. Now you are not just limited to configuring the webserver (although you can still use it that way) but you can actually write code. And we are not talking about writing code in some some weird pseudo language that nginx gives you by default. We are talking about a real programming language. We are talking about lua.

    So let me type this in bold -- openresty is nginx-- and this in small -- with extra goodies packaged together in a nice little bundle. Your existing configuration will continue to work with openresty. You might need to do some additional work if you've installed third party modules. Even then there is a pretty good possibility that your favorite module comes bundled in with openresty. In the worst case scenario you might need to install those bundles again but the default stuff works out of the box. And that makes for a damn smooth migration.

  2. Fast

  3. I know nginx is fast. You know nginx is fast. Everybody knows nginx is fast. The question is "Is it still fast with lua?"

    Yes it is, it seems . Openresty uses luajit to compile lua code. The code compiled with lua jit is damn fast reaching almost c levels of performance.

  4. Low memory footprint

  5. I know nginx's memory footprint is small. You know that nginx's memory footprint is small. Everybody knows that nginx's memory footprint is small. The question is Is it still small with lua?

    Yes it is. It seems. Here's an old benchmark done by the author of openresty.

    Below is the result using the command http_load -p 10 -s 5 http://localhost:8080/ on my ThinkPad T400 laptop with ngx_openresty 1.0.10.1:

    139620 fetches, 10 max parallel, 1.67544e+06 bytes, in 5.00001 seconds 12 mean bytes/connection 27923.9 fetches/sec, 335087 bytes/sec msecs/connect: 0.0531258 mean, 4.076 max, 0.014 min msecs/first-response: 0.258796 mean, 5.353 max, 0.067 min HTTP response codes: code 200 -- 139620

    So on my laptop, for only a single worker nginx server, we've got ~28k r/s. The memory footprint of the node process under load is 38.0m VIRT, 2.5m RES.

    The same test when repeated for node js gives the following result

    And node.js v0.6.1 yields 10k r/s:

    51206 fetches, 10 max parallel, 614472 bytes, in 5 seconds 12 mean bytes/connection 10241.2 fetches/sec, 122894 bytes/sec msecs/connect: 0.0356567 mean, 1.316 max, 0.019 min msecs/first-response: 0.916395 mean, 14.236 max, 0.077 min HTTP response codes: code 200 -- 51206

    The memory footprint of the node process under load is 629m VIRT, 50m RES.

    The low memory footprint of openresty is not surprising when you consider that the other thing that lua jit is famous for,besides it's speed, is it's ability to run in an embedded mode. It seems that the VM is just 115 KB in size. Compared to that when all is said and done this page is about 1 mb in size. Which makes this simple html page heavier than the lua jit vm.

  6. Lua

  7. I know that lua... you know that lua... everybody knows that lua... wait this won't work here. Let's try something else.

    Openresty uses lua as the programming language. Now lua lets you do some pretty cool things like allowing you to return multiple values from a function (which is used brilliantly, by the way, in the ngx.location.capture api), declare multiple variables on a single line, using co routines to pause and start the execution of functions etc. And you can do all of these cool things using a very familiar syntax,with decent support of third party libraries.

    But there are a couple of things that can be off putting to a person just starting out. Like for example array indices that start with 1. No support for ternary operators. For some people it can be a bit too verbose with all those end statements.

    But these "problems", if you want to call it that, is just a matter of getting used to the language. The only serious difficulty that I had while I was learning lua was getting my head around array indices that start with 1. Once you get used to lua,which should be pretty quick anyways, you wouldn't mind these petty things.

    Lua makes sense as a language of choice for openresty because it's highly embeddable and it has luajit. Which as we all know is damn fast and damn conservative when it comes to resource usage. Hot Damn!

  8. *by_lua directives

  9. You get to use all the sweet goodness of lua in the *by_lua directives of openresty. These directives do different things depending upon the context they are run in. The lua_code_cache directive for instance turns the caching of lua code on/off. That might increase the performance of our program but it does not let us sample the sweet goodness of lua in nginx.

    For that take a look at access_by_lua directive which allows you to do some lua processing during the access phase of an nginx request. Or rewrite_by_lua directive that allows you to inject lua into the nginx rewrite request phase.

    Cool right? At the very least these directives should give you a good excuse to learn more about the http request processing phases of nginx so you can inject lua code and hack it.

                
    location /hello_world{
     content_by_lua '
     ngx.say("Hello world")
     '
    }
    	    
              

    "Code inside strings is ugly yuck! yuck! yuck!"

    Calm down old fellow. I believe that saying yuck! one time is quite enough to convey the feeling of repugnance that a programmer feels when he has to write code embedded in strings.

    I will concede that emotions run high when the debugger fails to tell on exactly what line the error has occurred. And in this situation it is quite okay to say yuck! yuck! to emphasize that the disgust is quite strong.

    But in my opinion saying yuck! yuck! yuck! three times in quick succession helps no one. I mean to say that the feeling of strong disgust has already been conveyed by saying "yuck!" two times and a third utterance of the word yuck! only weakens the effect. In fact I believe that it would be far more powerful to say "yuck" and just leave it hanging there. Don't even use an exclamation mark to close it off. Imagine Alan Rickman saying Yuck and try to capture that effect when you speak.

    With that out of the way... you don't need to use strings to write lua code. You can have your all your lua code in a file and reference the file using the *by_lua_file directives. All *by_lua directives have an equivalent *by_lua_file directive as well so you are not really bound to single choice. Or if you insist upon in lining your lua code you'd find the *by_lua_block directives ,that allow you to write lua code inside "{}", quite useful. That clears up the clouds I hope :)

  10. ngx.location.capture.multi --the ultimate api mashup tool

  11. ngx.location.capture.multi is easily my favorite thing about openresty. In simple terms it allows you to make synchronous yet non blocking sub requests to nginx's location blocks.

    "Okay so what's so cool about it?"

    Well here's what's cool. You know how location blocks can use proxy_pass directives to pass requests to different servers, right? With location capture api you can get a response from the proxied server in your code. The implication of this is that you can have multiple services deployed in different locations and make requests to them from lua.

    Think microservices. Let's say your application has different services for authentication, billing and notification operations.You can have these services proxied with nginx locations and access them cleanly in lua with location.capture.

    The good thing about proxying through locations is that you can use the same method to proxy both external and internal systems. For example say you are building a system that requires calling external apis of mailchimp, paypal and amazon. To make these api's available all you need to do is configure their respective location blocks in exactly the same way as you did for your internal microservices. And that's all. You can now start making requests to the apis from your lua code.

  12. Simplified application architecture

  13. The lua directives offer a clean separation of the request coming in to nginx. To be precise nginx has a well defined cycle which a request must go through before a final response is sent. What openresty offers is the ability to inject lua code into these request phases.

    So for example an access_by_lua handler can allow you to inject lua while nginx processes a request in an access phase. Effectively what this does is it allows you to write arbitrary code that can reject a request then and there before it moves on to other handlers. This is very useful if you want to reject unauthorized access to a location block or configure a blacklist of ip addresses or impose other restrictions on the service of a request.

                
    location / {
    access_by_lua_block {	      
    local headers = ngx.req.get_headers()
    local uname_pass = 'abc:def'
    if  headers["Authorization"] ~= "Basic "..ngx.encode_base64(uname_pass) then
       ngx.header["WWW-Authenticate"]="Basic"
       ngx.req.clear_header("Authorization")
       return  ngx.exit(ngx.HTTP_UNAUTHORIZED)
    end
    ngx.exit(ngx.OK)
     }
    }
    	    
              

    "But exactly how does it simplify the architecture"

    First the practice of code reusablity is highly encouraged. Once you have written an access rule you can reuse the same code by referencing it from different access_by_lua file directive.

    Second it allows you to think of your code in terms of request processing phase handlers cleanly breaking down each action and tying it down to a particular handler. So your authorization/authentication code, as seen in the example above, will be handled and run by the access_directive.

    "Is access_by_lua the only directive that is available"

    No no of course not. I have a bit of soft spot for access_by_lua handler so I've talked about it quite a few times. But there's a long list of directives available that can be used to inject lua code during different phases of execution in the openresty readme .

  14. A good mix of configurability and programability

  15. A definitive advantage that you get with nginx is the number of things that you can do with the modules that are already available.

    Being a proxy server nginx allows you to, with one or two lines of configuration, set block lists for ip addresses,route requests to different handlers or maybe configure the security headers for your application.

    Openresty lets you do more or perform things that were not possible or difficult to do with just nginx configuration directives. So if you find that a module is not measuring up to your expectations you can always roll up your sleeves and write some lua code to do it yourself.

  16. Unified proxy and Application servers

  17. Where as before you had to maintain different proxy and application servers openresty gives you an opportunity to unify them.

    If you already have an existing application server openresty gives you the benefit of offloading some of the minor but resource consuming tasks to it and take some pressure off of your main machine. Offloading user authentication to openresty for example is one of the most impactful things that you can do to take the load off of your main servers. In such a scenario all requests can be checked for authentication before passing them to your main servers and in case it's unauthorized it can be rejected at the proxy level itself.

    Or maybe you could have openresty validate your data before you pass them to your servers so you can be sure that whatever data that comes through the proxy is clean and ready to be used as it is.

    In general by using openresty ,instead of plain old nginx, you get to have more options.

  18. Concurrency --saved the best for the last

  19. Openresty is damn concurrent. Each nginx worker process comes with it's own instance of lua jit to compile the lua code. The worker processes are responsible for listening to the requests coming in and then passing them along to the phase handlers. These phase handlers themselves are run in independent light weight threads. So there can be multiple execution endpoints per lua jit instance This combination of multiple lua jit vms and each vm running multiple light weight threads makes it damn hard to write blocking code in openresty. Though it is still possible if you put some serious effort into it.

[1] "You pulled that number out of your ass?"

I certainly did not! I feel offended that the idea even crossed your mind. Like any responsible writer when I put a number like this on an article you can be certain of the fact that I did a google search and accepted the count that came out of the first result without doing any kind of cross checking or verifying the validity of the statistic. You may accuse me of laziness but I'm certainly not "pulling numbers out of ass" type.

-- Akshat Jiwan Sharma,staticshin


comments powered by Disqus