twemproxy0.4原理分析-系統初始化過程原理分析
概述
本文介紹twemproxy的系統初始化過程。該過程包括以下幾個方面的內容:
- 讀取配置檔案,根據配置檔案初始化資料結構
- 和後臺伺服器建立連線,形成伺服器連線池
- 初始化事件處理框架,並設定最開始的事件處理函式
- 建立twemproxy的監聽socket,並把該監聽socket新增到事件處理框架中,用來監聽客戶端的連線請求。
載入配置檔案
twemproxy0.4是根據YAML檔案格式來進行後臺引數配置。在程序啟動時通過-c或–conf-file選項來指定配置檔案。啟動的實際命令如下例子:
cd twemproxy-0.4.1 ./src/nutcracker -c ./conf/nutcracker.yml
若下載的是twemproxy0.4的原始碼,配置檔案的樣例檔案在原始碼根目錄下的conf目錄下。
樣例配置檔案example.yml如下:
alpha: listen: 0.0.0.0:22121 hash: fnv1a_64 distribution: ketama auto_eject_hosts: true redis: true server_retry_timeout: 2000 server_failure_limit: 1 servers: - 127.0.0.1:6379:1 - 127.0.0.1:6378:1 - 127.0.0.1:6377:1 beta: listen: 0.0.0.0:22122 hash: fnv1a_64 hash_tag: "{}" distribution: ketama auto_eject_hosts: false timeout: 400 redis: true servers: - 127.0.0.1:6380:1 - 127.0.0.1:6381:1 - 127.0.0.1:6382:1
配置檔案中各個引數的說明如下:
- listen引數
配置twemproxy監聽的地址和埠。注意:每個服務池都需要配置一個監聽地址和埠。監聽的地址可以相同,但埠需要不同,這樣twemproxy才能區分不同的伺服器池。
從example.yml配置檔案中的listen引數可以看出,有兩個redis的伺服器池,這兩個伺服器池分別由監聽22122和22121的代理來服務。
- client_connections
允許來自redis客戶端的最大連線數。預設情況下無限制,但作業系統施加的限制仍然存在。
- hash
使用的hash函式的名字。可以是以下幾種:
函式名 |
---|
md5 |
crc16 |
crc32 |
crc32a |
fnv1_64 |
fnv1a_64 |
fnv1_32 |
fnv1a_32 |
hsieh |
murmur |
jenkins |
- hash_tag引數
由於key的分配非常重要,在使用叢集時,最好能讓key的分配可控性更強。
該引數可以讓key的分配可控性更強。若為該伺服器池配置了hash_tag,將會使用hash_tag之內的部分作為key分配的依據,否則會使用整個key的內容作為分配依據。
- distribution
設定key分配演算法,可以有三種:
(1) ketama: 通過一致性hash演算法來選擇後端伺服器
(2) modula: 取模方式來選擇後端伺服器
(3) random: 隨機選擇一個後端伺服器池中的伺服器
- timeout
我們等待建立與伺服器的連線或從伺服器接收響應的超時值(以毫秒為單位)。預設情況下會無限期地等待。
- backlog
TCP listen的backlog引數。預設為512。
- preconnect
一個布林值,用於控制在程序啟動時twemproxy是否應預先連線到此池中的所有伺服器。預設為false。
- redis
一個布林值,用於控制伺服器池是否使用redis或memcached協議。預設為false。
- redis_auth
在連線時對Redis伺服器進行身份驗證。
- redis_db
要在池伺服器上使用的庫編號(redis中可以通過select命令來選擇資料庫)。預設為0.注意:Twemproxy將始終以庫 0的形式呈現給客戶端。
- server_connections
可以開啟到每個伺服器的最大連線數。預設情況下,我們最多開啟1個伺服器連線。
- auto_eject_hosts
一個布林值,用於控制伺服器在連續失敗server_failure_limit次數時是否應暫時去除。預設值為false
- server_retry_timeout
當auto_eject_host設定為true時,在臨時彈出的伺服器上重試之前等待的超時值(以毫秒為單位)。預設為30000毫秒。
- servers
此伺服器池的伺服器列表。格式如下:
ip:port:weight //ip地址:埠:權重
或
name:port:weight //主機名:埠:權重
系統初始化
系統初始化主要完成以下幾件事情:
- (1) 初始化三個空閒佇列:空閒mbuf佇列,空閒msg佇列,空閒conn佇列
- (2) 初始化伺服器池,若需要還要和後端服務建立連線
- (3) 初始化伺服器key分配演算法,若選用一致性hash演算法,還需要構建伺服器的一致性hash環。
- (4) 初始化事件處理框架,設定初始事件處理回撥函式
- (5) 啟動事件處理框架
初始化空閒佇列
空閒佇列用來儲存不再使用的資料結構實體,比如:mbuf,msg,conn,這樣就可以複用這些結構體,再次使用時不需要再進行記憶體分配動作,從而提升了效能。
在twemproxy中會初始化三個空閒佇列:mbuf結構體,msg結構體,conn結構體。空閒佇列的初始化程式碼如下:
struct context *
core_start(struct instance *nci)
{
struct context *ctx;
mbuf_init(nci); // 初始化mbuf結構體空閒佇列
msg_init(); //初始化msg結構體空閒佇列
conn_init(); //初始化conn結構體空閒佇列
... ...
}
- 初始化mbuf佇列
初始化mbuf空閒佇列相對簡單,主要是初始化靜態變數:
tatic struct mhdr free_mbufq;
並設定每次建立mbuf結構時的記憶體塊的size大小的變數:mbuf_chunk_size,該變數的預設值是:16k。
void
mbuf_init(struct instance *nci)
{
nfree_mbufq = 0; //設定空閒佇列的mbuf結構體個數
STAILQ_INIT(&free_mbufq); //初始化佇列
mbuf_chunk_size = nci->mbuf_chunk_size; //mbuf的chunk大小
mbuf_offset = mbuf_chunk_size - MBUF_HSIZE; //mbuf的offset大小
... ...
}
free_mbufq初始化後的結構如下:
- 初始化msg空閒佇列
初始化msg空閒佇列主要是初始化靜態變數:free_msgq。
static struct msg_tqh free_msgq;
msg空閒佇列初始化要完成以下事項:
(1) 設定msg佇列的個數變數nfree_msgq為0
static struct msg_tqh free_msgq;
(2) 初始化靜態變數free_msgq,該變數用來管理空閒佇列
(3) 初始化一顆紅黑樹,後面要用該結構來儲存msg的引用
初始化的程式碼如下:
void
msg_init(void)
{
...
msg_id = 0; //初始化msg_id,這是msg的計數器
frag_id = 0; //初始化frag_id,這是fragment計數器
nfree_msgq = 0; //初始化msg的個數為0
TAILQ_INIT(&free_msgq); //初始化空閒msg佇列
rbtree_init(&tmo_rbt, &tmo_rbs); //初始化超時紅黑樹
}
- 初始化conn空閒佇列
初始化conn空閒佇列很簡單,主要是初始化空閒conn佇列的個數,並初始化free_connq佇列。
static struct conn_tqh free_connq;
初始化程式碼如下:
void
conn_init(void)
{
... ...
nfree_connq = 0;
TAILQ_INIT(&free_connq); //初始化conn佇列
}
初始化伺服器池
若設定了preconnect引數為true,則會預先和後端伺服器池中的每個伺服器建立好連線。若按上面的伺服器池配置,會得到如下的連線圖:
上圖只是一個示意圖,實際上在ketama的這個一致性hash環中,還會存在多個虛擬的節點,會根據配置的權重來安排虛擬節點的個數,這些虛擬節點對應著圖中的三個實體節點。詳細的ketama演算法的實現,可以閱讀我的這篇文章:twemproxy0.4原理分析-一致性hash演算法實現ketama分析。示意圖如下:
設定事件處理函式
twemproxy0.4使用的事件驅動框架的入口都是相同的,只是不同的事件會呼叫不同的函式來處理。事件處理框架的函式呼叫流程如下:
總結
本文分析了twemproxy0.4的配置檔案和系統的初始化過程。