... or how to switch from PHP + JavaScript to JavaScript + JavaScript
The idea to implement a project on server side JavaScript has been around for a long time. The problem was the lack of suitable server software. Existing open projects did not suit for various reasons. Installing an additional module for
Apache was not a good idea, because the performance and optimization of memory usage would not be at its best. Using
jslibs, you can configure FastCGI, but I really didn’t want to leave even the slightest chance of “502 Bad Gateway”, the
ngx_http_js_module project remained in the embryonic stage, and
ngxv8 is not developed enough to implement real applications. So I decided to make my own implementation of server javascript. And try to immediately program all the basic functionality so that you can test it in conditions close to reality.
As the main web server, it was decided to use
nginx , as a “javascript engine” - TraceMonkey (a javascript engine from
Mozilla Firefox , formerly SpiderMonkey), and write a module for nginx that would “stick together” them. Nothing complicated at first glance, but I really wanted to have a certain functionality (and it worked out!) So that you could work normally further. Most of the ideas are borrowed, by the way, from
PHP .
- Correct work in multi-thread conditions
- Ability to execute the script specified in the URL, rather than setting up a separate handler script and handler function for each location
- Ability to call include (), sleep (), alert () from a script, use __FILE__ and __LINE__
- Limit the memory allocated to each script, and the time of the script
- Protection of files opened by the script, specifying the list of allowed folders in the settings. Something like open_basedir in PHP
- Automatic parsing of request data (GET, POST, and, of course, cookies), in order not to write javascript data processing
- Support for application / x-www-form-urlencoded and multipart / form-data requests
- Basic authorization support
- Work with databases (first of all, MySQL and SQLite)
- Working with the file system: reading and writing files, checking for the existence of files, etc.
- Caching byte-code scripts, such as in eAccelerator
Plus some other features (tools for templating, for creating configuration files, etc.), but I did not include them in the main list - they can be made possible by the language features of TraceMonkey.
From words - to business! How to compile and configure how to test and compare ...
')
I do not go deep into the parts of the assembly, otherwise the text will turn out to be incredible sizes. Users who have experience of “building” programs under Linux will feel quite comfortable, and for everyone else I can offer a
binary build and the opportunity to skip the self-compilation process.
You will need:
- Linux
- Compilers C and C ++, autoconf 2.13
- Nginx sources
- TraceMonkey from repository
- NSPR library
- Our module
- MySQL and SQLite (optional) + development tools
The assembly procedure is as follows.
First NSPR latest version (at the time of writing - 4.8.2):
wget ftp://ftp.mozilla.org/pub/mozilla.org/nspr/releases/v4.8.2/src/nspr-4.8.2.tar.gz<br/>tar -xzf nspr-4.8.2.tar.gz<br/>cd nspr-4.8.2/mozilla/nsprpub<br/>./configure --prefix=/usr/local --with-pthreads<br/>make<br/>sudo make install<br/>
Then TraceMonkey from the repository (at the time of writing, version 1.8.5 in the repository, and you can download the source file only for 1.7.0):
hg clone http://hg.mozilla.org/tracemonkey/<br/>cd tracemonkey/js/src<br/>autoconf2.13<br/>./configure --prefix=/usr/local --with-nspr-prefix=/usr/local --with-system-nspr --with-pthreads --enable-threadsafe<br/>make<br/>sudo make install<br/>
This step can be problematic for several reasons. First, not everyone has the
hg
command. And secondly, all Mozilla Firefox sources are downloaded from the repository. Therefore, the first line of code can be replaced and download the source code only TraceMonkey:
# hg clone http://hg.mozilla.org/tracemonkey/<br/>wget http://js.nnov.ru/files/tracemonkey-20100119.tar.gz<br/>tar -xzf tracemonkey-20100119.tar.gz<br/>
And then compile.
Next nginx (0.8.32) and the javascript module:
wget http://sysoev.ru/nginx/nginx-0.8.32.tar.gz<br/>tar -xzf nginx-0.8.32.tar.gz<br/>cd nginx-0.8.32/src/http/modules<br/>svn co http://nginx-javascript.googlecode.com/svn/trunk/ javascript<br/>cd ../../..<br/>./configure --prefix=/usr/local/nginx-javascript --add-module=src/http/modules/javascript<br/>make<br/>sudo make install<br/>
If everything turned out - go to the setting. Happy owners of the binary assembly will find that the configuration has already been completed, but once again it will not hurt to check it. It is enough to perform the following steps:
- Add application / x-javascript-serverside type to mime.types for files that will be processed as javascript:
# /usr/local/nginx-javascript/conf/mime.types<br/>types {<br/> ...<br/> application/x-javascript-serverside jsx;<br/> ...<br/>}<br/>
The .jsx extension is selected instead of the standard .js, so that the server does not process the usual java-scripts as server ones - Allow javascript processing in the location / nginx.conf file. At the same time, we will change the port number on which the server will work:
# /usr/local/nginx-javascript/conf/nginx.conf<br/>...<br/> server {<br/> listen 8081;<br/> ...<br/> location / {<br/> ...<br/> javascript on;<br/> ...<br/> }<br/> }<br/>...<br/>
- Run nginx:
/usr/local/nginx-javascript/sbin/nginx<br/>
- Create a hello.jsx test script:
// /usr/local/nginx-javascript/html/hello.jsx<br/>print("Hello, people!");<br/>
- Check what hello.jsx looks like in the browser:
curl http://localhost:8081/hello.jsx<br/>
Having achieved that the server with javascript started working, I wondered how much more profitable this solution was than the standard Apache + PHP. Since the issues of internal optimization of TraceMonkey and PHP worried me somewhat less (for example, which interpreter performs the cycle of a million steps faster? I suspect that the difference is small), the “Hello, people!” Script was tested first.
Participated in the comparison:
- Apache / 2.2.14 (prefork) + PHP / 5.2.12 (module)
- nginx / 0.8.32 (1 workflow) + javascript
- nginx / 0.8.32 (8 workflows) + javascript
The testing environment is 4-core Xeon with 2GB of RAM and Debian Etch. All traffic is local. I don’t go into the details of the hardware, the configuration details are also more or less standard.
First, the test cycle of 1000 requests one by one:
# Apache 2.2.14 (prefork) + PHP 5.2.12 (module)<br/>ab -n 1000 http://localhost:8085/hello.php<br/>Time per request: 5.278 [ms] (mean, across all concurrent requests)<br/># nginx (1 worker) + javascript<br/>ab -n 1000 http://localhost:8081/hello.jsx<br/>Time per request: 1.298 [ms] (mean, across all concurrent requests)<br/># nginx (8 workers) + javascript<br/>ab -n 1000 http://localhost:8088/hello.jsx<br/>Time per request: 1.322 [ms] (mean, across all concurrent requests)<br/>
Now the test loop of 1000 requests when creating 100 simultaneous connections:
# Apache 2.2 (prefork) + PHP 5.2 (module)<br/>ab -n 1000 -c 100 http://localhost:8085/hello.php<br/>Time per request: 1.648 [ms] (mean, across all concurrent requests)<br/># nginx (1 worker) + javascript<br/>ab -n 1000 -c 100 http://localhost:8081/hello.jsx<br/>Time per request: 1.277 [ms] (mean, across all concurrent requests)<br/># nginx (8 workers) + javascript<br/>ab -n 1000 -c 100 http://localhost:8088/hello.jsx<br/>Time per request: 0.544 [ms] (mean, across all concurrent requests)<br/>
Conclusions from testing:
- If requests to the server go sequentially, one by one, nginx + javascript works much faster (about 3 times). At the same time, nginx with one workflow is even slightly faster. In reality, this situation almost never happens: more often, many customers open different pages at the same time.
- If server requests are sent at the same time, the speed of apache + php increases (we have shown almost the same speed as nginx + javascript with a single workflow). But the speed of nginx + javascript with multiple workflows also increases (we have - more than 2 times). And nginx + javascript with one workflow remained almost unchanged.
Besides the fact that such an implementation of server-side javascript makes it possible to achieve an increase in performance compared to traditional PHP, javascript allows using rather clever language constructs.
// id GET, POST cookies:<br/>print($request.get['id'], " ", $request.post['id'], " ", $request.cookie['id']);<br/>// Content-Type:<br/>$result.headers.push("Content-Type: text/html; charset=UTF-8");<br/>// , SELECT , GET, :<br/>var row = (new SQLite("database")).query("SELECT * FROM `table` WHERE `id`=?", $request.get['id']).fetch();<br/>// :<br/>print(File.open("index.html").getChars());<br/>// IP- , :<br/>print({$server.remoteAddr});<br/>
In the last example, there is no syntax error; XML documents can actually be used inside scripts. In this case, you can insert references to variables and calls to functions, enclosing them in braces. This technology,
E4X , is very convenient for creating templates. Some more examples can be found at
http://js.nnov.ru/nginx/examples.html .
Of course, there are a number of problems that need to be gradually solved:
- File upload support (coming soon!)
- Support cURL and GD, without which it is very difficult to live
- Optimization of the stat () system calls, which are now used to determine the actual path to the file
But, in general, you can use. By the way, a small site
http://js.nnov.ru is made on JavaScript. Please do not conduct hard tests for fault tolerance :-)
ZY> Special thanks to
FTM for an invite, thanks to which the topic is no longer in the sandbox
UPD> At once would publish in thematic, but there were problems with karma. Thanks to all involved!