The proton connection engine ============================ The next proton release will introduce `proton::connection_engine` in the C++ binding. (Possibly `Qpid::Proton::ConnectionEngine` in ruby if time permits)
The `connection_engine` is alternative to `proton::container` which is based on the C reactor. I'll explain why I think we need an alternative and then what the connection_engine is. Check the code for more details - there is some documentation, there will be more before release. Why do we need the connection_engine? ------------------------------------- The website clearly states the goal: "Qpid Proton is a high- performance, lightweight messaging library. It can be used in the widest range of messaging applications, including brokers, client libraries, routers, bridges, proxies, and more. Proton makes it trivial to integrate with the AMQP 1.0 ecosystem from any platform, environment, or language." I believe the original proton design met all these goals *except* ease of use. The messaging-handler and reactor did a good job on ease of use, but undermined the "widest range" goal. The connection_engine is going for the lot. The proton core is a pure AMQP protocol engine. It is highly portable, makes few assumptions and imposes few requirements. There is no thread synchronization and no IO code, there is no shared state between connections. You can process data from any IO source, and you can process separate connections concurrently. The only requirement is not to process a single connection concurrently. This core can scale from simple clients to high-performance, multi-threaded servers like qpidd, qpid dispatch and JBoss A-MQ. However the core is not very easy to use. Correctly co-ordinating the `pn_connction_t`, `pn_transport_t` and `pn_collector_t` with IO is a challenge. Enter the reactor: a framework that handles this co-ordination and manages socket IO for multiple connections. It delivers simple events (via the messaging-handler interface) to user-defined event handlers. It solves the ease-of-use problem very nicely. Unfortunately it introduces new assumptions and restrictions.The C reactor uses sockets and `poll` to serialize events from multiple connections *in a single thread*. (On Windows it uses IOCP but still in a single thread.) That works for some scenarios but high-scale, high-performance servers often need multi-threading and other dispatch mechanisms such as epoll, kqueue, solaris event ports, proactive iocp etc. Developers often want to use portable frameworks like boost::asio, libuv, libevent etc. The C reactor can't participate in any of this. The C reactor also creates problems for some language bindings. In python and ruby it creates problems with interprter locks. The python binding has workarounds (IMO risky ones), the ruby reactor is unusable in a program with any other ruby threads. The Go language is concurrent so serializing with `poll` makes no sense. The standard `net.Conn` interface doesn't have a file descriptor so you can't included it in the reactor's poll even if you want to. Windows IOCP is designed to be used as a concurrent *proactor*, which is not the same as a reactor. The proton reactor uses IOCP internally on windows, but forces it to act like `poll` which eliminates many of its benefits. Developing an IO framework that is portable, scalable, performant and usable is *hard*. It is not in the scope of the Qpid project, Qpid is about AMQP. So we want something that is easy to use (like the reactor) but that focuses on AMQP, is highly portable, and lets the user choose the IO and concurrency they want (like the proton core). The connection_engine --------------------- The `connection_engine` is a wrapper around the proton core. Each `connection_engine` manages a *single connection*. It hides the interaction between IO and the `pn_transport_t`/`pn_collector_t` and delivers events to the user's handler, like the reactor. Each engine is independent with no state shared between them, so they can be used in multiple threads like the proton core. Applications for the `connection_engine` are like those for the reactor, using the same handler and event classes. With a little care you can write handlers that will work with either. The C++ broker examples for reactor and connection_engine demonstrate this. The connection_engine class itself does no IO. You subclass and override simple methods `io_read`, `io_write` and `io_close` with your IO code. There is a `socket_engine` subclass provided which implements TCP sockets, so you can write applications out of the box without writing your own IO. The engine examples are copies of the reactor examples with a socket_engine instead of a container, you can compare them to see how little difference there is. You can use `socket_engine` with multiple threads, with poll, epoll, kqueue, boost::asio or any framework you choose. The broker example illustrates. You can implement your own engine classes. For example if you use boost::asio, you can implement an engine that uses boost::asio::read/write and get the portability/performance benefits of that library. You can implement engines for unusual platforms or IO libraries that are unrelated to TCP, sockets or Unix-like file descriptors. For example the connection_engine test code uses an in- memory connection using std::deque<byte> as a transport. Sub-classes of `connection_engine` are implemented in your chosen language, *not* in C. For example the ruby `ConnectionEngine` uses the native ruby `IO` class. This avoids locking problems, since native ruby IO is designed to work with ruby's threading and locking rules. It also makes it easy to integrate with frameworks in you chosen language, for example cool.io or celluloid in ruby. --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
