高性能离不开异步,异步离不开队列,而其内部都是Producer-Comsumer模式的原理。
Beanstalkd设计里面的核心概念:
一个需要异步处理的任务,是Beanstalkd中的基本单元,需要放在一个tube中。
一个有名的任务队列,用来存储统一类型的job,是producer和consumer操作的对象。
Job的生产者,通过put命令来将一个job放到一个tube中。
Job的消费者,通过reserve/release/bury/delete命令来获取job或改变job的状态。
Beanstalkd中一个job的生命周期如图2所示。一个job有READY, RESERVED, DELAYED, BURIED四种状态。当producer直接put一个job时,job就处于READY状态,等待consumer来处理,如果选择延迟put,job就先到DELAYED状态,等待时间过后才迁移到READY状态。consumer获取了当前READY的job后,该job的状态就迁移到RESERVED,这样其他的consumer就不能再操作该job。当consumer完成该job后,可以选择delete, release或者bury操作;delete之后,job从系统消亡,之后不能再获取;release操作可以重新把该job状态迁移回READY(也可以延迟该状态迁移操作),使其他的consumer可以继续获取和执行该job;有意思的是bury操作,可以把该job休眠,等到需要的时候,再将休眠的job kick回READY状态,也可以delete BURIED状态的job。正是有这些有趣的操作和状态,才可以基于此做出很多意思的应用,比如要实现一个循环队列,就可以将RESERVED状态的job休眠掉,等没有READY状态的job时再将BURIED状态的job一次性kick回READY状态。
下面我把项目中设计到的说有数据结构写出来。
一个job就是一个工作单位
struct job { Jobrec r; // persistent fields; these get written to the wal /* bookeeping fields; these are in-memory only */ char pad[6]; tube tube; job prev, next; /* linked list of jobs */ job ht_next; /* Next job in a hash table list */ size_t heap_index; /* where is this job in its current heap */ File *file; job fnext; job fprev; void *reserver; int walresv; int walused; char body[]; // written separately to the wal };
涉及到Jobrec,他的左右就是记录每个job的各种属性值
struct Jobrec { uint64 id; uint32 pri; int64 delay; int64 ttr; int32 body_size; int64 created_at; int64 deadline_at; uint32 reserve_ct; uint32 timeout_ct; uint32 release_ct; uint32 bury_ct; uint32 kick_ct; byte state; };
每个job设计到file处理
struct File { File *next; uint refs; int seq; int iswopen; // is open for writing int fd; int free; int resv; char *path; Wal *w; struct job jlist; // jobs written in this file };
file文件的一个链表
struct Wal { int filesize; int use; char *dir; File *head; File *cur; File *tail; int nfile; int next; int resv; // bytes reserved int alive; // bytes in use int64 nmig; // migrations int64 nrec; // records written ever int wantsync; int64 syncrate; int64 lastsync; int nocomp; // disable binlog compaction? };
消息的一个队列 所有的job将放入这里进行处理
struct tube { uint refs; char name[MAX_TUBE_NAME_LEN]; Heap ready; Heap delay; struct ms waiting; /* set of conns */ struct stats stat; uint using_ct; uint watching_ct; int64 pause; int64 deadline_at; struct job buried; };
这个结构体,应该是一个连接数,支持9.5million的书连接连接
struct Heap { int cap; int len; void **data; Less less; Record rec; };
下面就是涉及到了网络的一些东西 server设置服务器的东西。每次启动就会初始化wall文件链表
struct Server { char *port; char *addr; char *user; Wal wal; Socket sock; Heap conns; };
下面是socket需要的一些参数 初始的一些参数
struct Socket { int fd; Handle f; void *x; int added; };
要注意的是HANDLE 回调函数
typedef void(*Handle)(void*, int rw); // rw can also be 'h' for hangup
这个结构体主要是网络处理job
struct Conn { Server *srv; Socket sock; char state; char type; Conn *next; tube use; int64 tickat; // time at which to do more work int tickpos; // position in srv->conns job soonest_job; // memoization of the soonest job int rw; // currently want: 'r', 'w', or 'h' int pending_timeout; char cmd[LINE_BUF_SIZE]; // this string is NOT NUL-terminated int cmd_len; int cmd_read; char *reply; int reply_len; int reply_sent; char reply_buf[LINE_BUF_SIZE]; // this string IS NUL-terminated // How many bytes of in_job->body have been read so far. If in_job is NULL // while in_job_read is nonzero, we are in bit bucket mode and // in_job_read's meaning is inverted -- then it counts the bytes that // remain to be thrown away. int in_job_read; job in_job; // a job to be read from the client job out_job; int out_job_sent; struct ms watch; struct job reserved_jobs; // linked list header };
//
struct ms { size_t used, cap, last; void **items; ms_event_fn oninsert, onremove; }; enum { Walver = 7 }; /////job的六种状态。
enum // Jobrec.state { Invalid, Ready, Reserved, Buried, Delayed, Copy };
还有涉及到状态设置
struct stats { uint urgent_ct; uint waiting_ct; uint buried_ct; uint reserved_ct; uint pause_ct; uint64 total_delete_ct; uint64 total_jobs_ct; };
主要是涉及到了以上的数据结构,来进行数据处理。
更多代码分析,慢慢将会介绍
更多文章,欢迎访问:http://blog.csdn.net/wallwind