定时器设计(一)
前言
客户端与服务器通过socket进行连接和通信,但是socket有限的,非活跃连接占用了连接资源,对服务器的性能影响很大,通过设计服务器定时器来处理非活跃连接,释放连接资源。
一、定时器设计
定时器的设计包括基于小根堆的定时器、基于排序链表的定时器、基于时间轮的定时器。本文主要介绍基于排序链表的定时器和基于时间轮的定时器。
定时器是将所需的定时事件封装起来,本文主要是将检测非活跃连接封装成一个定时器。
定时器回调函数:从内核事件表删除事件,关闭文件描述符,释放连接资源。
void cb_func(client_data *user_data)
{
//删除非活动连接在socket上的注册事件
epoll_ctl(Utils::u_epollfd, EPOLL_CTL_DEL, user_data->sockfd, 0);
assert(user_data);
//关闭文件描述符
close(user_data->sockfd);
//减少连接数
http_conn::m_user_count--;
}
连接资源主要包括 客户端套接字地址、文件描述符、定时器。
class util_timer;
struct client_data
{
sockaddr_in address;
int sockfd;
util_timer *timer;
};
1、基于排序链表定时器设计
基于排序链表的类包括超时时间、回调函数、连接资源、前向定时器、后继定时器。
class util_timer
{
public:
util_timer() : prev(NULL), next(NULL) {
}
public:
time_t expire;
void (* cb_func)(client_data *);
client_data *user_data;
util_timer *prev;
util_timer *next;
};
2、基于时间轮定时器设计
基于时间轮的定时器类包括时间轮圈数、时间轮的槽、回调函数、连接资源、前向定时器、后继定时器。
class util_timer
{
public:
util_timer(int rot ,int ts) : prev(NULL), next(NULL),rotation(rot),time_slot(ts) {
}
public:
// 时间轮圈数
int rotation;
// 时间轮的槽
int time_slot;
//回调函数:从内核事件表删除事件,关闭文件描述符,释放连接资源
void (* cb_func)(client_data *);
//连接资源
client_data *user_data;
//前向定时器
util_timer *prev;
//后继定时器
util_timer *next;
};
二、定时器容器设计
1、基于排序链表的定时器设计
基于排序链表定时器设计,是利用alarm函数周期触发SIGALRM信号,信号处理函数通过管道通知主循环对排序链表的定时器进行处理。
定时器容器为升序双向链表,按照超时时间升序排列,根据触发信号定时将到期的定时信号从链表中删除。
升序双向链表的主要成员函数如下:
class sort_timer_lst
{
public:
sort_timer_lst();
~sort_timer_lst();
void add_timer(util_timer *timer);
void adjust_timer(util_timer *timer);
void del_timer(util_timer *timer);
void tick();
private:
void add_timer(util_timer *timer, util_timer *lst_head);
util_timer *head;
util_timer *tail;
};
1.add_timer函数,将定时器添加到链表中,如果当前链表中没有其他定时器那么直接插入链表,否则将定时器根据超时时间升序插入。
void add_timer(util_timer *timer)
{
if (!timer)
{
return;
}
if (!head)
{
head = tail = timer;
return;
}
if (timer->expire < head->expire)