Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1742089
  • 博文数量: 782
  • 博客积分: 2455
  • 博客等级: 大尉
  • 技术积分: 4140
  • 用 户 组: 普通用户
  • 注册时间: 2011-04-06 21:37
个人简介

Linux ,c/c++, web,前端,php,js

文章分类

全部博文(782)

文章存档

2015年(8)

2014年(28)

2013年(110)

2012年(307)

2011年(329)

分类:

2011-08-21 16:45:59

在memcached-1.2.8的memcached.c中有如下代码使用libevent中的timer event:

  1. /*
  2.  * We keep the current time of day in a global variable that's updated by a
  3.  * timer event. This saves us a bunch of time() system calls (we really only
  4.  * need to get the time once a second, whereas there can be tens of thousands
  5.  * of requests a second) and allows us to use server-start-relative timestamps
  6.  * rather than absolute UNIX timestamps, a space savings on systems where
  7.  * sizeof(time_t) > sizeof(unsigned int).
  8.  */
  9. #define reltime_t unsigned int
  10. volatile rel_time_t current_time;
  11. static struct event clockevent;

  12. /*
  13.  * time-sensitive callers can call it by hand with this,
  14.  * outside the normal ever-1-second timer
  15.  */
  16. static void set_current_time(void) {
  17.     struct timeval timer;

  18.     gettimeofday(&timer, NULL);
  19.     current_time = (rel_time_t) (timer.tv_sec - stats.started);
  20. }

  21. static void clock_handler(const int fd, const short which, void *arg) {
  22.     struct timeval t = {.tv_sec = 1, .tv_usec = 0};
  23.     static bool initialized = false;

  24.     if (initialized) {
  25.         /* only delete the event if it's actually there. */
  26.         evtimer_del(&clockevent);
  27.     } else {
  28.         initialized = true;
  29.     }

  30.     evtimer_set(&clockevent, clock_handler, 0);
  31.     event_base_set(main_base, &clockevent);
  32.     evtimer_add(&clockevent, &t);

  33.     set_current_time();
  34. }

  35. 在memcached.c文件中main.c中有如下代码:
  36. /* initialize main thread libevent instance */
  37. main_base = event_init();

  38. /* start up worker threads if MT mode */
  39. thread_init(settings.num_threads, main_base);

  1. /* initialise clock event */
  2. clock_handler(0, 0, 0);

  1. /* 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的功能:

  1. /** file scope variables **/

  2. static item **todelete = NULL;
  3. static int delcurr;
  4. static int deltotal;

  5. # define run_deferred_deletes() do_run_deferred_deletes()

  6. static void delete_handler(const int fd, const short which, void *arg) {
  7.     struct timeval t = {.tv_sec = 5, .tv_usec = 0};
  8.     static bool initialized = false;

  9.     if (initialized) {
  10.         /* some versions of libevent don't like deleting events that don't
  11.          * exist, so only delete once we know this event has been added.
  12.          */
  13.         evtimer_del(&deleteevent);
  14.     } else {
  15.         initialized = true;
  16.     }
  17.     evtimer_set(&deleteevent, delete_handler, 0);
  18.     event_base_set(main_base, &deleteevent);
  19.     evtimer_add(&deleteevent, &t);
  20.     run_deferred_deletes();
  21. }

  22. /* Call run_deferred_deletes instead of this. */
  23. void do_run_deferred_deletes(void)
  24. {
  25.     int i, j = 0;

  26.     for (i = 0; i < delcurr; i++) {
  27.         item *it = todelete[i];
  28.         if (item_delete_lock_over(it)) {
  29.             assert(it->refcount > 0);
  30.             it->it_flags &= ~ITEM_DELETED;
  31.             do_item_unlink(it);
  32.             do_item_remove(it);
  33.         } else {
  34.             todelete[j++] = it;
  35.         }
  36.     }
  37.     delcurr = j;
  38. }

  39. /*
  40.  * Adds an item to the deferred-delete list so it can be reaped later.
  41.  * Returns the result to send to the client.
  42.  */
  43. char *do_defer_delete(item *it, time_t exptime)
  44. {
  45.     if (delcurr >= deltotal) {
  46.         item **new_delete = realloc(todelete, sizeof(item *) * deltotal * 2);
  47.         if (new_delete) {
  48.             todelete = new_delete;
  49.             deltotal *= 2;
  50.         } else {
  51.             /*
  52.              * can't delete it immediately, user wants a delay,
  53.              * but we ran out of memory for the delete queue
  54.              */
  55.             item_remove(it); /* release reference */
  56.             return "SERVER_ERROR out of memory expanding delete queue";
  57.         }
  58.     }

  59.     /* use its expiration time as its deletion time now */
  60.     it->exptime = realtime(exptime);
  61.     it->it_flags |= ITEM_DELETED;
  62.     todelete[delcurr++] = it;

  63.     return "DELETED";
  64. }

  65. 在main函数中有如下代码:

  66. /* initialise deletion array and timer event */
  67. deltotal = 200;
  68. delcurr = 0;

  69. if ((todelete = malloc(sizeof(item *) * deltotal)) == NULL) {
  70.         perror("failed to allocate memory for deletion array");
  71.         exit(EXIT_FAILURE);
  72. }
  73. delete_handler(0, 0, 0); /* sets up the event */
很明显实现的功能是:
每隔5秒钟触发一次delete_handler从而实现每5秒中调用一次run_deferred_deletes();也就是每5秒钟遍历一次可以删除的item的链表,先判断它们的过期时间是否到了:

  1. /*
  2.  * returns true if a deleted item's delete-locked-time is over, and it
  3.  *  should be removed from the namespace
  4.  */
  5.     bool item_delete_lock_over (item *it) {
  6.     assert(it->it_flags & ITEM_DELETED);
  7.     return (current_time >= it->exptime);
  8.     }

然后标记删除标记: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函数的定义如下:
  1. #define REALTIME_MAXDELTA 60*60*24*30
  2. /*
  3.  * given time value that's either unix time or delta from current unix time,
  4.  * return unix time. Use the fact that delta can't exceed one month (and real
  5.  * time value can't be that low).
  6.  */
  7.     static rel_time_t realtime(const time_t exptime) {
  8.         /* no. of seconds in 30 days - largest possible delta exptime */
  9.         if (exptime == 0) return 0; /* 0 means never expire */
  10.         if (exptime > REALTIME_MAXDELTA) {
  11.             /* if item expiration is at/before the server started, give it an
  12.              * expiration time of 1 second after the server started.
  13.              * (because 0 means don't expire). without this, we'd
  14.              * underflow and wrap around to some large value way in the
  15.              * future, effectively making items expiring in the past
  16.              * really expiring never
  17.              */
  18.             if (exptime <= stats.started)
  19.                 return (rel_time_t)1;
  20.             return (rel_time_t)(exptime - stats.started);
  21.         } else {
  22.             return (rel_time_t)(exptime + current_time);
  23.         }
  24.     }
第一段注释的意思是:
给定一个时间,不论它是真正的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.

阅读(753) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~