当前位置: 首页 > 工具软件 > TMCache > 使用案例 >

自己动手写缓存系统 - tmcache

蒙华翰
2023-12-01

自己动手写缓存系统 - tmcache

 

作者:heiyeluren
时间:2008-10-24
博客:http://blog.csdn.net/heiyeshuwu

 

 

 

【 原理介绍 】


tmcache 大致就是一个类似于Memcache的缓存服务器,用过的应该都大致了解它的执行过程,为了便于理解,我简单描述一下。


发送请求过程:
客户端(PHP/Java/C++) --> 缓存服务器 --> 内存(共享内存)


接收数据过程:
内存(共享内存) --> 缓存服务器 --> 客户端


大致描述就是:客户端(任何能够访问Socket的客户端语言或工具) 访问缓存服务器的指定端口,进行 存储/读取/删除 数据的操作,缓存服务器接收到指令后进行内存操作,操作结束后回写结果给客户端。所以缓存服务器端包含这些模块:Socket通信、协议解析、数据存储、数据有效期控制


以下代码就是按照这些模块来进行描述的,下面的代码取自于 tmcache - TieMa(Tiny&Mini) Memory Cache,tmcache 目前支持的功能包括:


  *  Based memory data storage
  *  Compatible memcached communication protocol
  *  Few operation interface, The use of simple
  *  Support custom port,max_clients,memory use control

 

tmcache下载(Windows版可直接运行):

Windows版本:http://heiyeluren.googlecode.com/files/tmcache-1.0.0_alpha-win32.zip
Unix/Linux版: http://heiyeluren.googlecode.com/files/tmcache-1.0.0_alpha.tar.gz

 

 

【 系统实现 】

 

一、通信协议处理模块

 

这个主要是包含一方面是监听处理Socket,tmcache里主要是依靠 init_server_listen() 函数进行监听操作,同时并发接受连接是程序里很重要的一块,可以选择方式有 select/poll 多路IO的方式,epoll/kqueue 的事件方式,另外还可以使用线程(thread)的方式,tmcache为了兼容性和简单起见,使用了线程的方式。


线程相关核心处理代码:

void tm_thread( int serversock, unsigned int max_client ){
    int clientsock, *arg;
    struct sockaddr_in client_addr;
    char currtime[32];
    unsigned clientlen;
    pthread_attr_t thread_attr;
    void *thread_result;
    
    /* Setting pthread attribute */
    pthread_attr_init(&thread_attr);
    pthread_attr_setdetachstate(&thread_attr, PTHREAD_CREATE_DETACHED);

    /* Run until cancelled */
    while (1){
        pthread_t thread;
        unsigned int clientlen = sizeof(client_addr);
        memset(currtime, 0, sizeof(currtime));
        getdate(currtime);

        /* Wait for client connection */
        if ((clientsock = accept(serversock, (struct sockaddr *) &client_addr, &clientlen)) < 0){
            die("Failed to accept client connection");
        }
        /* Use thread process new connection */
        arg = &clientsock;
        if (pthread_create(thread, &thread_attr, tm_thread_callback, (void *)arg) != 0){
            die("Create new thread failed");
        }
    }
    /* Destory pthread attribute */
    (void)pthread_attr_destroy(&thread_attr);
}


协议处理是很核心的,主要是包括存储数据的 set/add/replace/append,还有提取数据的 get/gets,删除数据的 delete/remove,获取状态 stats/stat 等指令的各种操作,主要操作处理函数是 proc_request(),它负责协议的分析很调用相关的接口来进行处理。

 


 

二、数据处理模块

 

这是数据存储处理的核心,主要是通过使用哈希表来存储数据,使用队列来记录数据的存储顺序并且为内存不够用时的处理数据结构,还有使用概率处理算法来不定期清除过期数据等等。

 

1. 哈希表数据存储

数据是采用哈希表的存储方式,存储速度简单快速,算法效率是 O(1),非常适合这种 Key => Value 的存储场合,核心的哈希算法是经典的Times33算法:


 

unsigned tm_hash( const char *str, unsigned table_size ){
 unsigned long hash = 5381; 
 int c;
 while (c = *str++) hash = ((hash << 5) + hash) + c; /* hash * 33 + c */
 hash = table_size > 0 ? hash % table_size : hash;
 return hash;
}
同时如果存在一个数据节点冲突的情况,则采用开拉链法来解决,一个哈希存储节点的数据结构,next程序用于存储下一个相同哈希映射结果的值:

 

/* Hash data item struct */
struct tm_hash_entry_t {
 char *key;   /* data key string */
 char *data;   /* data value string */
 size_t length;   /* data length */
 unsigned created;  /* data create time (Unix Timestamp) */
 unsigned expired;  /* data expire time (Unix Timestamp) */
 struct tm_hash_entry_t *next; /* key conflict link next data node pointer */
};


2. 数据失效处理

 

目前主要是两种方法处理时效,一种是当访问某个数据节点的时候,如果发现该数据的 expired 字段已经超过当前时间,那么将remove该节点。另外一种方法是在进行数据操作的时候,按照概率计算算法,不定期的清除掉已经过期的算法,看看概率算法实现:


status get_gc_probability(unsigned probaility, unsigned divisor){
    int n;
    struct timeval tv; 
    gettimeofday(&tv , (struct timezone *)NULL);
    srand((int)(tv.tv_usec + tv.tv_sec));
    n = 1 + (int)( (float)divisor * rand() / (RAND_MAX+1.0) );
    return (n <= probaility ? TRUE : FALSE); 
}

概率的几率百分比是通过 probaility 和 divisor 来确定的,缺省是 1/100 的几率,就是一百次操作里,有一次是可能执行清除过期数据操作的,这样做便于减轻程序操作的压力。


3. 内存使用完了的操作


如果tmcache启动的时候,设定了16MB的内存使用空间,但是最后内存不够用了,那么就只有通过清除前面插入的缓存数据来空出空间来进行存储新数据,这里主要是使用了队列,因为队列是使用先进先出(First in first out) 的原则的,代码:

 

/* current memory use size exceed MAX_MEM_SIZE, remove last node from queue, remove key from hash table */
if ( (get_mem_used() + length) > g_max_mem_size ){
 struct tm_queue_node_t *qnode;
 while ( (get_mem_used() + length) > g_max_mem_size ){
  qnode = tm_qremove( g_qlist );
  remove_data( qnode->key );
 }
}

这样做的缺点很明显,就是明明数据没有失效期,确被删除了,所以,缓存工具并不能作为持久化数据一样的对待方式,必须确保每次查询缓存的时候都进行了相应的存储操作,因为无法保证数据是还在内存中的。

 

 

【 结束语 】

 

基本可以确定 tmcache 是一个非常简单的缓存系统,比Memcache差距很远,更多来说 tmcache 只是一个学习的作品,同时也是做了一些简单的引导思路,希望对真正要做一个成型复杂稳定的缓存系统做一个抛砖引玉的简单参考,所以,tmcache 并不是一个稳定可靠的缓存系统,也不适合用于生产环境,更适合作为一个学习参考的小东西。 :-)


关于其他上面没有描述的内容,建议阅读tmcache的代码来获得更多相关知识。

 

下载地址:http://code.google.com/p/heiyeluren/downloads


本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/heiyeshuwu/archive/2008/10/24/3132977.aspx

 类似资料: