Architecture.
The following diagram shows the internal design of petrel. It embeds lua and uses a couple of libraries to implement the http and http2 protocols as well as fibers and network/disk IO. Check out the github page for detailed build instructions of petrel and the dependencies.
OS base.
The lowest layer uses libraries like boost.asio, boost.fiber or nghttp2 to implement the following functionalities:
- Network IO: async socket operations, dns lookups etc (boost.asio)
- Fibers: userspace context switches (boost.fiber)
- HTTP/1 protocol (boost.http)
- HTTP/2 protocol (nghttp2)
- syslog: logging base (posix syslog or console via std)
Petrel core.
The core layer is responsible for running the web server and executing the lua code and it's interaction with native code. It provides:
- HTTP server: http/http2 protocols, loading the handler routing table (router) to pass requests to the installed request handlers
- Lua environment: loading builtin and external native libraries, loading/executing the users lua code
- Metrics: providing a metrics interface (registry) and metrics implementations (meter, timer, counter) with graphite support
- DNS cache: a boost.asio resolver compatible interface to cache DNS lookups
- Logging: interface for level based logging to the console or syslog
C++ libraries.
Based on the core layer petrel provides several builtin libraries to configure petrel itself (setup routes, load external libraries), metrics, logging etc. A user can implement native libraries and load them into the lua environment.
Lua library interface.
The library interface makes it easy to extent petrels functionality. Libraries will be implemented in C++ and get linked to shared objects which can be loaded by petrel during the bootstrap phase. The lua engine will expose all builtin and external libraries to the lua environment to make them available to the users lua code.
Lua business logic.
The highest layer is entirely based on the users code. The services behavior will be implemented in lua. The environment is restricted (no network/disk IO, no system calls, no coroutines) to keep the business logic as simple as possible. If functionality is missing a user can implement his own native extensions or lua modules.
Request processing.
Each request runs in its own fiber and in an isolated lua state. If a user needs to share data between requests, (s)he has to go through a native library and needs to make sure the library is thread safe.
Lua states live in a state buffer and get pre-created asynchronously. The diagram below shows the request processing chain.
States get returned to the state buffer to be reused. The lua code can invoke native calls through the library interface.