1. 程式人生 > >TrafficServer原始碼初體驗–2

TrafficServer原始碼初體驗–2

剛剛在開篇中介紹到eventProcessor.start方法,這時trafficserver的事件處理子系統已經被啟動。接下來的程式碼分析過程種可以看到,這個事件處理子系統會被其他子系統使用。先把開篇種proxy/Main.cc的總控制流圖引過來,便於繼續分析。

eventProcessor之後,下面的程式碼

int use_separate_thread = 0;
int num_remap_threads = 1;
TS_ReadConfigInteger(use_separate_thread, "proxy.config.remap.use_remap_processor");
TS_ReadConfigInteger(num_remap_threads, "proxy.config.remap.num_remap_threads");
if (use_separate_thread && num_remap_threads < 1)
num_remap_threads = 1;

if (use_separate_thread) {
Note("using the new remap processor system with %d threads", num_remap_threads);
remapProcessor.setUseSeparateThread();
}
remapProcessor.start(num_remap_threads);

這段程式碼是根據配置檔案,來選擇是否為remap型別的事件單獨啟動執行緒作為事件處理。如果需要,則會呼叫eventProcessor中的 ET_REMAP = eventProcessor.spawn_event_threads(num_threads, “ET_REMAP”);

接下來的

RecProcessStart呼叫沒怎麼看明白,從命名上感覺像是一系列的同步相關的排程,包括stat統計同步功能、conf配置同步功能、以及遠端同步功能,暫時不知做什麼的,細節等回過頭來在仔細研究吧。

然後 init_signals2 呼叫,根據我的理解應該是定期的將記憶體中的資料dump到stderr輸出中,用於日誌跟蹤。

接下來的這段程式碼用於處理命令列風格的啟動,如

traffic_server -C list 用於現實cache的配置情況。注意,這裡的cmd_mode是處理traffic_server的命令模式的命令,而不是traffic_server的啟動引數,換句話說,就是處理traffic_server -C (或者traffic_server –command) 這個模式的命令,包含(list,clear_cache,clear_hostdb,help)等。可以通過traffic_server -C help 看到命令模式支援的命令列表。

if (command_flag) {
// pmgmt initialization moved up, needed by RecProcessInit
//pmgmt->start();
int cmd_ret = cmd_mode();
if (cmd_ret != CMD_IN_PROGRESS) {
if (cmd_ret >= 0)
_exit(0);               // everything is OK
else
_exit(1);               // in error
}
}

接下來如果沒有定義過INK_NO_ACL(雙重否定,換句話說就是如果需要ACL控制) 則需要呼叫
initCacheControl() ,這個呼叫讀取 proxy.config.cache.control.filename 這個路徑對應的配置檔案,載入關於cache方面的訪問控制配置。

接下來

initCongestionControl();
IpAllow::InitInstance();
ParentConfig::startup();
#ifdef SPLIT_DNS
SplitDNSConfig::startup();
#endif

分別是關於擁塞控制配置、ip allow,以及dns配置的處理。其中ParentConfig::startup()的作用暫時還沒搞得很明白。

接下來的 netProcessor.start();這行呼叫,真正開啟了網路事件的處理,包括epoll_wait的loop,是通過eventProcessor實現的event loop.

接下來進行的程式碼段如下:

#ifndef INK_NO_HOSTDB
dnsProcessor.start();
if (hostDBProcessor.start() < 0)
SignalWarning(MGMT_SIGNAL_SYSTEM_ERROR, "bad hostdb or storage configuration, hostdb disabled");
#endif

這段程式碼啟動dns事件處理以及hostDB事件處理,hostDB應該是負責cache的儲存。

隨後:

#ifndef INK_NO_CLUSTER
    clusterProcessor.init();
#endif

啟動cluster相關事件處理。

接下來的程式碼是載入http服務相關配置。

 // Load HTTP port data. getNumSSLThreads depends on this.
    if (!HttpProxyPort::loadValue(http_accept_port_descriptor))
      HttpProxyPort::loadConfig();
    HttpProxyPort::loadDefaultIfEmpty();

接著啟動cache,udpNet,sslNetProcessor對應的事件處理。

cacheProcessor.start();
udpNet.start(num_of_udp_threads); // XXX : broken for __WIN32
sslNetProcessor.start(getNumSSLThreads());

貌似udpNet那塊並沒有使用epoll等高效的處理非同步非阻塞處理方式,具體原因還沒有分析。

然後啟動 日誌處理, Log::init(remote_management_flag ? 0 : Log::NO_REMOTE_MANAGEMENT);

擴充套件處理,處理擴充套件邏輯.(extension,而不是plugin,後面還會有一次對plugin的初始化)

plugin_init(system_config_directory, true); // extensions.config

start_stats_snap();啟動統計快照

body_factory = NEW(new HttpBodyFactory);

eventProcessor.schedule_every(NEW(new ShowStats), HRTIME_SECONDS(show_statistics), ET_CALL); 顯示當前統計資訊

接下來初始化外掛、啟動transformProcessor。transformProcessor應該是將使用者訪問的內容從源站取到ts上後進行一系列的轉換,用於最終傳回給請求呼叫者。

#ifndef TS_NO_API
    plugin_init(system_config_directory, false);        // plugin.config
#else
    api_init();                 // we still need to initialize some of the data structure other module needs.
    extern void init_inkapi_stat_system();
    init_inkapi_stat_system();
    // i.e. http_global_hooks
#endif
#ifndef TS_NO_TRANSFORM
    transformProcessor.start();
#endif

接下來準備啟動http的proxy server,也就是我們看到的預設8080的埠將被開啟,ts將接受使用者請求,實現cdn服務。

    init_HttpProxyServer();
    int http_enabled = 1;
    TS_ReadConfigInteger(http_enabled, "proxy.config.http.enabled");

    if (http_enabled) {
#ifndef INK_NO_ICP
      int icp_enabled = 0;
      TS_ReadConfigInteger(icp_enabled, "proxy.config.icp.enabled");
#endif
      start_HttpProxyServer(num_accept_threads);
#ifndef INK_NO_ICP
      if (icp_enabled)
        icpProcessor.start();
#endif
    }

    // "Task" processor, possibly with its own set of task threads
    tasksProcessor.start(num_task_threads);

    int back_door_port = NO_FD;
    TS_ReadConfigInteger(back_door_port, "proxy.config.process_manager.mgmt_port");
    if (back_door_port != NO_FD)
      start_HttpProxyServerBackDoor(back_door_port, num_accept_threads > 0 ? 1 : 0); // One accept thread is enough

上面程式碼提到的icpProcessor是用於處理ICP協議( Internet Cache Protocol)的處理執行緒。taskProcessor是用於處理任務。

隨後啟動管理埠,可用於http web ui的管理

 int back_door_port = NO_FD;
    TS_ReadConfigInteger(back_door_port, "proxy.config.process_manager.mgmt_port");
    if (back_door_port != NO_FD)
      start_HttpProxyServerBackDoor(back_door_port, num_accept_threads > 0 ? 1 : 0); // One accept thread is enough

接下來啟動socks代理

#ifndef INK_NO_SOCKS
    if (netProcessor.socks_conf_stuff->accept_enabled) {
      start_SocksProxy(netProcessor.socks_conf_stuff->accept_port);
    }
#endif

updateManager.start();
用於啟動自動更新ts配置。

run_AutoStop();
用於自動停止ts,停止後會有processmanager重啟,這個機制只在系統存在環境變數PROXY_AUTO_EXIT大於0的時候生效,並且程序會在PROXY_AUTO_EXIT秒後自動退出,然後由processmanager重啟這個程序。有點類似apache裡面的maxchildrequest那個玩意兒,到一定程度後,程序自動退出,隨後由監控程序重啟,這樣可以避免由於程式疏忽造成的記憶體洩漏問題。

最後啟動主執行緒。
this_thread()->execute();

到此為止,proxy/Main.cc的內容已經粗略的過了一遍,接下來準備對比較重要的部分深入探討下。