background
envoy底層使用libevent構(gòu)建網(wǎng)絡(luò)IO處理流程。同步代碼轉(zhuǎn)換成event based model需要在遇到IO的時(shí)候拆分成callback的形式,基本上切換到不同IO對(duì)象時(shí)都會(huì)進(jìn)行callback切換,很容易形成callback hell,對(duì)于開發(fā)者來說是件比較痛苦的事情。所以golang這種自帶協(xié)程的語言,即有開發(fā)上的便捷,又能享受性能上的提升,作為后端服務(wù)開發(fā)大大提高了效率。
但是作為proxy,內(nèi)存分配回收頻率是非常大的,作為關(guān)鍵路徑上的核心組件,如果有GC對(duì)系統(tǒng)影響還是比較大的,使用c++是最合適的語言。
envoy使用absl作為基本的c++ library,是google開源c++項(xiàng)目的base library,之前看webrtc源碼時(shí)也有看到。
thread model
envoy由一個(gè)main thread、一些(cpu核數(shù)個(gè)或通過concurrency參數(shù)指定)worker thread、輔助線程(file flush)組成。
每個(gè)線程擁有自己獨(dú)立的dispatcher loop,互相之間不干擾,獨(dú)立運(yùn)行。代碼邏輯寫起來很簡(jiǎn)單,基本不需要考慮加鎖的事情。
thread loop
使用libevent中的event_base作為最底層的event scheduler
代碼在 source/common/event/dispatcher_impl.h。 可以看到post是線程安全的。
void DispatcherImpl::run(RunType type) {
run_tid_ = api_.threadFactory().currentThreadId();
// Flush all post callbacks before we run the event loop. We do this because there are post
// callbacks that have to get run before the initial event loop starts running. libevent does
// not guarantee that events are run in any particular order. So even if we post() and call
// event_base_once() before some other event, the other event might get called first.
runPostCallbacks();
if (type == RunType::NonBlock) {
base_scheduler_.nonBlockingLoop();
} else {
base_scheduler_.blockingLoop();
}
}
在event based model實(shí)現(xiàn)中會(huì)使用大量的callback引用其它對(duì)象,如果直接刪除,可能會(huì)在callback中使用導(dǎo)致core dump。DispatcherImpl中添加了deferredDelete接口延遲對(duì)象的刪除,deferred_delete_timer_ enable之后會(huì)在下次event loop中進(jìn)行真正的刪除。
main thread的dispatcher_在source/server/server.cc中InstanceImpl constructor中建立。listener_manager_初始化時(shí)新建options().concurrency()個(gè)worker,每個(gè)worker有自己獨(dú)立的dispatcher。
//source/server/worker_impl.cc
WorkerPtr ProdWorkerFactory::createWorker(OverloadManager& overload_manager) {
Event::DispatcherPtr dispatcher(api_.allocateDispatcher());
return WorkerPtr{new WorkerImpl(
tls_, hooks_, std::move(dispatcher),
Network::ConnectionHandlerPtr{new ConnectionHandlerImpl(ENVOY_LOGGER(), *dispatcher)},
overload_manager, api_)};
}
InstanceImpl run()時(shí)listener_manager_ startWorkers()把所有的listener添加到worker中。
WorkImpl start()時(shí)會(huì)創(chuàng)建真正的thread。
// source/server/worker_impl.cc
void WorkerImpl::start(GuardDog& guard_dog) {
ASSERT(!thread_);
thread_ =
api_.threadFactory().createThread([this, &guard_dog]() -> void { threadRoutine(guard_dog); });
}
多線程服務(wù)是很容易產(chǎn)生死鎖的。在這種event based的thread loop中,所有event的callback都是由dispatcher處理的,在dispatcher中添加一個(gè)timer以一定間隔不停地更新touch_time,由另外的線程檢測(cè)touch_time是否過期,即可檢測(cè)程序是否發(fā)生了死鎖或死循環(huán)。
inter thread communication
如thread loop一節(jié)中所述,dispatcher的post是加鎖的,通過post將callback放到其它線程的dispatcher待執(zhí)行列表中,在dispatcher所在線程的loop中真正被執(zhí)行。
通過timer來觸發(fā)post list func的執(zhí)行
在ListenerManagerImpl::addListenerToWorker中通過回調(diào)中調(diào)用post,在main thread中完成listener統(tǒng)計(jì)及其它工作。
threadlocal也使用post完成thread_local_data_的link、remove、update等。
thread local storage
實(shí)現(xiàn)在source/common/thread_local/thread_local_impl.*中。分配了一個(gè)slot vector。
參考
https://blog.envoyproxy.io/envoy-threading-model-a8d44b922310