The following is adapted from a presentation given by Igor Sysoev at nginx.conf 2015, held in San Francisco in September. You can view the video on YouTube.
Table of Contents
|The Lua Module
|The nginScript VM
|The State of nginScript
|The nginScript Interface
|Questions and Answers
0:27 nginScript Beginnings
Since the start of NGINX’s development, I’ve wanted NGINX to have the ability to run scripts inside the server.
I first attempted to implement this capability in 2005. At that time I tried to embed Perl – which was, and still is, my favorite language for general scripting.
Perl has some drawbacks, however, that make it unsuitable as a server scripting language. For example, when it runs out of memory, Perl just exits. Now that may be acceptable for an ordinary script but it’s very bad for servers. Servers need to behave much more gracefully in those situations.
1:30 The Lua Module
Some time after that, we decided to embed Lua, another scripting language, inside NGINX. This was made available through a third‑party module.
But Lua has some oddities. If you’ve never programmed with Lua, you will find the syntax a bit strange and different from what you may be used to with C (for example). You will see strange regular expressions as well, using unusual syntax.
4:04 V8 Issues
The second issue is memory. V8 runs in one browser tab, and if it runs out of memory, it will stop and maybe just close that tab. This is not acceptable for servers. Servers should behave more gracefully in these situations. It should just close that request and continue to process other requests.
The next thing is preemption. There is no easy way to use one V8 virtual machine, preempt it, and switch to running another V8 VM.
And the last thing is having to chase all the API changes made by Google. As you may know, Node.js never uses the latest version of V8. This is because Google changes the API quite often and developers simply cannot keep up.
6:15 The nginScript VM
All these virtual machines share the same bytecode which compiles at startup, so creating a new virtual machine is quite cheap. We just need to allocate some memory and copy some initial values to this memory. Teardown is even cheaper. We just free all allocated memory, which is a fast operation.
We use a register‑based virtual machine. This is not stack‑based, so the size of the virtual machine is quite small. For small tasks, it takes roughly several kilobytes of memory. So, it’s quite lightweight.
7:51 The State of nginScript
The current state of our implementation is a subset of ECMAScript 5.1. We do not support closures yet. We don’t yet support some built‑in objects, like date, month, etc., but we are considering adding these things later.
Currently, we have no garbage collector. As I’ve mentioned earlier, a garbage collector is not required for short‑lived requests due to our implementation, but we would also like to support long‑lived connections. In this case, a garbage collector may be required.
9:10 The nginScript Interface
We have the
$r object, which represent requests. This object has several fields:
remoteAddress. This list will expand in the future.
There is another object, inside the request object
$r, which allows us to work with the response –
$r.response gives us the fields
$r.response also has three methods that allow you to send a response to the client.
11:34 Questions and Answers
Q: What phases does this execute at?
A: Currently, nginScript can be used for declaring/defining variables, and it can be run in the content‑generation phase. There are other phases in NGINX, for example the access phase, the log‑writing phase. Eventually, we will add support for these phases. But currently, nginScript is limited to the content‑generation phase.
Q: Did you do any comparisons or benchmarking against LuaJIT?
A: When I started this project, I ran some simple benchmarks. I supposed that the most common task would be working with strings, so I wrote a simple benchmark evaluating Fibonacci numbers by concatenating strings.
Instead of adding numbers, which is the slowest algorithm to calculate the Fibonacci sequence, it recursively calls two numbers. I changed the numbers to strings, and at the end of the procedure I just count the length of the string. So, this microbenchmark tests the overhead of function calls and concatenating strings which I believe will be the most common tasks while scripting inside NGINX.
Of course, it’s not suitable for some mass operations – for encryption, for compressing, etc. – but it’s good for string manipulations, and actually this is the most important task in a server environment.
To learn more about nginScript, see our blog.