三、Twemproxy关键数据结构分析
1: array. 位于:nc_array.h
Twemproxy自己实现了一个数组结构以及一系列对这个结构进行操作的函数,array结构定义如下:
struct array { uint32_t nelem; /* # element 当前数组的位置索引*/ void *elem; /* element 元素指针,指向array的首地址*/ size_t size; /* element size 每一个元素的大小 */ uint32_t nalloc; /* # allocated element 数组可以分配的元素 */ };
其中需要注意的是size这个元素,它并不是指当前array的大小,而是指每一个elem的大小。这是一个非常重要的数据结构,后边在对配置文件做初始话的时候都需要用到它。
2: nc_conf.h中定义的一系列数据结构。
当我最开始看twemproxy的时候,对于它配置文件以及相应程序中对应数据结构的定义感觉很不好理解。因为twemproxy使用的是纯文本的配置,然后使用yaml对配置文件进行解析。
a:conf_listen 位于: nc_conf.h
struct conf_listen { struct string pname; /* listen: as "name:port" */ struct string name; /* name */ int port; /* port */ struct sockinfo info; /* listen socket info */ unsigned valid:1; /* valid? */ };
配置文件一个节点配置的twemproxy实例监听的port以及host的抽象
b:conf_server 位于:nc_conf.h
struct conf_server { struct string pname; /* server: as "name:port:weight" */ struct string name; /* name */ int port; /* port */ int weight; /* weight */ struct sockinfo info; /* connect socket info */ unsigned valid:1; /* valid? */ };
配置文件中一个节点配置的servers的抽象,即后端服务的host,port,weight
c:conf_pool 位于nc_conf.h
struct conf_pool { struct string name; /* pool name (root node) */ struct conf_listen listen; /* listen: */ hash_type_t hash; /* hash: */ struct string hash_tag; /* hash_tag: */ dist_type_t distribution; /* distribution: */ int timeout; /* timeout: */ int backlog; /* backlog: */ int client_connections; /* client_connections: */ int redis; /* redis: */ int preconnect; /* preconnect: */ int auto_eject_hosts; /* auto_eject_hosts: */ int server_connections; /* server_connections: */ int server_retry_timeout; /* server_retry_timeout: in msec */ int server_failure_limit; /* server_failure_limit: */ struct array server; /* servers: conf_server[] */ unsigned valid:1; /* valid? */ };
conf_pool即配置文件中一个节点的抽象
d:conf 位于 nc_conf.h
struct conf { char *fname; /* file name (ref in argv[]) */ FILE *fh; /* file handle */ struct array arg; /* string[] (parsed {key, value} pairs) */ struct array pool; /* conf_pool[] (parsed pools) */ uint32_t depth; /* parsed tree depth */ yaml_parser_t parser; /* yaml parser */ yaml_event_t event; /* yaml event */ yaml_token_t token; /* yaml token */ unsigned seq:1; /* sequence? */ unsigned valid_parser:1; /* valid parser? */ unsigned valid_event:1; /* valid event? */ unsigned valid_token:1; /* valid token? */ unsigned sound:1; /* sound? */ unsigned parsed:1; /* parsed? */ unsigned valid:1; /* valid? */ };
conf即配置文件的抽象。其中:
sound: 用于标识配置文件的格式是否正确。
parsed: 用于标识配置文件是否已经解析过了。
3:nc_conn.h中定义的数据结构
struct conn { TAILQ_ENTRY(conn) conn_tqe; /* link in server_pool / server / free q */ /* * 标识一个连接的拥有者,对于proxy和client来说是struct seerver_pool,对于server来说,是struct server */ void *owner; /* connection owner - server_pool / server */ int sd; /* socket descriptor */ int family; /* socket address family */ socklen_t addrlen; /* socket length */ struct sockaddr *addr; /* socket address (ref in server or server_pool) */ struct msg_tqh imsg_q; /* incoming request Q 进来的请求 */ struct msg_tqh omsg_q; /* outstanding request Q 未完成的请求 */ struct msg *rmsg; /* current message being rcvd */ struct msg *smsg; /* current message being sent */ conn_recv_t recv; /* recv (read) handler sd上读事件发生时的回调函数*/ conn_recv_next_t recv_next; /* recv next message handler */ conn_recv_done_t recv_done; /* read done handler */ conn_send_t send; /* send (write) handler sd上写事件发生时的回调函数*/ conn_send_next_t send_next; /* write next message handler */ conn_send_done_t send_done; /* write done handler */ conn_close_t close; /* close handler */ conn_active_t active; /* active? handler */ conn_ref_t ref; /* connection reference handler */ conn_unref_t unref; /* connection unreference handler */ conn_msgq_t enqueue_inq; /* connection inq msg enqueue handler */ conn_msgq_t dequeue_inq; /* connection inq msg dequeue handler */ conn_msgq_t enqueue_outq; /* connection outq msg enqueue handler */ conn_msgq_t dequeue_outq; /* connection outq msg dequeue handler */ size_t recv_bytes; /* received (read) bytes */ size_t send_bytes; /* sent (written) bytes */ uint32_t events; /* connection io events */ err_t err; /* connection errno */ unsigned recv_active:1; /* recv active? */ unsigned recv_ready:1; /* recv ready? */ unsigned send_active:1; /* send active? */ unsigned send_ready:1; /* send ready? */ unsigned client:1; /* client? or server? */ unsigned proxy:1; /* proxy? */ unsigned connecting:1; /* connecting? */ unsigned connected:1; /* connected? */ unsigned eof:1; /* eof? aka passive close? */ unsigned done:1; /* done? aka close? */ unsigned redis:1; /* redis? */ };
conn是twemproxy一个非常重要的结构,客户端到twemproxy的连接以及twemproxy到后端server的连接,以及proxy本身监听的tcp端口都可以抽象为一个conn。同时对于一个conn来说,也有不同的种类,例如:客户端到proxy的连接conn就属于一个”client”, proxy到后端server的连接就属于”server”, 而proxy本身监听的端口所在的tcp套接字,就属于“proxy”。
client:这个元素标识这个conn是一个client还是一个server。
proxy:这个元素标识这个conn是否是一个proxy。
owner:这个元素标识这个conn的属主。
纵观twemproxy, 他里边的conn有三种,client, server, proxy。当这个conn 是一个proxy或者client时,则它此时的owner就是server_pool; 而当这 个conn是一个server时,则它此时的owner就是server
sd: 这个conn包含的socket描述符。
conn还包含有很多函数指针,都是在对应事件发生时调用的回调函数,这些在具体分析请求处理的时候在做说明。
4: nc_server.h中定义的一系列数据结构
a:
struct server_pool { uint32_t idx; /* pool index */ struct context *ctx; /* owner context */ struct conn *p_conn; /* proxy connection (listener) */ uint32_t nc_conn_q; /* # client connection */ struct conn_tqh c_conn_q; /* client connection q */ struct array server; /* server[] */ uint32_t ncontinuum; /* # continuum points */ uint32_t nserver_continuum; /* # servers - live and dead on continuum (const) */ struct continuum *continuum; /* continuum */ uint32_t nlive_server; /* # live server */ int64_t next_rebuild; /* next distribution rebuild time in usec */ struct string name; /* pool name (ref in conf_pool) */ struct string addrstr; /* pool address (ref in conf_pool) */ uint16_t port; /* port */ int family; /* socket family */ socklen_t addrlen; /* socket length */ struct sockaddr *addr; /* socket address (ref in conf_pool) */ int dist_type; /* distribution type (dist_type_t) */ int key_hash_type; /* key hash type (hash_type_t) */ hash_t key_hash; /* key hasher */ struct string hash_tag; /* key hash tag (ref in conf_pool) */ int timeout; /* timeout in msec */ int backlog; /* listen backlog */ uint32_t client_connections; /* maximum # client connection */ uint32_t server_connections; /* maximum # server connection */ int64_t server_retry_timeout; /* server retry timeout in usec */ uint32_t server_failure_limit; /* server failure limit */ unsigned auto_eject_hosts:1; /* auto_eject_hosts? */ unsigned preconnect:1; /* preconnect? */ unsigned redis:1; /* redis? */ };
server_pool: 配置文件中每一个节点配置的servers的一个抽象。
redis : 标识是否是redis的代理
dist_type: 数据分片类型
b:
struct server { uint32_t idx; /* server index */ struct server_pool *owner; /* owner pool */ struct string pname; /* name:port:weight (ref in conf_server) */ struct string name; /* name (ref in conf_server) */ uint16_t port; /* port */ uint32_t weight; /* weight */ int family; /* socket family */ socklen_t addrlen; /* socket length */ struct sockaddr *addr; /* socket address (ref in conf_server) */ uint32_t ns_conn_q; /* # server connection */ struct conn_tqh s_conn_q; /* server connection q */ int64_t next_retry; /* next retry time in usec */ uint32_t failure_count; /* # consecutive failures */ };
server即配置文件中一个节点配置的servers中一个server的抽象。其中:
owner:标识这个server属于哪个server_pool
ns_conn_q:标识到这个后端server的连接数
s_conn_q:标识到这个server所有的conn(conn是twemproxy中很关键的一个数据结构,标识一个连接,下边介绍)。注意这个属性维持的是一个队列(队列使用的是Freebsd中提供的一个队列实现:queue.h;libevent也使用这个队列实现)。