- /*
-
* We keep the current time of day in a global variable that's updated by a
-
* timer event. This saves us a bunch of time() system calls (we really only
-
* need to get the time once a second, whereas there can be tens of thousands
-
* of requests a second) and allows us to use server-start-relative timestamps
-
* rather than absolute UNIX timestamps, a space savings on systems where
-
* sizeof(time_t) > sizeof(unsigned int).
-
*/
-
#define reltime_t unsigned int
-
volatile rel_time_t current_time;
-
static struct event clockevent;
-
-
/*
- * time-sensitive callers can call it by hand with this,
- * outside the normal ever-1-second timer
-
*/
-
static void set_current_time(void) {
-
struct timeval timer;
-
-
gettimeofday(&timer, NULL);
-
current_time = (rel_time_t) (timer.tv_sec - stats.started);
-
}
-
-
static void clock_handler(const int fd, const short which, void *arg) {
-
struct timeval t = {.tv_sec = 1, .tv_usec = 0};
-
static bool initialized = false;
-
-
if (initialized) {
-
/* only delete the event if it's actually there. */
-
evtimer_del(&clockevent);
-
} else {
-
initialized = true;
-
}
-
-
evtimer_set(&clockevent, clock_handler, 0);
-
event_base_set(main_base, &clockevent);
-
evtimer_add(&clockevent, &t);
-
-
set_current_time();
- }
-
在memcached.c文件中main.c中有如下代码:
-
/* initialize main thread libevent instance */
-
main_base = event_init();
-
-
/* start up worker threads if MT mode */
- thread_init(settings.num_threads, main_base);
-
/* initialise clock event */
- clock_handler(0, 0, 0);
- /* enter the event loop */
event_base_loop(main_base, 0);
我们保存当前的时间在一个全局变量中volatile rel_time_t current_time,通过timer event即libevent中的“定时触发器”功能对其进行每1秒update一次。从而是我们节省了了许多的对time()系统函数的调用(我们其实只是需要每秒钟获得一次时间,但是如果我们不用timer event的话,会导致高并发时在1秒钟会有几万次的time()调用,利用了timer event之后,我们每秒只是调用了一次,其它需要获得当前时间的函数直接从全局变量current_time中
直接读取)。同时我们没有使用UNIX timestamps,而是使用了当前时间与系统启动时间的相对值,来表示时间的概念,这样也给我们节省了空间,因为有些系统中:
sizeof(time_t) > sizeof(unsigned int).
********************************************************************************************
在memcached.c中另一处也用到了libevent中的timer event的功能:
- /** file scope variables **/
-
-
static item **todelete = NULL;
-
static int delcurr;
-
static int deltotal;
-
-
# define run_deferred_deletes() do_run_deferred_deletes()
-
-
static void delete_handler(const int fd, const short which, void *arg) {
-
struct timeval t = {.tv_sec = 5, .tv_usec = 0};
-
static bool initialized = false;
-
-
if (initialized) {
- /* some versions of libevent don't like deleting events that don't
- * exist, so only delete once we know this event has been added.
- */
-
evtimer_del(&deleteevent);
-
} else {
-
initialized = true;
-
}
-
evtimer_set(&deleteevent, delete_handler, 0);
-
event_base_set(main_base, &deleteevent);
-
evtimer_add(&deleteevent, &t);
-
run_deferred_deletes();
-
}
-
-
/* Call run_deferred_deletes instead of this. */
-
void do_run_deferred_deletes(void)
-
{
-
int i, j = 0;
-
-
for (i = 0; i < delcurr; i++) {
-
item *it = todelete[i];
-
if (item_delete_lock_over(it)) {
-
assert(it->refcount > 0);
-
it->it_flags &= ~ITEM_DELETED;
-
do_item_unlink(it);
-
do_item_remove(it);
-
} else {
-
todelete[j++] = it;
-
}
-
}
-
delcurr = j;
-
}
-
-
/*
-
* Adds an item to the deferred-delete list so it can be reaped later.
-
* Returns the result to send to the client.
-
*/
-
char *do_defer_delete(item *it, time_t exptime)
-
{
-
if (delcurr >= deltotal) {
-
item **new_delete = realloc(todelete, sizeof(item *) * deltotal * 2);
-
if (new_delete) {
-
todelete = new_delete;
-
deltotal *= 2;
-
} else {
-
/*
-
* can't delete it immediately, user wants a delay,
-
* but we ran out of memory for the delete queue
-
*/
-
item_remove(it); /* release reference */
-
return "SERVER_ERROR out of memory expanding delete queue";
-
}
-
}
-
-
/* use its expiration time as its deletion time now */
-
it->exptime = realtime(exptime);
-
it->it_flags |= ITEM_DELETED;
-
todelete[delcurr++] = it;
-
-
return "DELETED";
-
}
-
-
在main函数中有如下代码:
-
-
/* initialise deletion array and timer event */
-
deltotal = 200;
-
delcurr = 0;
-
-
if ((todelete = malloc(sizeof(item *) * deltotal)) == NULL) {
-
perror("failed to allocate memory for deletion array");
-
exit(EXIT_FAILURE);
-
}
- delete_handler(0, 0, 0); /* sets up the event */
每隔5秒钟触发一次delete_handler从而实现每5秒中调用一次run_deferred_deletes();也就是每5秒钟遍历一次可以删除的item的链表,先判断它们的过期时间是否到了:
- /*
- * returns true if a deleted item's delete-locked-time is over, and it
- * should be removed from the namespace
- */
-
bool item_delete_lock_over (item *it) {
-
assert(it->it_flags & ITEM_DELETED);
-
return (current_time >= it->exptime);
- }
然后标记删除标记:it->it_flags &= ~ITEM_DELETED;
最后删除:do_item_unlink(it);do_item_remove(it);。
item的删除链表 todelete 最初的大小为 deltotal = 200; 当空间不够时,再将加倍:
if (delcurr >= deltotal) {
item **new_delete = realloc(todelete, sizeof(item *) * deltotal * 2);
另外:
char *do_defer_delete(item *it, time_t exptime)实现的功能是:
客户端请求服务器端将it这个item的可以在将来被删除的时间设置为exptime,比如客户端请求服务器端将it在未来的7天之后删除掉。则服务端就会先调用该函数来设置it可以被删除的时间。但是exptime是客户端传来的时间,我们服务器端不能确定exptime的值是7天之后对应的的真正的UNIX time,还是7天对应的time_t:7 * 60 * 60 * 24,所以我们这里调用了realtime这个函数来分情况来得到真正的UNIX时间处理:it->exptime = realtime(exptime);
realtime函数的定义如下:
- #define REALTIME_MAXDELTA 60*60*24*30
- /*
- * given time value that's either unix time or delta from current unix time,
- * return unix time. Use the fact that delta can't exceed one month (and real
- * time value can't be that low).
- */
-
static rel_time_t realtime(const time_t exptime) {
-
/* no. of seconds in 30 days - largest possible delta exptime */
-
if (exptime == 0) return 0; /* 0 means never expire */
-
if (exptime > REALTIME_MAXDELTA) {
-
/* if item expiration is at/before the server started, give it an
-
* expiration time of 1 second after the server started.
-
* (because 0 means don't expire). without this, we'd
-
* underflow and wrap around to some large value way in the
-
* future, effectively making items expiring in the past
-
* really expiring never
- */
-
if (exptime <= stats.started)
-
return (rel_time_t)1;
-
return (rel_time_t)(exptime - stats.started);
-
} else {
-
return (rel_time_t)(exptime + current_time);
-
}
- }
给定一个时间,不论它是真正的UNIX time,还是相对于当前的相对时间,返回真正的UNIX time相对于memcached启动的值,也就是真正的UNIX time减去memcached启动时的UNIX time。源代码中的注释有误!判断参数exptime是不是真正的UNIX time用到了一个基本事实:
UNIX time是指1970-01-01 00:00:00到当前的时间差的秒数,肯定大于REALTIME_MAXDELTA
并且memcached的协议也规定:在memcached中存储的object的存在的时间不能超过30天。所以客户端不可能请求服务器端在30天或40天之后才删除某个item对象。
第二段注释的意思是:
如果参数exptime的值早于memcached服务器启动的时间,那么我们就将它的设置为memcached启动之后的1秒(因为0表示不过期)。如果不这样做的话,那么因为
(exptime - stats.started)是一个负数,将它强制转换成rel_time_t(rel_time_t的真正类型是unsigned int),这将导致溢出并回卷而变成一个极大的正数,将导致该item在实际效果上是等同于永远不过期。
对item其它相关函数的分析参见随后的blog.