目錄
第一節 main函數
第二節 建立server
建立ListenerManager
建立ClusterManager
第三節 啟動server
envoy在處理請求時用到的主要的元件為Listener和Cluster,Listener負責監聽downstream發送的請求,Listener監聽到downstream發送的請求後,由Cluster來選擇upstream中的某個端點來處理該請求,最終Listener将端點的回複資訊傳回到downstream,其架構圖如下所示:
Listener,Cluster在系統啟動時會根據其配置被初始化。 Listener,Cluster和系統其餘元件構成了一個完整的伺服器,當所有這些元件初始化成功後,envoy伺服器也初始化成功了,運作中的伺服器可以不斷的利用Listener,Cluster等元件來完成其工作。
envoy有一個基于事件的線程模型,主線程會管理envoy伺服器的生命周期,配置處理,運作狀态等,其餘還有一些工作線程,這些工作線程用來處理各種網絡請求。所有線程都圍繞一個事件循環(libevent)操作,任何給定的downstream的TCP連接配接(包括其上的所有多路複用流)在其生命周期内都将由一個工作線程處理。每個工作線程會維護一個到 upstream的端點的TCP連接配接池。
接下來通過分析envoy的啟動過程源碼來看envoy是怎麼建立其伺服器的。
envoy源碼位址:https://github.com/envoyproxy/envoy.git
第一節 main函數
envoy實作main函數的檔案為source/exe/main.cc,main函數調用MainCommon::main實作了envoy的啟動,MainCommon::main的代碼如下所示:
source/exe/main_common.cc
圖1-1
第234,235行代碼一起執行個體化了一個server,255行代碼啟動該server,至此,envoy就啟動起來了。
第二節 建立server
envoy通過啟動一個伺服器server來工作,該server由各種元件組成,這裡最關鍵的元件就是用于管理系統中的監聽器listener的監聽管理器ListenerManager,用來管理叢集cluster的叢集管理器ClusterManager, server在初始化時會初始化這些元件。
第一節中234,235建立server時最終調用的server建立代碼如下:
source/exe/main_common.cc
圖2-1
這裡是調用的Server::InstanceImpl類的構造函數構造了server的執行個體,下面來分析Server::InstanceImpl類的構造函數,代碼如下所示:
source/server/server.cc
圖2-2
Server::InstanceImpl類構造了server對象,并建立了該server需要的所有元件,比如用于管理系統中的監聽器listener的監聽管理器ListenerManager,用來管理叢集cluster的叢集管理器ClusterManager等。
95行:建立并初始化檔案日志系統,用于管理日志;
105行:初始化envoy的熱重新開機功能子產品,用于管理envoy伺服器的熱重新開機;
106行:初始化一個DrainManager,這個在伺服器熱重新開機或者動态删除Listener時會使用
107行:Server::InstanceImpl構造函數調用initialize來建立伺服器的其餘元件,監聽管理器ListenerManager和叢集管理器ClusterManager也是在這裡建立的。
initialize核心代碼如下所示:
source/server/server.cc
圖2-3
圖2-4
圖2-5
346行:從配置檔案中加載bootstrap資訊
510行:建立ListenerManager來管理Listener
591行:通過從配置檔案加載出來的bootstrap資訊來建立靜态的ClusterManager和listener
609行:初始化靜态ClusterManager
建立ListenerManager
ListenerManager擁有一個或多個工作線程,每個工作線程會去處理一個給定的downstream的TCP連接配接,它會維護一個到 upstream的端點的TCP連接配接池。ListenerManager負責建立這些工作線程,工作線程建立好了以後ListenerManager就建立完成了。
上文中ListenerManager在source/server/server.cc的510行通過ListenerManagerImpl類的構造函數被建立,該構造函數傳入了4個參數:
第一個參數this,是這裡的server對象;第二個參數listener_component_factory_是監聽器元件工廠類ProdListenerComponentFactory,它将建立真實的套接字,在系統熱啟動時它會嘗試從父程序擷取套接字;第三個參數worker_factory_是一個工作線程工廠類ProdWorkerFactory,它将用于為伺服器建立工作線程;第四個參數是個布爾類型,訓示是否啟用事件分發器的統計功能,這個值預設是false,因為啟用了事件分發器的統計功能後資料量可能會非常大,如果需要啟用該統計功能,需要将bootstrap的enable_dispatcher_stats的值配置為true。下面我們來看ListenerManagerImpl類構造函數的代碼:
envoy/source/server/listener_manager_impl.cc
圖2-6
261行:server.options().concurrency() 是指令行指定的工作線程的個數,預設是一個
262行:建立工作線程,這裡的worker_factory就是上文中提到的工作線程工廠類ProdWorkerFactory,其建立線程的額函數createWorker()如下所示:
envoy/source/server/worker_impl.cc
圖2-7
建立ClusterManager
圖2-5中,server建立了ClusterManager,config_是一個Configuration::MainImpl對象,其initialize方法實作如下:
source/server/configuration_impl.cc
圖2-8
94行:通過bootstrap的中叢集的配置來建立叢集管理器。
96行:通過bootstrap的中配置的靜态資源來建立了一系列靜态的監聽器。
98~101行:将這裡建立的靜态監聽器加入到了前面已經建立好的監聽管理器ListenerManager中
到這裡,envoy伺服器就建立完成了。接下來就要啟動伺服器了
第三節 啟動server
圖1-1中255行代碼啟動了server。這裡最終調用的是Server::InstanceImpl對象的run()方法,run()代碼如下:
source/server/server.cc
圖3-1
run()通過調用Server::RunHelper來運作,Server::RunHelper隻有一個構造函數,這個構造函數在構造Server::RunHelper對象時啟動server,Server::RunHelper的關鍵代碼如下:
source/server/server.cc
圖3-2
732行:這裡初始化了一個init_watcher_,這是一個Init::WatcherImpl對象,這個對象的構造函數有兩個參數,第一個參數是個字元串,是這個對象的名稱,第二個參數是個回調函數,當Init::WatcherImpl對象被初始化管理器InitManager調用時,會執行該回調函數,回調函數中執行的是post_init_cb回調函數,從圖3-1可知這裡的post_init_cb回調函數中執行了startWorkers()函數,前文中在建立ListenerManager時建立了建立工作線程,這裡startWorkers()函數就是将ListenerManager中的工作線程都啟動起來。
770行:這裡設定ClusterManager的初始化回調函數,當ClusterManager初始化成功後會調用該回調函數,該回調函數執行時,在786行InitManager會處理732行的init_watcher_對象,此時,post_init_cb函數會被執行,ListenerManager中的工作線程啟動起來了,到此,ClusterManager和ListenerManager都就緒了,整個envoy伺服器也就緒了。
Note:ClusterManager初始化包括靜态初始化和動态初始化,如圖2-5 ClusterManager的靜态初始化發生在建立server時,當運作server時,靜态的ClusterManager已經初始化成功,ListenerManager中工作線程會立即開始工作。CDs可以動态初始化ClusterManager,CDs相關的内容會在後續介紹