Twemproxy 緩存代理服務器
Twemproxy 概述
Twemproxy(又稱為nutcracker)是一個輕量級的Redis和Memcached代理,主要用來減少對后端緩存服務器的連接數。Twemproxy是由Twitter開源出來的緩存服務器集群管理工具,主要用來彌補Redis/Memcached 對集群(cluster)管理的不足。
antirez(Redis作者)寫過一篇對twemproxy的介紹,他認為twemproxy是目前Redis 分片管理的最好方案,雖然antirez的Redis cluster正在實現並且對其給予厚望,但從現有的cluster實現上還是認為cluster除了增加Redis復雜度,對於集群的管理沒有twemproxy來的輕量和有效。
談到集群管理不得不又說到數據的分片管理(shard),為了滿足數據的日益增長和擴展性,數據存儲系統一般都需要進行一定的分片,如傳統的MySQL進行橫向分表和縱向分表,然后應用程序訪問正確的位置就需要找的正確的表。這時候,這個數據定向工作一般有三個位置可以放:
- 數據存儲系統本身支持,Redis Cluster就是典型的試圖在數據存儲系統上支持分片;
- 客戶端支持,Memcached的客戶端對分片的支持就是客戶端層面的;
- 代理支持,twemproxy就是試圖在服務器端和客戶端中間建代理支持;
在三種方案中:
1、客戶端方案我認為欠妥,因為這樣每個客戶端需要維護一定的服務器信息,但是如果動態的增加或減少節點就需要重寫配置各個客戶端。
2、而在服務器端增加集群管理有利於使用者,減少使用者需要了解的東西,整合集群管理使得性能比其他方案都要更高,但是缺點是其會嚴重增加代碼復雜度,導致服務器端代碼爆炸。
3、而采用中間層代理的方式我認為是最優雅和有效的,在不改動服務器端程序的情況下,twemproxy使得集群管理更簡單,去除不支持的操作和合並,同時更可以支持多個后端服務,大大減少連接數等等,但是缺點也是顯而易見的,它不能更有效的利用集群的優勢,如多鍵運算和范圍查找操作等等,這都是需要服務器端程序本身支持。
Twemproxy 安裝
從源碼安裝:
git clone git@github.com:twitter/twemproxy.git cd twemproxy autoreconf -fvi ./configure --enable-debug=full make src/nutcracker -h
twemproxy命令行選項:
Usage: nutcracker [-?hVdDt] [-v verbosity level] [-o output file] [-c conf file] [-s stats port] [-a stats addr] [-i stats interval] [-p pid file] [-m mbuf size]
-h, –help : 查看幫助文檔,顯示命令選項
-V, –version : 查看nutcracker版本
-t, –test-conf : 測試配置腳本的正確性
-d, –daemonize : 以守護進程運行
-D, –describe-stats : 打印狀態描述
-v, –verbosity=N : 設置日志級別 (default: 5, min: 0, max: 11)
-o, –output=S : 設置日志輸出路徑,默認為標准錯誤輸出 (default: stderr)
-c, –conf-file=S : 指定配置文件路徑 (default: conf/nutcracker.yml)
-s, –stats-port=N : 設置狀態監控端口,默認22222 (default: 22222)
-a, –stats-addr=S : 設置狀態監控IP,默認0.0.0.0 (default: 0.0.0.0)
-i, –stats-interval=N : 設置狀態聚合間隔 (default: 30000 msec)
-p, –pid-file=S : 指定進程pid文件路徑,默認關閉 (default: off)
-m, –mbuf-size=N : 設置mbuf塊大小,默認64K,含義見下面的零拷貝;
零拷貝(Zero Copy)
在twemproxy中,請求和響應都是分配到一塊叫mbuf的內存中的,接收請求的mbuf同時會用於轉發到backend,類似地,從backend接收響應的mbuf同時也會用於轉發到client,這樣做就避免了內存拷貝。
此外,mbuf使用內存池,一旦分配就不再釋放,當一個請求結束時,它所使用的mbuf會放回內存池。一個mbuf占16K,這個大小需要在I/O性能和連接並發數之間做取舍,mbuf尺寸越大,對socket的讀寫系統調用次數越少,但整個系統可支持的並發數也越小。如果希望支持更高的client並發請求數,可以把mbuf的尺寸設置小一點(通過-m選項)。
Twemproxy 配置
Twemproxy 通過YAML文件配置,例如:
alpha:
listen: 127.0.0.1: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
beta:
listen: 127.0.0.1:22122
hash: fnv1a_64
hash_tag: "{}"
distribution: ketama
auto_eject_hosts: false
timeout: 400
redis: true
servers:
- 127.0.0.1:6380:1 server1
- 127.0.0.1:6381:1 server2
- 127.0.0.1:6382:1 server3
- 127.0.0.1:6383:1 server4
gamma:
listen: 127.0.0.1:22123
hash: fnv1a_64
distribution: ketama
timeout: 400
backlog: 1024
preconnect: true
auto_eject_hosts: true
server_retry_timeout: 2000
server_failure_limit: 3
servers:
- 127.0.0.1:11212:1
- 127.0.0.1:11213:1
delta:
listen: 127.0.0.1:22124
hash: fnv1a_64
distribution: ketama
timeout: 100
auto_eject_hosts: true
server_retry_timeout: 2000
server_failure_limit: 1
servers:
- 127.0.0.1:11214:1
- 127.0.0.1:11215:1
- 127.0.0.1:11216:1
- 127.0.0.1:11217:1
- 127.0.0.1:11218:1
- 127.0.0.1:11219:1
- 127.0.0.1:11220:1
- 127.0.0.1:11221:1
- 127.0.0.1:11222:1
- 127.0.0.1:11223:1
omega:
listen: /tmp/gamma
hash: hsieh
distribution: ketama
auto_eject_hosts: false
servers:
- 127.0.0.1:11214:100000
- 127.0.0.1:11215:1
說明:
listen
twemproxy監聽地址,支持UNIX域套接字。
hash
可以選擇的key值的hash算法:
- one_at_a_time
- md5
- crc16
- crc32 (crc32 implementation compatible with libmemcached)
- crc32a (correct crc32 implementation as per the spec)
- fnv1_64
- fnv1a_64,默認選項
- fnv1_32
- fnv1a_32
- hsieh
- murmur
- jenkins
hash_tag
hash_tag允許根據key的一個部分來計算key的hash值。hash_tag由兩個字符組成,一個是hash_tag的開始,另外一個是hash_tag的結束,在hash_tag的開始和結束之間,是將用於計算key的hash值的部分,計算的結果會用於選擇服務器。
例如:如果hash_tag被定義為”{}”,那么key值為"user:{user1}:ids"和"user:{user1}:tweets"的hash值都是基於”user1”,最終會被映射到相同的服務器。而"user:user1:ids"將會使用整個key來計算hash,可能會被映射到不同的服務器。
distribution
存在ketama、modula和random3種可選的配置。其含義如下:
- ketama,一致性hash算法,會根據服務器構造出一個hash ring,並為ring上的節點分配hash范圍。ketama的優勢在於單個節點添加、刪除之后,會最大程度上保持整個群集中緩存的key值可以被重用。
- modula,根據key值的hash值取模,根據取模的結果選擇對應的服務器;
- random,無論key值的hash是什么,都隨機的選擇一個服務器作為key值操作的目標;
timeout
單位是毫秒,是連接到server的超時值。默認是永久等待。
backlog
監聽TCP 的backlog(連接等待隊列)的長度,默認是512。
preconnect
是一個boolean值,指示twemproxy是否應該預連接pool中的server。默認是false。
redis
是一個boolean值,用來識別到服務器的通訊協議是redis還是memcached。默認是false。
server_connections
每個server可以被打開的連接數。默認,每個服務器開一個連接。
auto_eject_hosts
是一個boolean值,用於控制twemproxy是否應該根據server的連接狀態重建群集。這個連接狀態是由server_failure_limit閥值來控制。
默認是false。
server_retry_timeout
單位是毫秒,控制服務器連接的時間間隔,在auto_eject_host被設置為true的時候產生作用。默認是30000 毫秒。
server_failure_limit
控制連接服務器的次數,在auto_eject_host被設置為true的時候產生作用。默認是2。
servers
一個pool中的服務器的地址、端口和權重的列表,包括一個可選的服務器的名字,如果提供服務器的名字,將會使用它決定server的次序,從而提供對應的一致性hash的hash ring。否則,將使用server被定義的次序。
Twemproxy 監控
Twemproxy 監控端口默認為22222,並每隔30s收集一次信息。
nutcracker --describe-stats
報告的信息如下:
pool stats:
client_eof "# eof on client connections"
client_err "# errors on client connections"
client_connections "# active client connections"
server_ejects "# times backend server was ejected"
forward_error "# times we encountered a forwarding error"
fragments "# fragments created from a multi-vector request"
server stats:
server_eof "# eof on server connections"
server_err "# errors on server connections"
server_timedout "# timeouts on server connections"
server_connections "# active server connections"
requests "# requests"
request_bytes "total request bytes"
responses "# responses"
response_bytes "total response bytes"
in_queue "# requests in incoming queue"
in_queue_bytes "current request bytes in incoming queue"
out_queue "# requests in outgoing queue"
out_queue_bytes "current request bytes in outgoing queue"
Pipelining
Twemproxy 可以同時接收很多client端的請求,並僅通過一個或幾個連接回源,這種結構很適合使用流水線處理請求和響應,從而節省TCP往返時間。
例如,Twemproxy 正在同時代理3個client端的請求,分別是:'get key\r\n'、'set key 0 0 3\r\nval\r\n'和'delete key\r\n' ',Twemproxy 可以將這3個請求打包成一個消息發送給后端的redis: 'get key\r\nset key 0 0 3\r\nval\r\ndelete key\r\n'。
pipelining也是Twemproxy高性能的原因之一。