nxweb - HTTP server for C applications

nxweb is the new embedded high-performance web server for C applications. In terms of functionality, it is a framework for writing HTTP request handlers. Analogs: G-WAN / libevent / Mongoose, Apache / mod_ <your favorite language>, Tomcat, Node.js. Developer - Yaroslav Stavnichy. I was primarily interested in the project because it represents a real alternative to existing solutions, each of which has its own drawbacks. Choice is good. You may also like the combination of features, pros and cons of this server.

Under the cut, detailed information about the project from an interview with the developer.

Q: Tell me about yourself, what do you do, where do you work?

Yaroslav: I have been developing software for a long time. Nexoft Company. I write mainly in Java, but I also occasionally recall C / C ++ when I need to do something fast. For example, a banner twist engine for a site with high traffic.

Q: So nxweb was created with the goal of twisting banners?

Yaroslav: Well, not only for the sake of banners, but this is one of the first tasks. The specific task, at least, is not abstract.

Q: High traffic is how many requests per second?

Yaroslav: Now about 100 requests per second are coming. Some of them are sent to Tomcat. But up to 20 banner codes are inserted into each HTML page. The server, in principle, is difficult, so I want to not burden him with the simple task of returning short HTML fragments. Maybe, of course, it would work on the pearl as well, but we are not looking for easy ways.

Actually, I had such an engine for a long time, it was written as a module for Apache, but recently I have been transferring all projects to nginx.

In general, writing modules for Apache and nginx is not a pleasant experience. There's a bunch of tinsel imposed by concern for portability. In addition, they are multi-process, and this is a separate problem. With threads, things are much simpler. Shared memory is priceless. I'm used to Java, there it is. I climbed inside nginx, everything is very cumbersome there, plus, again, shared memory, etc.

I decided to try to implement my application on a micro engine like Mongoose, I came across it on the network earlier. He began to dig, found several more alternatives, including G-WAN. I liked everything about it very much, except for closed-source. I tried G-WAN exactly as an application server, and not as a static shipper. But pretty quickly it began to come across glitches, which were not possible to find and fix because of the closed code.

So, I wrote a twist on Mongoose, began to test performance and found that it was already very low compared to G-WAN. It was only later that I guessed to launch 2500 threads, but at first I thought that 8-16 would be enough. With so many there is nothing at all.

Q: Because mongoose uses one OS thread per request?

Yaroslav: Yes, actually, 2500 threads are not a solution either. They consume a ton of memory, and, again

, the 2501th request appears - and hello ... I began to dig deeper, found microhttpd and libevent. In terms of performance, when compared with the G-WAN, they looked pale. While I was dealing with all this, delving into the source, I realized that writing a web server is not so difficult. In mongoose, for example, you must manually write the entire HTTP response to the module developer.

libevent looked the most promising, but single-threaded. I decided to redo the same for libev (a lightweight alternative to libevent). So nxweb was born. The main initial goal is C web applications, not a full-featured server. If you are already developing something in C, then you want it to work extremely fast, which means that the platform must also be fast. And then you’ll write under mongoose, and, one wonders, for what he tried, if he would release everything on the brakes.

I had no purpose to compete with anyone. I wanted to get closer to the G-WAN, as to the standard of speed. But as soon as the project was published, a stream of letters went right there. They all began to gaze at me. So I had to spend a few more days, puff, to still overtake the G-WAN.

I overtook him or not, is not yet clear. On my computer, I overtook him. As they say, in its territory. Again, probably not in all modes.

By the way, nginx was very pleased. The speed of its HTTP stack is higher than that of G-WAN (if configured correctly, of course). Just nginx does not cache files in memory if it is not requested. There is no point in competing with nginx. It’s even surprising how such a functional server provides such high performance. Here I was convinced that the elementary use of sprintf instead of strcat is able to put performance at 5-10 thousand requests per second. Those. the battle is already over for every extra CPU instruction.

Q: Well, tell us about nxweb architecture in a nutshell.

Yaroslav: The main thread listens on the socket and pushes incoming connections into network flows (each has its own turn to avoid mutexes). Network streams (it makes sense to run them one at a time on the processor core) provide the HTTP protocol. Decode the request, pass it to the handler module. Workers are optional; they are designed solely to support slow handlers so that they do not stop the network flow.

Now the modules have a callback, which is called by the server before entering the main loop. There is also a main.c file that can be removed or replaced. Its function is exclusively service: open the log file and transfer control to _nxweb_main (). He also knows how to launch a demon. And double: one demon ensures that the second (where nxweb actually spins) does not stop. If the internal daemon crashes, the first one restarts it automatically. If desired, main.c can be replaced with any other. Most importantly, it should call _nxweb_main ().

Q: Why does each network stream not accept loop? Then the OS will be able to distribute the connections and you can make several accepts in parallel on different kernels.

Yaroslav: I tried both options, incl. accept the network stream without the participation of the main. I didn’t find a difference in speed, I returned to a single acceptor, as it seemed to me that this reduces the percentage of connection errors. For nginx and G-WAN, part of the connections freezes and does not carry loads. See my comment on real concurrency on the benchmark page.

Q: I see. The first version used libev, but now the wiki says that there is no dependency on libev. What changed?

Yaroslav: I wrote my libev. This is exactly what I meant by "puffing to overtake the G-WAN." It is imprisoned for my needs and is not an independent project. In other words, I now work with epoll directly. nxweb is completely not portable. It works only on Linux and even on the kernel> = 2.6.22. I am using Edge-Triggered epoll, which libev does not support due to its intolerance. Yes, libev is a super-cool thing, I would stop on it, but people wanted it faster. So I had to suffer.

Q: Was Edge-Triggered the last gain?

Yaroslav: Not only. I also had to work on the code. In particular, to exclude sprintf, a new memory backup system, etc. There is an idea to fasten all this back to libev. Maybe it will not be worse, but I don’t know if I’ll get together. I don’t have to make portable code. Hosting is almost always on Linux. And libev is an extra dependency that needs to be installed in order to build nxweb. minus too.

Q: Igor Sysoev and the libev authors claim that epoll has a ton of problems. Have you encountered them yet?

Yaroslav: It is hoped that these problems in the modern core have already been cleaned up. Still, many years have passed. I am testing on 2.6.32 and 3.0.0, I have not encountered any problems.

Q: What about memory backup? Are you allocating some kind of large pool to make less siskol?

Yaroslav: Free-list for connections and its own obstack counterpart for generating responses.

Q: Do you use your own implementation of the HTTP parser or did you take something ready?

Yaroslav: Own implementation. Of course, I do not support absolutely all the nuances of the HTTP protocol. Although, for example, I have 100-continue or chunked-encoding. Again, shipping statics is the business of the module. The kernel parses the request, calls the handler, receives the response, wraps it in HTTP, and sends it to the client. Those. ETag and Range are all there - this is the task of the module. Right now my sendfile.c module generates only the main headers, although I will probably do support for Range and If-Modified-Since soon.

Yes, there are probably ready-made parsers checked for errors. But, on the other hand, in my own parser I’ll catch the error faster if they tell me about it than in someone else’s. Here really the expense goes to the CPU instructions. And all independent projects of parsers are so comprehensive that there can be no question of any performance with them.

Q: On the other hand, I would not let anyone except nginx listen to the external port of the combat server. And after it all incorrect requests are already filtered.

Yaroslav: At the moment (version 2.0) nxweb is not very ready to expose it. Mostly because real applications are not just made up of C scripts. You need to tie up both Java and statics and everything else. Unless it comes to the implementation of any chats, where it is important to keep many thousands of simultaneous connections.

Q: There are a number of libraries that make userland threads in C, one of them is libtask, used in mongrel2. Each coroutine has its own stack, so the library contains several assembler instructions for switching the stack. This allows you to write regular sequential code, without callbacks like on_request, on_success ...

Yaroslav: Yes, I heard about coroutines. But if I already wrote everything in callbacks, then I won’t get any performance gain by switching to coroutines. Only the extra memory will go into storage of the stacks. Yes, and problems with gdb - also not the most pleasant. It is a matter of taste and habit. You need to get used to coroutines. This is a special concept.

Q: A significant gain can be in the support of the code of the handlers, when people want not just request-response, but, for example, sleep in the middle or go to the database. For a simple banner twist, of course, there is no sense in them.

Yaroslav: For slip-ins and going to the base - that's what I provided for the workers. And checked the first thing. If 100 workers are configured and each worker makes sleep 1 sec, then the server processes 100 requests per second smoothly and without hesitation. Neither G-WAN nor nginx can handle this. They either issue 3-4 requests per second, or hang stupidly.

Although, in case of urgent need to go to the base, you can write a non-blocking adapter for this purpose. There will be one call from the servlet - to queue the database request and register the callback. After this return, the callback continues to work. In principle, here for the convenience of writing you can think about coroutines.

Q: How would you describe the current status of nxweb? Stability, mistakes?

Yaroslav: Well, I already screwed it as a backend to a site with quite a lot of traffic, i.e. launched in production. A month has passed, and not a single restart / failure. Although the backend is not the toughest baptism of fire.

I believe that the status is alpha. In particular, not all nxweb features have been thoroughly tested. For example, chunked request encoding. It works on test cases, but you never know what pops up. It is also unclear what stability will be with incorrect client behavior. For example, requests with errors can cause a failure.

There is still a problem at the current stage - I am actively changing h-files, data structures, etc.

Q: Personally, as a potential developer for nxweb, I need the ability to completely control the processing of all requests and a thick library of auxiliary functions (this is appropriate in the form of modules), such as compare url, sendfile, put the header, parses cookies, zaproksiruyte on the backend. This is how I imagine programming critical sections on nxweb.

Yaroslav: I must say that this is approximately my approach. Create basic functions with which you can configure the server. But configure in C, and not with the help of some kind of config file. Because creating an analogue of the config that nginx has is a very difficult task.

Q: The following question, more likely for a tick, what do you think about Windows support?

Yaroslav: 99% excluded. I generally look at code portability coldly. In fact, it is precisely because of concerns about portability that the nginx internal interface (and apache) is so confused. We have to abandon modern technology for the sake of code portability. For now, I want to concentrate only on Linux.

Q: G-WAN has a rather convenient feature - autocompilation of applications. Of course, there are a lot of open questions, such as security, compiler flags, updates on the fly. Do you plan to add the same feature to nxweb?

Yaroslav: Yes, I really liked her. But I do not plan yet. Excessive hemorrhoids, which in no way brings me closer to starting to fully use nxweb for my needs.

Q: Well, the more pressing question is, in what form do you plan to distribute nxweb? Source tree? Library? Static / dynamic?

Yaroslav: So far, only in the one I’m already distributing. An open repository where you can download the source and run make. Currently, a full compilation of the project takes seconds. I don’t see the point of damaging the library yet.

Q: Let's say I want to write my hello world application. My actions?

Yaroslav: hello.c is a module template. Further, in the Makefile there are small comments about connecting your modules. There are special variables SRC_MODULES and INC_MODULES. In addition, you need to add a link to your module in modules.c.

I agree that the process of working with modules needs to be somehow better equipped, especially if there are more of them.

Organizational development of modules may look like this: you fork nxweb on bitbucket, or you clone nxweb somewhere else. Do your development there, commit, etc. To update nxweb do hg pull ... / nxweb

Q: Still need some documentation, a description of the possible modules. Examples of solving typical problems.

Yaroslav: As long as there is an example hello.c, a lot is clear from it. And the rest, sorry, only the source code. There is a wiki on bitbucket, there is some part of the documentation.

Q: Next - where to communicate to interested developers. Newsletter / Forum. Some place for questions and answers, with history and search.

Yaroslav: The other day I created two Google groups: nxweb and nxweb-ru .

Q: When will the stable versions freeze the API?

Yaroslav: Oh, I don’t know ... API freezing is not soon. Although, it must be said, between the 1st and 2nd versions the changes in the modules were minimal. Despite the fact that the insides of the server were all redone.

Q: This is a good sign. What are your plans for building an app utility library? More specifically: logging, template engine, work with headers, cookies.

Yaroslav: Headers and cookies are already parsed into tables and can be retrieved by name. I don’t know what else can be done with them. access_log - was not needed yet, in principle it is not complicated, but it will significantly slow down the work. The utility library to begin with replenished proxy functions, I think.

Q: access log is just not needed, you need application logging.

Yaroslav: Logging is common. The so-called error_log, function nxweb_log_error ("", ...). It is flushed after each line.

Q: Further, according to plans. It would be great to configure the number of threads automatically. I see two directions here: 1) at startup, determine the number of cores and put so many threads, 2) generate new threads while LA is below the specified value, well, with an upper limit, of course.

Yaroslav: Autoconfiguration is a big question. nxweb, in principle, has not yet been configured otherwise than by recompilation. Does it make sense to make one parameter automatically configurable, I don’t know. In fact, it’s easy to start any automatically determined number of threads at startup. It is a little more difficult to start or stop threads in the process, although it is also feasible.

For practical purposes, I still see the implementation of proxying. Primarily in Java. This is what I work with. Then - SSL, managed caching. Maybe the interface to the database.

The request and response are now fully buffered. This may be bad for some tasks (for example, upload a file). Therefore, I am thinking of introducing the concept of partially processed data handlers into modules, as well as sending data in parts.

Q: Proxy and SSL, like nginx can. Not to put nxweb outside.

Yaroslav: If you do not put it outside, then all the advantages of speed are lost. I tested nxweb yesterday behind nginx: 25k requests per second. Despite the fact that he gives 160 thousand, and nginx 130 thousand can. This is for the current stable version of nginx 1.0, which does not have keep-alive for backends. In version 1.1, you get 50 thousand requests per second - much faster, but still more than a three-fold slowdown.

Q: It seems that the use cases of nxweb can be divided into two large categories, with different needs:
  • A lot of rps for banners, toplines, etc. Then you need to put it outside, there are no options
  • The load on requests is small, but it is very important to give the answer as quickly as possible, so a piece of the application is written in C. Here it is quite appropriate to stand up for nginx for the convenience of admins.


Yaroslav: I believe that new use cases will appear with new versions of nxweb.

Also popular now: