以后的分析都是基于beanstalk版本1.6版本的源码。
1. 服务实例Server
beanstalk中将一个监听实例抽象成为一个Server, 服务实例是监听指定地址(ip:port)的运行实例。结构如下:

serverstruct Server {
char *port; //端口
char *addr; //地址
char *user; //用户
Wal wal; //binlog文件
Socket sock; //监听socket连接
Heap conns; //连接堆
};
2.epoll抽象
beanstalk对epoll相关的函数命名很奇怪,封装为如下

epoll处理void sockinit(Handle tick, void *x, int64 ns); //初始化epoll
int sockwant(Socket *s, int rw); //在epoll中增加句柄
epoll在srvserver中初始化,并将监听socket加入epoll

srvservevoid
srvserve(Server *s)
{
int r;
//创建epoll服务
sockinit((Handle)srvtick, s, 10*1000000); // 10ms
s->sock.x = s;
s->sock.f = (Handle)srvaccept;
s->conns.less = (Less)connless;
s->conns.rec = (Record)connrec;
//对监听服务启动监听
r = listen(s->sock.fd, 1024);
if (r == -1) {
twarn("listen");
return;
}
//将监听服务加入epoll
r = sockwant(&s->sock, 'r');
if (r == -1) {
twarn("sockwant");
exit(2);
}
//主循环函数
sockmain();
twarnx("sockmain");
exit(1);
}
2. beanstalk主循环
beanstalk主循环用作处理epoll事件、时钟事件。对于epoll事件,每次只处理一个事件。

主循环void
sockmain()
{
int i, r, n = 1;
int64 e, t = nanoseconds();
struct epoll_event evs[n];
for (;;) {
r = epoll_wait(epfd, evs, n, ival); //ival在sockinit中设置为10ms
if (r == -1 && errno != EINTR) {
twarn("epoll_wait");
exit(1);
}
// should tick?
e = nanoseconds();
if ((e-t) / 1000000 > ival) {
//当前时间e比上次定时处理时间t超过ival时,定时器调用协议处理,srvtick
tick(tickval, 0);
t = e;
}
for (i=0; i<r; i++) {
//处理epoll事件
handle(evs[i].data.ptr, evs[i].events);
}
}
}