memcached源代码分析

合集下载

pythonmemcache简单操作

pythonmemcache简单操作

pythonmemcache简单操作python memcache 简单操作 (用于web前端优化,减少读库次数)2013-05-21 09:29:11| 分类: Python | 标签: |举报 |字号大中小订阅Memcached官网简单介绍:memcached很强大,它可以支持分布式的共享内存缓存,大型站点都用它。

对小站点来说,有足够内存的话,使用它也可以得到超赞的效果。

使用目的:由前面的介绍看到,大家使用它都是为了速度,不过我却是为了解决Session在不同浏览器中偶尔丢失的数据。

其实也不能怪浏览器啦,主要是我需要一个dict类型的session,哈哈。

安装Linux安装包对于大多数Linux发行版本来说,可以使用官方推荐的方法:Debian/Ubuntuapt-get install memcachedRedhat/Fedora/CentOSyum install memcached源码安装(CentOS 5.5)1、下载libevent(依赖)和memcached分别到以下引用地址下载最新版本:引用地址/~provos/libevent/引用地址:/p/memcached/downloads/list2、安装ibevent(依赖)和memcachedlibevent(目前最新是libevent-2.0.10-stable.tar.gz,请注意版本号)tar xvf libevent-2.0.10-stable.tar.gzcd libevent-2.0.10-stable./configure --prefix=/usr/local/libevent/makemake install接着,ls /usr/local/libevent/lib,将查看到的第一个类似libevent-X.X.so.Xln -s /usr/local/libevent/lib/libevent-2.0.so.5 /lib/libevent-2.0.so.5memcached (目前最新是1.4.5,请注意版本号)tar zxvf memcached-1.4.5.tar.gzcd memcached-1.4.5./configure --prefix=/usr/local/memcached --with-libevent=/usr/local/libevent/makemake install3、启动memcached启动参数说明(这是我是复制粘贴的,如有错误请指正):-d 选项是启动一个守护进程-m 是分配给Memcache使用的内存数量,单位是MB,默认64MB-M return error on memory exhausted (rather than removing items)-u 是运行Memcache的用户,如果当前为root 的话,需要使用此参数指定用户-l 是监听的服务器IP地址,默认为所有网卡-p 是设置Memcache的TCP监听的端口,最好是1024以上的端口-c 选项是最大运行的并发连接数,默认是1024-P 是设置保存Memcache的pid文件-f chunk size growth factor (default: 1.25)-I Override the size of each slab page. Adjusts max item size(1.4.2版本新增)例子:/usr/local/memcached/bin/memcached -d -m 100 -c 1000 -u root -p 11211可以启动多个守护进程,但是端口不能重复。

memcached编译

memcached编译

memcached编译
要编译Memcached,首先你需要确保你的系统中安装了编译Memcached所需的依赖项。

通常情况下,你需要安装开发工具包和
相关的库文件。

接下来,你可以按照以下步骤进行编译:
1. 下载Memcached源代码,你可以从Memcached官方网站或者GitHub上获取Memcached的源代码。

下载后解压到你选择的目录中。

2. 进入解压后的Memcached目录,使用终端或命令行工具进入
解压后的Memcached目录。

3. 运行配置脚本,运行`./configure`命令来配置Memcached
的编译选项。

你可以使用`--help`参数来查看可用的配置选项。

例如,你可以指定安装路径等选项。

4. 编译,运行`make`命令来编译Memcached。

这将会生成可执
行文件。

5. 安装,运行`make install`命令来安装编译好的Memcached
可执行文件和相关的文件到系统中。

你可能需要使用`sudo`命令来
获取足够的权限进行安装。

完成上述步骤后,你就成功地编译并安装了Memcached。

你可以通过运行`memcached`命令来启动Memcached服务器。

记得查看Memcached的文档以获取更多详细的信息和配置选项。

希望这些步骤可以帮助你成功编译Memcached。

Memcached 原理剖析

Memcached 原理剖析

余问题都解决了,空间利用率会大大提升。
修改 slabs_clsid 函数,让它直接返回一个定值(比如 1 )
unsigned int slabs_clsid(size_t size) {return 1;}
修改slabs_init函数,去掉循环创建所有classid属性的部分,直接添加
slabclaபைடு நூலகம்s[1]:
• 数据存储方式:Slab Allocation • 数据过期方式:Lazy Expiration + LRU
7
Memcache原理分析
数据存储方式:Slab Allocation
Slab Alloction 构造图
Slab Allocator的基本原理是按照预先 规定的大小,将分配的内存分割成特定 长度的块,以完全解决内存碎片问题。
Memcached 入门
作者:
2009-01
Tech Talk 目录索引
Memcache是什么 Memcache,ehcache的比较 Memcache原理分析 Memcache安装和基本配置 Memcache的在大型网站中的使用策略 Memcache的一些经验和技巧 Memcache一致性算法(consistent hasing)
一个id,在数据量非常大的情况下,slab链会很长(因为所有数据都挤在一条
链上了),遍历起来的代价比较高。
前面介绍了三种空间冗余,设置chunk长度等于item长度,解决了第一种空间
浪费问题,不预申请空间解决了第二种空间浪费问题,那么对于第一种问题
(slab内剩余)如何解决呢,这就需要修改POWER_BLOCK常量,使得每一
15
Memcache原理分析:

Memcached源码剖析笔记

Memcached源码剖析笔记

Memcached源码剖析笔记XguruMemcached是一个自由、源码开放、高性能、分布式内存对象缓存系统,目的在于通过减轻数据库负载来使动态Web应用程序提速。

目录1.背景 (3)2.memcached的安装 (4)3.memcached的配置 (5)4.memcached的使用 (6)4.1.存储命令 (7)4.2.读取命令 (8)4.3.删除命令 (8)4.4.高级命令 (9)4.5.其他命令 (10)5.Memcached内部工作机制 (11)5.1.Memcached基本的数据结构 (11)5.2.基本设计概念和处理流程 (12)5.3.内部Hash机制 (15)5.3.1.Hash函数及冲突解决 (15)5.3.2.HashTable主要函数 (15)5.4.slab内存处理机制 (17)5.4.1.slab主要函数 (17)5.4.2.slab机制中所采用的LRU算法 (19)5.5.控制item各种函数 (20)5.6.守护进程机制 (22)5.7.Socket处理机制 (23)15.7.1.Unix域协议 (23)5.7.2.TCP/UDP协议 (24)5.8.多线程处理机制 (25)5.9.事件处理机制 (25)6.未完善之处 (27)7.参考文献 (28)21.背景Memcached 是一个高性能的分布式内存对象缓存系统,用于动态Web应用以减轻数据库负载。

它通过在内存中缓存数据和对象来减少读取数据库的次数,从而提供动态、数据库驱动网站的速度。

Memcached基于一个存储键/值对的hashmap。

Memcached是一个自由、源码开放、高性能、分布式内存对象缓存系统,目的在于通过减轻数据库负载来使动态Web应用程序提速。

Memcached是一个在内存中对任意的数据(比如字符串,对象等)所使用的key-value 存储。

数据可以来自数据库调用,API调用,或者页面渲染的结果。

memcached源码分析一-slab

memcached源码分析一-slab

memcached源码分析⼀-slabSlab作为⼀种内存管理⽅案,其作⽤主要有以下2点:a) 避免频繁的内存分配释放造成的内存碎⽚b) 减少内存分配操作产⽣的性能开销Linux内核数据结构中也有slab的设计,Linux提供了⼀套接⼝,使⽤这套接⼝可以动态创建与释放⼀个slab结构,该slab的chunk⼤⼩通过接⼝指定,创建成功后就可以从该slab中动态申请与释放chunk⼤⼩的内存⽤于存储⽬标数据,例如内核中⽤于表⽰进程的结构体task_struc 就是使⽤的slab⽅式进⾏管理。

memcached与linux内核不同,memcached中在程序启动时即初始化⼀个slabclass_t的全局数组,每⼀个slabclass_t结构的chunk⼤⼩不同,构成⼀个全局slab池。

memcached中的slab相关操作源码主要集中在源⽂件slabs.c中,下⾯对它进⾏分析。

1. 结构体slabclass_t以下是memcached中对slabclass_t的定义,typedef struct {unsigned int size; /* sizes of items */unsigned int perslab; /* how many items per slab */void *slots; /* list of item ptrs */unsigned int sl_curr; /* total free items in list */unsigned int slabs; /* how many slabs were allocated for this class */void **slab_list; /* array of slab pointers */unsigned int list_size; /* size of prev array */size_t requested; /* The number of requested bytes */} slabclass_t;slabclass_t对内存的组织可以粗略的⽤图1-1表⽰,图1-1 slabclass_t内存组织⽅式slab_list是⼀个可以动态分配的数组,数组⼤⼩以list_size表⽰,数组中已存储元素数⽬以slabs表⽰,数组中每⼀个元素都表⽰⼀个page ⼤⼩的可⽤内存(page也称为slab)。

Python中的Memcached缓存

Python中的Memcached缓存

Python中的Memcached缓存Memcached是一款高性能的分布式内存对象缓存系统。

它的主要功能是将数据存储在内存中,从而提高数据访问速度。

作为一款大型网站所必不可少的缓存工具,Memcached在Python 中的应用极度广泛。

在这篇论文中,我们将深入探讨Memcached在Python中的应用,分析其优点和缺点,并提出一些最佳实践,以帮助开发人员更好地利用Memcached提升应用的性能和用户体验。

一、Memcached的优点1.高速缓存Memcached是一款基于内存的缓存系统,它可以实现非常快速的缓存访问速度。

由于数据存储在内存中,所以它的响应速度相当快,甚至可以达到微秒级别。

在大型网站前端中,常使用Memcached来缓存一些静态或不怎么变化的数据,如网站配置信息、静态页面等。

通过缓存这些数据,可以减少对数据库等后端系统的请求,从而提高网站的性能。

2.可扩展性Memcached是一款分布式缓存系统,它可以将缓存数据分散存储在多台机器的内存中,从而实现更大的存储容量和更高的并发处理能力。

在高并发的情况下,系统可以简单地通过增加或减少服务器数量来扩展缓存能力。

此外,Memcached还具有自动数据平衡和故障转移等功能,可以实现高可用性和灵活性。

3.支持多种语言Memcached支持多种语言,包括Python、Java、PHP等,可以方便地嵌入到各种应用程序中,快速提高应用程序的性能。

在Python中,Memcached是一种常见的缓存解决方案,可以通过安装对应的Python模块,轻松地集成到应用程序中。

二、Memcached的缺点1.容量限制由于Memcached是一款基于内存的缓存系统,所以它的缓存容量是有限的。

在实际应用中,需要根据业务需求和服务器硬件条件等因素综合考虑,设置合适的缓存容量,避免因容量不足而导致缓存失效。

2.数据不持久化Memcached只是一款内存缓存系统,它并不支持数据持久化。

memcached字符编码

memcached字符编码

memcached字符编码Memcached 是一款开源的高性能分布式内存对象缓存系统,它主要用于缓存数据库查询结果、API调用结果或其他计算成本高的操作的结果,从而提高应用程序的性能和响应速度。

在 Memcached 中,字符编码主要涉及两个方面,存储数据时的编码和读取数据时的解码。

1. 存储数据时的编码:当我们向 Memcached 存储数据时,通常是以字符串的形式进行存储。

Memcached 默认使用的字符编码是 ASCII 编码,它是一种基于拉丁字母的字符编码标准,支持英文字母、数字和一些特殊字符。

ASCII 编码每个字符占用一个字节的存储空间。

另外,Memcached 也支持使用其他字符编码存储数据,如UTF-8 编码。

UTF-8 是一种可变长度的 Unicode 字符编码,它可以表示世界上几乎所有的字符。

UTF-8 编码中,英文字母和数字仍然只占用一个字节的存储空间,而中文、日文、韩文等非拉丁字符则占用多个字节的存储空间。

2. 读取数据时的解码:当我们从 Memcached 中读取数据时,Memcached 会将存储的字符串数据解码成对应的字符编码。

如果存储时使用的是 ASCII编码,那么读取时就会解码成 ASCII 编码的字符串。

同理,如果存储时使用的是 UTF-8 编码,那么读取时就会解码成 UTF-8 编码的字符串。

在读取数据时,我们的应用程序需要根据具体的需求和使用的编程语言,将解码后的字符串再进行进一步的处理和转换。

例如,在使用 Python 进行开发时,可以使用内置的字符串处理函数来处理解码后的字符串。

需要注意的是,Memcached 本身并不关心存储的数据的具体编码方式,它只是将数据当作二进制的字节流进行存储和读取。

因此,在使用 Memcached 存储和读取数据时,我们需要确保应用程序和Memcached 之间的数据编码方式是一致的,以避免出现乱码或数据解析错误的情况。

memcache slab模块源码解读

memcache slab模块源码解读

memcache slab模块源码解读slab模块简介由于采用常用的malloc和free函数会造成内存碎片比较多,所以memcache采用的是自己写的slab内存管理模块来管理内存,特点如下:∙系统中用不同大小的chunk(struct item)来存数据,对某个需要缓存的数据,找到最适合大小的item来保存∙系统中分为多个slabclass,每个slabclass有一个或多个slab,每个slab 里面包含多个大小相同的itemslab结构体关系图我们从某个slabclass来看其与slab、item之间的关系从图中可以看出,每个slabclass对应一个或多个slab,在slabclass_t结构体里面有个slab_list数组,其指向的是每个slab内存块的首地址(在这里,特别说明一下,slab是一段连续的内存空间,和操作系统中的内存页有点类似,memcache没有专门的一个结构体来描述它,只要其首地址来比较某个slab),而每个slab中有多个chunk(struct item)数据块用来存数据整个系统的slab初始化过程在memcache模块初始化的时候,首先调用的函数是slab_init,其源码如下:/*** Determines the chunk sizes and initializes the slab class descriptors* accordingly.*/void slabs_init(const size_t limit, const double factor, const bool prealloc) {int i = POWER_SMALLEST - 1;unsigned int size = sizeof(item) + settings.chunk_size;mem_limit = limit;if (prealloc) {/* Allocate everything in a big chunk with malloc */mem_base = malloc(mem_limit);if (mem_base != NULL) {mem_current = mem_base;mem_avail = mem_limit;} else {fprintf(stderr, "Warning: Failed to allocate requested memory in" " one large chunk.\nWill allocate in smaller chunks\n"); }}memset(slabclass, 0, sizeof(slabclass));while (++i < POWER_LARGEST && size <= settings.item_size_max / factor) { /* Make sure items are always n-byte aligned */if (size % CHUNK_ALIGN_BYTES)size += CHUNK_ALIGN_BYTES - (size % CHUNK_ALIGN_BYTES);slabclass[i].size = size;slabclass[i].perslab = settings.item_size_max / slabclass[i].size;size *= factor;if (settings.verbose > 1) {fprintf(stderr, "slab class %3d: chunk size %9u perslab %7u\n", i, slabclass[i].size, slabclass[i].perslab);}}power_largest = i;slabclass[power_largest].size = settings.item_size_max;slabclass[power_largest].perslab = 1;if (settings.verbose > 1) {fprintf(stderr, "slab class %3d: chunk size %9u perslab %7u\n",i, slabclass[i].size, slabclass[i].perslab);}/* for the test suite: faking of how much we've already malloc'd */{char *t_initial_malloc = getenv("T_MEMD_INITIAL_MALLOC");if (t_initial_malloc) {mem_malloced = (size_t)atol(t_initial_malloc);}}if (prealloc) {slabs_preallocate(power_largest);}}我们来分析这个函数:1.首先申请mem_limit大小的内存块2.while循环中对管理item大小不同的slabclass_t结构体进行初始化3.调用slabs_preallocate对分配mem_limit大小的内存块进行具体的处理下面,我们来看slabs_preallocate的代码:static void slabs_preallocate (const unsigned int maxslabs) {int i;unsigned int prealloc = 0;/* pre-allocate a 1MB slab in every size class so people don't getconfused by non-intuitive "SERVER_ERROR out of memory"messages. this is the most common question on the mailinglist. if you really don't want this, you can rebuild withoutthese three lines. */for (i = POWER_SMALLEST; i <= POWER_LARGEST; i++) {if (++prealloc > maxslabs)return;if (do_slabs_newslab(i) == 0) {fprintf(stderr, "Error while preallocating slab memory!\n""If using -L or other prealloc options, max memory must be " "at least %d megabytes.\n", power_largest);exit(1);}}}从代码可以看出:1.slabs_preallocate会对所有的slabclass_t进行分配slab的操作(这里需要注意的是,初始化的时候默认每个slabclass是只有一个slab的),即do_slabs_newslab函数下面就进接着看do_slabs_newslab函数static int do_slabs_newslab(const unsigned int id) {slabclass_t *p = &slabclass[id];int len =? settings.item_size_max: p->size * p->perslab;char *ptr;if ((mem_limit && mem_malloced + len > mem_limit && p->slabs > 0) ||(grow_slab_list(id) == 0) ||((ptr = memory_allocate((size_t)len)) == 0)) {MEMCACHED_SLABS_SLABCLASS_ALLOCATE_FAILED(id);return 0;}memset(ptr, 0, (size_t)len);split_slab_page_into_freelist(ptr, id);p->slab_list[p->slabs++] = ptr;mem_malloced += len;MEMCACHED_SLABS_SLABCLASS_ALLOCATE(id);return 1;}do_slabs_newslab函数的功能就是为某个slabclass新分配一个slab大小的内存块1.当memcache分配一个新的slab时会造成所使用的内存超过对定的mem_limit大小时,则会提示内存不够分配2.当mem_limit大小的内存够分配的时候,则调用grow_slab_list来根据需要扩充struct slabclass_t中void **slab_list数组的大小,然后就是用memory_allocate函数分配一个slab内存块3.然后调用split_slab_page_into_freelist函数把slab内存块分成若干个item,然后把这些空闲的item都插入到slabclass_t中的void *slots指向的链表中4.把新分配的slab的首地址添加到'slabclass_t`数组中去5.跟新memcache已申请的内存大小mem_malloced这样memcache的slab模块的初始化工作就完成了item相关操作在整个memcache内存管理slab系统初始化完成后,就进入了memcahe的运行阶段,这时候,会涉及到的一些操作是(在文件items.c中)∙item的分配∙item的释放∙item获取∙item链接到队列(当一个item被set的时候,会链接到队列)∙item从队列中取消链接下面我们会依次序来说上述的几个操作:item的分配item的分配对应于do_item_alloc先给出do_item_alloc的程序流程图然后贴出代码item *do_item_alloc(char *key, const size_t nkey, const int flags,const rel_time_t exptime, const int nbytes,const uint32_t cur_hv) {uint8_t nsuffix;item *it = NULL;char suffix[40];size_t ntotal = item_make_header(nkey + 1, flags, nbytes, suffix, &nsuffix); if (e_cas) {ntotal += sizeof(uint64_t);}unsigned int id = slabs_clsid(ntotal);if (id == 0)return 0;mutex_lock(&cache_lock);/* do a quick check if we have any expired items in the tail.. */int tries = 5;int tried_alloc = 0;item *search;void *hold_lock = NULL;rel_time_t oldest_live = settings.oldest_live;search = tails[id];/* We walk up *only* for locked items. Never searching for expired.* Waste of CPU for almost all deployments */for (; tries > 0 && search != NULL; tries--, search=search->prev) {uint32_t hv = hash(ITEM_key(search), search->nkey, 0);/* Attempt to hash item lock the "search" item. If locked, no* other callers can incr the refcount*//* FIXME: I think we need to mask the hv here for comparison? */if (hv != cur_hv && (hold_lock = item_trylock(hv)) == NULL)continue;/* Now see if the item is refcount locked */if (refcount_incr(&search->refcount) != 2) {refcount_decr(&search->refcount);/* Old rare bug could cause a refcount leak. We haven't seen* it in years, but we leave this code in to prevent failures* just in case */if (search->time + TAIL_REPAIR_TIME < current_time) {itemstats[id].tailrepairs++;search->refcount = 1;do_item_unlink_nolock(search, hv);}if (hold_lock)item_trylock_unlock(hold_lock);continue;}/* Expired or flushed *//**检查LRU尾部的item是否超时,如果超时直接,返回这个item即可**/if ((search->exptime != 0 && search->exptime < current_time)|| (search->time <= oldest_live && oldest_live <= current_time)) {itemstats[id].reclaimed++;if ((search->it_flags & ITEM_FETCHED) == 0) {itemstats[id].expired_unfetched++;}it = search;slabs_adjust_mem_requested(it->slabs_clsid, ITEM_ntotal(it), ntotal); do_item_unlink_nolock(it, hv);/* Initialize the item block: */it->slabs_clsid = 0;} else if ((it = slabs_alloc(ntotal, id)) == NULL) { /*尝试去slab中申请一个新的item*/tried_alloc = 1;if (settings.evict_to_free == 0) {/*如果在slab中申请失败,并且不允许LRU 替换策略*/itemstats[id].outofmemory++;} else {/*如果在slab申请失败,但是允许LRU策略,那么就从LRU队尾取出一个item*/itemstats[id].evicted++;itemstats[id].evicted_time = current_time - search->time;if (search->exptime != 0)itemstats[id].evicted_nonzero++;if ((search->it_flags & ITEM_FETCHED) == 0) {itemstats[id].evicted_unfetched++;}it = search;slabs_adjust_mem_requested(it->slabs_clsid, ITEM_ntotal(it), ntotal);do_item_unlink_nolock(it, hv);/* Initialize the item block: */it->slabs_clsid = 0;/* If we've just evicted an item, and the automover is set to* angry bird mode, attempt to rip memory into this slab class. * TODO: Move valid object detection into a function, and on a* "successful" memory pull, look behind and see if the next alloc * would be an eviction. Then kick off the slab mover before the * eviction happens.*/if (settings.slab_automove == 2)slabs_reassign(-1, id);}}refcount_decr(&search->refcount);/* If hash values were equal, we don't grab a second lock */if (hold_lock)item_trylock_unlock(hold_lock);break;}if (!tried_alloc && (tries == 0 || search == NULL))it = slabs_alloc(ntotal, id);if (it == NULL) {itemstats[id].outofmemory++;mutex_unlock(&cache_lock);return NULL;}assert(it->slabs_clsid == 0);assert(it != heads[id]);/* Item initialization can happen outside of the lock; the item's already * been removed from the slab LRU.*/it->refcount = 1; /* the caller will have a reference */mutex_unlock(&cache_lock);it->next = it->prev = it->h_next = 0;it->slabs_clsid = id;DEBUG_REFCNT(it, '*');it->it_flags = e_cas ? ITEM_CAS : 0;it->nkey = nkey;it->nbytes = nbytes;memcpy(ITEM_key(it), key, nkey);it->exptime = exptime;memcpy(ITEM_suffix(it), suffix, (size_t)nsuffix);it->nsuffix = nsuffix;return it;}请注意:我刚开始读这段代码的时候有个最不清楚的地方就是那个for循环好像执行了5次,如果这5次for循环都没找到过期的item,那么岂不是会调用slab_alloc函数5次,重复分配了5次item吗?答案:后来仔细一看,原来在for循环的最后一行代码有个break语句,那么这就说明其实for循环里面的语句最多执行一次,也就不会有上面的问题了。

  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

Part2
Part2
Memcached的线程关系
typedef struct { pthread_t thread_id; struct event_base *base; struct event notify_event; int notify_receive_fd; int notify_send_fd; CQ new_conn_queue; } LIBEVENT_THREAD; void thread_init(int nthreads, struct event_base *main_base) { threads[i].notify_receive_fd = fds[0]; threads[i].notify_send_fd = fds[1]; setup_thread(&threads[i]); for (i = 1; i < nthreads; i++) { create_worker(worker_libevent, &threads[i]); } } static void setup_thread(LIBEVENT_THREAD *me) { if (! me->base) me->base = event_init(); event_set(&me->notify_event, me->notify_receive_fd, EV_READ | EV_PERSIST, thread_libevent_process, me); event_base_set(me->base, &me->notify_event); if (event_add(&me->notify_event, 0) == -1) { fprintf(stderr, "Can't monitor libevent notify pipe\n"); exit(1); } cq_init(&me->new_conn_queue); }
使用memcached的公司
Part2
预备知识
libevent简介 线程池模型简介
Part2
libevent是一个开源的、跨平台的事件处理函数库。 它封装了网络、信号以及定时器的事件,以及附带了一些缓冲区管理功能,非常适合用 来处理server的底层IO。对于网络事件,他封装了epoll, select, kqueue(BSD)等模 型,性能非常好。 下面是使用libevent的一个简单例子:
static int server_socket(const int port, const bool is_udp) { socket(); bind() listen(); conn_new(sfd, conn_listening, EV_READ | EV_PERSIST, 1, false, main_base); } conn *conn_new(const int sfd, const int init_state, const int event_flags, const int read_buffer_size, const bool is_udp, struct event_base *base) { conn *c = conn_from_freelist(); event_set(&c->event, sfd, event_flags, event_handler, (void *)c); event_base_set(base, &c->event); c->ev_flags = event_flags; event_add(&c->event, 0) ; } //主线程和worker线程的公有事件处理回调函数 void event_handler(const int fd, const short which, void *arg) { conn *c; c = (conn *)arg; assert(c != NULL); c->which = which; drive_machine(c); //处理具体的事件 return; }
Libevent由于所有的事件都是由一个event_base对象来管理,是一个全局对象因此不支 持多线程。Memcached给每个线程创建了一个event_base对象,每个线程自己管理 自己的网络IO
Part2
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) { 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(); } void do_run_deferred_deletes(void) { for (i = 0; i < delcurr; i++) { item *it = todelete[i]; if (item_delete_lock_over(it)) { it->it_flags &= ~ITEM_DELETED; do_item_unlink(it); do_item_remove(it); } else { todelete[j++] = it; } } delcurr = j; }
static void thread_libevent_process(int fd, short which, void *arg) { read(fd, buf, 1); item = cq_peek(&me->new_conn_queue); conn_new(item->sfd, item->init_state, item->event_flags, item->read_buffer_size/*2048*/, item->is_udp, me->base); }
Memcached源代码分析
looloochen 2009-7-20
P在用memcached Memcached的适用场合
Part 1
简介
memcached是高性能的,分布式的内存对象缓存系统,常常用于在应用 中减少数据库负载,提升访问速度。 memcached由Danga Interactive公司开发,是一个开源软件。 memcached一般作为数据库的前端cache使用,用于减少磁盘的访问, 它的数据都以key-value的形式存储,访问数据很简单,不像db解析 SQL,一条简单的命令即可存取数据。 可以使用unix域套接字,tcp以及udp协议访问memcached,因此使用 memcached是有代价的,如果想要缓存的数据不需要被共享,那么本 地缓存可能更适用一些。
static void drive_machine(conn *c) { while (!stop) { switch(c->state) { case conn_listening: //只有主线程才会有的事件 sfd = accept(c->sfd, (struct sockaddr *)&addr, &addrlen); dispatch_conn_new(sfd, conn_read, EV_READ | EV_PERSIST, DATA_BUFFER_SIZE, false); break; case conn_read: if (try_read_command(c) != 0) { continue; } if (try_read_network(c)) != 0) { continue; } update_event(c, EV_READ | EV_PERSIST); break; case conn_nread: complete_nread(c); break; res = read(c->sfd, c->ritem, c->rlbytes); if (res > 0) { c->ritem += res; c->rlbytes -= res; break; } if (res == 0) { /* end of stream */ conn_set_state(c, conn_closing); break; } if (res == -1 && (errno == EAGAIN || errno == EWOULDBLOCK)) { if (!update_event(c, EV_READ | EV_PERSIST)) { conn_set_state(c, conn_closing); break; } stop = true; break; } //其它错误,关闭socket conn_set_state(c, conn_closing); break; case conn_swallow: ... case conn_write: ... case conn_mwrite: ... ... case conn_closing: if (c->udp) conn_cleanup(c); else conn_close(c); stop = true; break; } } return; }
struct event_base *base = event_init(); event_set(&event, fd, EV_READ, callback_fun, arg); event_base_set(base, &event); event_add(&, 0); event_base_loop(base, 0); 它的功能相当于: While(1) { FD_SET(fd, rd_set); select(max_fd + 1, rd_set, NULL, NULL, tv); for(int i = 0; i < max_fd; ++i) if(FD_ISSET(i, rd_set)) callback_fun(fd, arg); }
相关文档
最新文档