📜 ⬆️ ⬇️

nxweb - HTTP server for C applications

nxweb is a 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 - Jaroslav Stavnichy. I was interested in the project primarily because it represents a real alternative to existing solutions, each of which has its own drawbacks. The choice is good. Perhaps you will enjoy 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 about yourself, what do you do, where do you work?

Yaroslav: I have been developing software for a long time. Nexoft company. I write mostly in Java, but I also occasionally recall C / C ++ when I need to do something fast. For example, banner banner for a site with high attendance.
')
Q: So nxweb was created with the goal - to turn 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 attendance - how many requests per second?

Yaroslav: Now comes about 100 requests per second. Some of them are sent to Tomcat. But up to 20 banner codes are inserted into each HTML page. The server is, in principle, hard, so I want the simple task of sending short HTML fragments to not burden him too much. Maybe, of course, it would all work on a pearl, 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 the most pleasant thing. There are a lot of tinsel imposed portability care. In addition, they are multi-process, and this is a separate problem. With threads, everything is much simpler. Shared memory is priceless. I'm used to Java, there it is. Got inside nginx, it's all very cumbersome there, plus, again, shared memory, etc.

I decided to try to implement my application on a micro-engine like Mongoose, I ran across it on the network earlier. He began to dig, found several other alternatives, incl. G-WAN. I liked everything very much, except closed-source. I tried G-WAN exactly as an application server, and not as a static shipper. But rather quickly I began to stumble upon glitches, and it was impossible to find and fix them because of the closeness of the code.

So, I wrote a Mongoose twister, I began to test the speed and found that it was too low compared to G-WAN. It was only later that I guessed that 2500 threads would be launched, but at first I thought that 8-16 would be enough. With so many there at all.

Q: Because mongoose uses one OS thread for each request?

Yaroslav: Yes, actually, 2500 threads are not a solution either. In memory, they eat a ton, and, again, the 2501st query will appear - and hello ...

I began to dig deeper, I found microhttpd and libevent. In terms of performance, when compared with the G-WAN, they looked pale. While I was dealing with all this, I was digging into the source code, it was understood that it was not so difficult to write a web server. In mongoose, for example, and so the entire HTTP response must be manually written to the developer of the module.

libevent looked the most promising, but single-threaded. I decided to redo the same on libev (a lightened alternative to libevent). This is how nxweb was born. The primary primary 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 be fast. And then you will write under mongoose, and, it is asked, for the sake of what tried, if he on brakes will lower everything.

I did not have a goal to compete with anyone. I wanted to get closer to the G-WAN, as the standard of speed. But as soon as the project was published, the flow of letters immediately began. They began to encourage me all. So I had to spend a few more days, puffing, to overtake the G-WAN.

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

By the way, nginx is 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 unless it is requested. With nginx generally compete meaningless. It even surprises how such a functional server provides such a high speed. I made sure here that the elementary use of sprintf instead of strcat is capable of landing speeds of 5-10 thousand requests per second. Those. the battle is already over every extra CPU instruction.

Q: Well, tell me about the nxweb architecture in brief.

Yaroslav: The main thread listens to the socket and cracks incoming connections over the network flows (each has its own turn to avoid mutexes). Network streams (it makes sense to run them one per processor core) provide the HTTP protocol. Decode the request, pass it to the module-processor. Workers are an optional thing, conceived 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 a log file and transfer control to _nxweb_main (). He can also run a demon. And double: one daemon makes sure that the second (where nxweb actually turns) does not stop. If the internal daemon crashes, the first one restarts it automatically. If desired, main.c can be replaced by any other. The main thing is to call _nxweb_main ().

Q: Why doesn't every network thread accept a loop? Then the OS will be able to distribute connections and you can make several accept-s in parallel on different cores.

Yaroslav: I tried both options, incl. accept network flow without the participation of the main. I did not find any difference in speed, I returned to a single acceptor, as it seemed to me that this reduces the percentage of connection errors. In nginx and G-WAN, some of the connections hang, and do not carry the load. See my comment about real concurrency on the benchmarks page.

Q: I see. The first version used libev, but now it is written in the wiki 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 sharpened for my needs and is not an independent project. In other words, I now work directly with epoll. nxweb is not portable at all. It works only on Linux and even on the kernel> = 2.6.22. I use Edge-Triggered epoll, which libev does not support because of its intolerance. Yes, libev is a super cool thing, I would stop at it, but the people wanted faster. That had to suffer.

Q: It was Edge-Triggered allowed to get the latest increase?

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

Q: Igor Sysoev and the libev authors argue that the epoll has a lot of problems. Have you already encountered them?

Yaroslav: There is a hope that these problems in the modern core have already been cleaned. Still, many years have passed. I test on 2.6.32 and 3.0.0, I have not encountered any problems.

Q: What about memory backup? Do you allocate a large pool to make less sisols?

Yaroslav: Free-list for connections and its analogue obstack for forming answers.

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

Yaroslav: Implementation of its own. 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, static shipping is a module matter. 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. Now my module sendfile.c forms only the main headers, although I’m probably going to support Range and If-Modified-Since soon.

Yes, probably there are ready-made parsers checked for errors. But, on the other hand, in my own parser, I quickly catch the error, if they tell me about it, than in someone else's. There really is an account on the CPU instructions. And all the independent projects of parsers are so thorough 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 behind it all the incorrect requests are already filtered.

Yaroslav: At the moment (version 2.0) nxweb is not very ready to put it out. Mainly because real applications are not only C-scripts. You need to tie and Java and statics and everything else. Unless we are talking about the implementation of some 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 corortina has its own stack, so the library contains several instructions in assembly language for switching the stack. This allows you to write ordinary sequential code, without callbacks such as on_request, on_success ...

Yaroslav: Yes, I heard about Korutiny. But if I’ve already written everything by callbacks, I won’t get any performance gain by switching to corutines. Only an extra memory will go to the storage of the stacks. Moreover, problems with gdb are not the most pleasant either. It's a matter of taste and habit. Korutiny need to get used to. This is a special concept.

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

Yaroslav: For slip and go to the base - just for this I provided the workers. And checked the first thing. If 100 workers are configured and each worker does sleep 1 second, then the server smoothly and without hesitation fulfills 100 requests per second. Neither G-WAN, nor nginx can handle it. They either issue 3-4 requests per second, or stupidly hang.

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

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

Yaroslav: Well, I already screwed it as a backend to a site with a rather large attendance, i.e. launched in production. A month has passed, and not a single restart / failure. Although, the backend is not the toughest baptism in battle.

I believe that the status - alpha. In particular, not all nxweb functions are thoroughly tested. For example, chunked request encoding. On the test examples it works, but it will not come out well. It is also unclear what will be the stability with the incorrect behavior of the client. For example, requests with errors can trigger a crash.

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

Q: Personally, as a potential developer for nxweb, I need the ability to fully manage 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, write a header, parrot cookies, zaproksiruy on the backend. This is how I imagine programming critical sections on nxweb.

Yaroslav: I must say that this is about my approach. Create basic functions that can be used to configure server operation. But to configure on With, but not by means of some config file. Because creating an analog config that nginx has is a very difficult task.

Q: The next question, rather for show, what do you think about Windows support?

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

Q: G-WAN has a pretty handy feature - auto-compiling 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 in nxweb?

Yaroslav: Yes, I really liked her. But I do not plan yet. Excess hemorrhage, which does not bring me anywhere to begin to fully use nxweb for their needs.

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

Yaroslav: So far, only in what I am already distributing. Open repository, where you can download the source and run make. At the moment, a complete compilation of the project takes seconds. I don’t see any sense in building a library.

Q: Suppose I want to write my hello world application. My actions?

Yaroslav: hello.c is a template template. Further, in the Makefile there are small comments about the connection of their modules. There are also 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 should somehow be better equipped, especially if they become more.

Organizationally developing modules may look like this: you fork a nxweb on a bitbucket, or you clone nxweb somewhere else. You make there your designs, commit, etc. To update nxweb do hg pull ... / nxweb

Q: You also need some kind of documentation, a description of possible modules. Examples of solving typical problems.

Yaroslav: While there is an example of hello.c, much is clear from it. And the rest, sorry, just the source code. On bitbucket there is a wiki, there is some part of the documentation.

Q: Next is 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 do not know ... API freezing is not soon. Although, I must say, between the 1st and 2nd versions, the changes in the modules were minimal. With that, the inside of the server was all redone.

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

Yaroslav: Headers and cookies are already parted into tables and can be retrieved by name. I do not know what else to do with them. access_log - was not needed yet, in principle it is not complicated, however, it will significantly slow down the work. The utility library for the beginning will be replenished with functions of proxying, I think.

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

Yaroslav: Logging is common. The so-called error_log function nxweb_log_error ("", ...). He fills after every line.

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

Yaroslav: Autoconfiguration is a big question. In principle, nxweb is not yet configured except by recompiling. Does it make sense to make one parameter automatically configurable, I don’t know. In fact, it is not difficult to start any automatically defined number of threads at startup. It is a little more difficult to start or stop threads in the process, although it is also realizable.

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

Now the request and response are fully buffered. This can be bad for some tasks (for example, uploading a file). Therefore, I am thinking of introducing in modules the concept of processors of partially received data, as well as sending data in parts.

Q: Proxy and SSL, like nginx can. Do not 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: 25 thousand requests per second. Moreover, he himself 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 it turns out 50 thousand requests per second - much faster, but, all the same, more than threefold slowdown.

Q: It seems that the use cases of nxweb can be divided into two broad categories, with different needs:


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

Source: https://habr.com/ru/post/263545/


All Articles