Event-Driven Framework#
Design#
Everyone sees Envoy as a proxy, mainly implementing request forwarding with customizable logic. That’s not wrong. But like other middleware that require high throughput and low latency, the design must take into account load scheduling and flow control. A good scheduling mechanism should balance throughput, response time, and resource footprint.
Figure: Event-Driven Framework Design#
Dispatcher thread event loop: The
Dispatcher Threadwaits for events (epoll wait), and processes them when timeout occurs or events are triggered.The following events can wake up epoll wait:
Receiving inter-thread post callback messages. Mainly used for updating Thread Local Storage (TLS) data, such as cluster/stats updates.
Dispatcher handles inter-thread events.
Timer timeout events
File/socket/inotify events
Internal active events. Events triggered explicitly by other threads or the dispatcher thread itself.
Event handling
One full loop of event processing includes the above three steps. This full cycle is called an event loop, or sometimes an event loop iteration.
Implementation#
The above describes how event processing works at the kernel syscall level. Now we’ll look at how it is abstracted and encapsulated in Envoy’s codebase.
Envoy uses libevent, a C-based event library, and adds further abstraction with C++ OOP wrappers.
Figure: Envoy Event Abstraction Model#
How do you quickly understand the core logic of a project that makes heavy (even excessive) use of OOP encapsulation and design patterns, without getting lost in the sea of source code? The answer: follow the main thread. For Envoy’s event handling, that thread starts with the core libevent objects:
libevent::event_baselibevent::event
If you’re unfamiliar with libevent, check the section Core Concepts of libevent in this book.
libevent::eventis wrapped inImplBaseobjects.libevent::event_baseis included inLibeventScheduler←DispatcherImpl←WorkerImpl←ThreadImplPosix
Different types of libevent::event are further wrapped into different ImplBase subclasses:
TimerImpl– used for timer-based functions like connection timeouts or idle timeouts.SchedulableCallbackImpl– Under heavy load, Envoy needs to balance event responsiveness with throughput. To prevent a singleevent loopfrom doing too much work and delaying subsequent event handling, certain internal or timed processes can be scheduled to complete at the end of the currentevent loopor be deferred to the next one.SchedulableCallbackImplencapsulates this kind of schedulable task. Use cases include thread callback posts and retry logic.FileEventImpl– handles file/socket events.
Additional details are already well explained in the diagram above, so we won’t elaborate further.
Extended reading#
If you are interested in studying the implementation details, I recommend checking out the articles on my Blog:
And last but not least: Envoy author Matt Klein: Envoy threading model