当前位置: 首页 > news >正文

微信服务号菜单链接网站怎么做的网站优化的主要内容

微信服务号菜单链接网站怎么做的,网站优化的主要内容,数据库和wordpress,甘肃建设住房厅网站Memcached是danga.com#xff08;运营LiveJournal的技术团队#xff09;开发的一套分布式内存对象缓存系统#xff0c;用于在动态系统中减少数据库负载#xff0c;提升性能。关于这个东西#xff0c;相信很多人都用过#xff0c;本文意在通过对memcached的实现及代码分析…  Memcached是danga.com运营LiveJournal的技术团队开发的一套分布式内存对象缓存系统用于在动态系统中减少数据库负载提升性能。关于这个东西相信很多人都用过本文意在通过对memcached的实现及代码分析获得对这个出色的开源软件更深入的了解并可以根据我们的需要对其进行更进一步的优化。末了将通过对BSM_Memcache扩展的分析加深对memcached的使用方式理解。 本文的部分内容可能需要比较好的数学基础作为辅助。 ◎Memcached是什么 在阐述这个问题之前我们首先要清楚它“不是什么”。很多人把它当作和SharedMemory那种形式的存储载体来使用虽然memcached使用了同样的“KeyValue”方式组织数据但是它和共享内存、APC等本地缓存有非常大的区别。Memcached是分布式的也就是说它不是本地的。它基于网络连接当然它也可以使用localhost方式完成服务本身它是一个独立于应用的程序或守护进程Daemon方式。 Memcached使用libevent库实现网络连接服务理论上可以处理无限多的连接但是它和Apache不同它更多的时候是面向稳定的持续连接的所以它实际的并发能力是有限制的。在保守情况下memcached的最大同时连接数为200这和Linux线程能力有关系这个数值是可以调整的。关于libevent可以参考相关文档。 Memcached内存使用方式也和APC不同。APC是基于共享内存和MMAP的memcachd有自己的内存分配算法和管理方式它和共享内存没有关系也没有共享内存的限制通常情况下每个memcached进程可以管理2GB的内存空间如果需要更多的空间可以增加进程数。  ◎Memcached适合什么场合 在很多时候memcached都被滥用了这当然少不了对它的抱怨。我经常在论坛上看见有人发贴类似于“如何提高效率”回复是“用memcached”至于怎么用用在哪里用来干什么一句没有。memcached不是万能的它也不是适用在所有场合。 Memcached是“分布式”的内存对象缓存系统那么就是说那些不需要“分布”的不需要共享的或者干脆规模小到只有一台服务器的应用memcached不会带来任何好处相反还会拖慢系统效率因为网络连接同样需要资源即使是UNIX本地连接也一样。 在我之前的测试数据中显示memcached本地读写速度要比直接PHP内存数组慢几十倍而APC、共享内存方式都和直接数组差不多。可见如果只是本地级缓存使用memcached是非常不划算的。 Memcached在很多时候都是作为数据库前端cache使用的。因为它比数据库少了很多SQL解析、磁盘操作等开销而且它是使用内存来管理数据的所以它可以提供比直接读取数据库更好的性能在大型系统中访问同样的数据是很频繁的memcached可以大大降低数据库压力使系统执行效率提升。另外memcached也经常作为服务器之间数据共享的存储媒介例如在SSO系统中保存系统单点登陆状态的数据就可以保存在memcached中被多个应用共享。 需要注意的是memcached使用内存管理数据所以它是易失的当服务器重启或者memcached进程中止数据便会丢失所以memcached不能用来持久保存数据。很多人的错误理解memcached的性能非常好好到了内存和硬盘的对比程度其实memcached使用内存并不会得到成百上千的读写速度提高它的实际瓶颈在于网络连接它和使用磁盘的数据库系统相比好处在于它本身非常“轻”因为没有过多的开销和直接的读写方式它可以轻松应付非常大的数据交换量所以经常会出现两条千兆网络带宽都满负荷了memcached进程本身并不占用多少CPU资源的情况。 ◎Memcached的工作方式 以下的部分中读者最好能准备一份memcached的源代码。 Memcached是传统的网络服务程序如果启动的时候使用了-d参数它会以守护进程的方式执行。创建守护进程由daemon.c完成这个程序只有一个daemon函数这个函数很简单如无特殊说明代码以1.2.1为准 CODE: #include fcntl.h #include stdlib.h #include unistd.h int daemon(nochdir, noclose) int nochdir, noclose; { int fd;  switch (fork()) { case -1: return (-1); case 0:  break;   default: _exit(0); } if (setsid() -1) return (-1); if (!nochdir) (void)chdir(”/”); if (!noclose (fd open(”/dev/null”, O_RDWR, 0)) ! -1) { (void)dup2(fd, STDIN_FILENO); (void)dup2(fd, STDOUT_FILENO); (void)dup2(fd, STDERR_FILENO); if (fd STDERR_FILENO) (void)close(fd); } return (0); } 这个函数 fork 了整个进程之后父进程就退出接着重新定位 STDIN 、 STDOUT 、 STDERR 到空设备 daemon 就建立成功了。  Memcached 本身的启动过程在 memcached.c 的 main 函数中顺序如下  1 、调用 settings_init() 设定初始化参数 2 、从启动命令中读取参数来设置 setting 值 3 、设定 LIMIT 参数 4 、开始网络 socket 监听如果非 socketpath 存在 1.2 之后支持 UDP 方式 5 、检查用户身份 Memcached 不允许 root 身份启动 6 、如果有 socketpath 存在开启 UNIX 本地连接Sock 管道 7 、如果以 -d 方式启动创建守护进程如上调用 daemon 函数 8 、初始化 item 、 event 、状态信息、 hash 、连接、 slab 9 、如设置中 managed 生效创建 bucket 数组 10 、检查是否需要锁定内存页 11 、初始化信号、连接、删除队列 12 、如果 daemon 方式处理进程 ID 13 、event 开始启动过程结束 main 函数进入循环。  在 daemon 方式中因为 stderr 已经被定向到黑洞所以不会反馈执行中的可见错误信息。  memcached.c 的主循环函数是 drive_machine 传入参数是指向当前的连接的结构指针根据 state 成员的状态来决定动作。  Memcached 使用一套自定义的协议完成数据交换它的 protocol 文档可以参考 http://code.sixapart.com/svn/memcached/trunk/server/doc/protocol.txt 在API中换行符号统一为\r\n ◎Memcached的内存管理方式 Memcached有一个很有特色的内存管理方式为了提高效率它使用预申请和分组的方式管理内存空间而并不是每次需要写入数据的时候去malloc删除数据的时候free一个指针。Memcached使用slab-chunk的组织方式管理内存。 1.1和1.2的slabs.c中的slab空间划分算法有一些不同后面会分别介绍。 Slab可以理解为一个内存块一个slab是memcached一次申请内存的最小单位在memcached中一个slab的大小默认为1048576字节1MB所以memcached都是整MB的使用内存。每一个slab被划分为若干个chunk每个chunk里保存一个item每个item同时包含了item结构体、key和value注意在memcached中的value是只有字符串的。slab按照自己的id分别组成链表这些链表又按id挂在一个slabclass数组上整个结构看起来有点像二维数组。slabclass的长度在1.1中是21在1.2中是200。 slab有一个初始chunk大小1.1中是1字节1.2中是80字节1.2中有一个factor值默认为1.25 在1.1中chunk大小表示为初始大小*2^nn为classid即id为0的slab每chunk大小1字节id为1的slab每chunk大小2字节id为2的slab每chunk大小4字节……id为20的slab每chunk大小为1MB就是说id为20的slab里只有一个chunk CODE: void slabs_init(size_t limit) { int i; int size1; mem_limit limit; for(i0; iPOWER_LARGEST; i, size*2) { slabclass[i].size size; slabclass[i].perslab POWER_BLOCK / size; slabclass[i].slots 0; slabclass[i].sl_curr slabclass[i].sl_total slabclass[i].slabs 0; slabclass[i].end_page_ptr 0; slabclass[i].end_page_free 0; slabclass[i].slab_list 0; slabclass[i].list_size 0; slabclass[i].killing 0; } /* 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 atol(getenv(”T_MEMD_INITIAL_MALLOC”)); } } /* pre-allocate slabs by default, unless the environment variable for testing is set to something non-zero */ { char *pre_alloc getenv(”T_MEMD_SLABS_ALLOC”); if (!pre_alloc || atoi(pre_alloc)) { slabs_preallocate(limit / POWER_BLOCK); } } } 在1.2中chunk大小表示为初始大小*f^nf为factor在memcached.c中定义n为classid同时201个头不是全部都要初始化的因为factor可变初始化只循环到计算出的大小达到slab大小的一半为止而且它是从id1开始的即id为1的slab每chunk大小80字节id为2的slab每chunk大小80*fid为3的slab每chunk大小80*f^2初始化大小有一个修正值CHUNK_ALIGN_BYTES用来保证n-byte排列 保证结果是CHUNK_ALIGN_BYTES的整倍数。这样在标准情况下memcached1.2会初始化到id40这个slab中每个chunk大小为504692每个slab中有两个chunk。最后slab_init函数会在最后补足一个id41它是整块的也就是这个slab中只有一个1MB大的chunk CODE: void slabs_init(size_t limit, double factor) { int i POWER_SMALLEST – 1; unsigned int size sizeof(item) settings.chunk_size; /* Factor of 2.0 means use the default memcached behavior */ if (factor 2.0 size 128) size 128; mem_limit limit; memset(slabclass, 0, sizeof(slabclass)); while (i POWER_LARGEST size POWER_BLOCK / 2) { /* 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 POWER_BLOCK / slabclass[i].size; size * factor;  if (settings.verbose 1) { fprintf(stderr, “slab class %3d: chunk size %6d perslab %5d\n”, i, slabclass[i].size, slabclass[i].perslab); }        } power_largest i; slabclass[power_largest].size POWER_BLOCK; slabclass[power_largest].perslab 1; /* 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 atol(getenv(”T_MEMD_INITIAL_MALLOC”)); }        } #ifndef DONT_PREALLOC_SLABS { char *pre_alloc getenv(”T_MEMD_SLABS_ALLOC”); if (!pre_alloc || atoi(pre_alloc)) { slabs_preallocate(limit / POWER_BLOCK); } } #endif } 由上可以看出memcached的内存分配是有冗余的当一个slab不能被它所拥有的chunk大小整除时slab尾部剩余的空间就被丢弃了如id40中两个chunk占用了1009384字节这个slab一共有1MB那么就有39192字节被浪费了。 Memcached使用这种方式来分配内存是为了可以快速的通过item长度定位出slab的classid有一点类似hash因为item的长度是可以计算的比如一个item的长度是300字节在1.2中就可以得到它应该保存在id7的slab中因为按照上面的计算方法id6的chunk大小是252字节id7的chunk大小是316字节id8的chunk大小是396字节表示所有252到316字节的item都应该保存在id7中。同理在1.1中也可以计算得到它出于256和512之间应该放在chunk_size为512的id9中(32位系统。 Memcached初始化的时候会初始化slab前面可以看到在main函数中调用了slabs_init()。它会在slabs_init()中检查一个常量DONT_PREALLOC_SLABS如果这个没有被定义说明使用预分配内存方式初始化slab这样在所有已经定义过的slabclass中每一个id创建一个slab。这样就表示1.2在默认的环境中启动进程后要分配41MB的slab空间在这个过程里memcached的第二个内存冗余发生了因为有可能一个id根本没有被使用过但是它也默认申请了一个slab每个slab会用掉1MB内存 当一个slab用光后又有新的item要插入这个id那么它就会重新申请新的slab申请新的slab时对应id的slab链表就要增长这个链表是成倍增长的在函数grow_slab_list函数中这个链的长度从1变成2从2变成4从4变成8…… CODE: static int grow_slab_list (unsigned int id) { slabclass_t *p slabclass[id]; if (p-slabs p-list_size) { size_t new_size  p-list_size ? p-list_size * 2 : 16;  void *new_list realloc(p-slab_list, new_size*sizeof(void*)); if (new_list 0) return 0; p-list_size new_size; p-slab_list new_list; } return 1; } 在定位item时都是使用slabs_clsid函数传入参数为item大小返回值为classid由这个过程可以看出memcached的第三个内存冗余发生在保存item的过程中item总是小于或等于chunk大小的当item小于chunk大小时就又发生了空间浪费。 ◎Memcached的NewHash算法 Memcached的item保存基于一个大的hash表它的实际地址就是slab中的chunk偏移但是它的定位是依靠对key做hash的结果在primary_hashtable中找到的。在assoc.c和items.c中定义了所有的hash和item操作。 Memcached使用了一个叫做NewHash的算法它的效果很好效率也很高。1.1和1.2的NewHash有一些不同主要的实现方式还是一样的1.2的hash函数是经过整理优化的适应性更好一些。 NewHash的原型参考http://burtleburtle.net/bob/hash/evahash.html。数学家总是有点奇怪呵呵 为了变换方便定义了u4和u1两种数据类型u4就是无符号的长×××u1就是无符号char(0-255)。 具体代码可以参考1.1和1.2源码包。 注意这里的hashtable长度1.1和1.2也是有区别的1.1中定义了HASHPOWER常量为20hashtable表长为hashsize(HASHPOWER)就是4MBhashsize是一个宏表示1右移n位1.2中是变量16即hashtable表长65536 CODE: typedef  unsigned long  int  ub4;   /* unsigned 4-byte quantities */ typedef  unsigned       char ub1;   /* unsigned 1-byte quantities */ #define hashsize(n) ((ub4)1(n)) #define hashmask(n) (hashsize(n)-1) 在assoc_init()中会对primary_hashtable做初始化对应的hash操作包括assoc_find()、assoc_expand()、assoc_move_next_bucket()、assoc_insert()、assoc_delete()对应于item的读写操作。其中assoc_find()是根据key和key长寻找对应的item地址的函数注意在C中很多时候都是同时直接传入字符串和字符串长度而不是在函数内部做strlen返回的是item结构指针它的数据地址在slab中的某个chunk上。 items.c是数据项的操作程序每一个完整的item包括几个部分在item_make_header()中定义为 key键 nkey键长 flags用户定义的flag其实这个flag在memcached中没有启用 nbytes值长包括换行符号\r\n suffix后缀Buffer nsuffix后缀长 一个完整的item长度是键长值长后缀长item结构大小32字节item操作就是根据这个长度来计算slab的classid的。 hashtable中的每一个桶上挂着一个双链表item_init()的时候已经初始化了heads、tails、sizes三个数组为0这三个数组的大小都为常量LARGEST_ID默认为255这个值需要配合factor来修改在每次item_assoc()的时候它会首先尝试从slab中获取一块空闲的chunk如果没有可用的chunk会在链表中扫描50次以得到一个被LRU踢掉的item将它unlink然后将需要插入的item插入链表中。 注意item的refcount成员。item被unlink之后只是从链表上摘掉不是立刻就被free的只是将它放到删除队列中item_unlink_q()函数。 item对应一些读写操作包括remove、update、replace当然最重要的就是alloc操作。 item还有一个特性就是它有过期时间这是memcached的一个很有用的特性很多应用都是依赖于memcached的item过期比如session存储、操作锁等。item_flush_expired()函数就是扫描表中的item对过期的item执行unlink操作当然这只是一个回收动作实际上在get的时候还要进行时间判断 CODE: /* expires items that are more recent than the oldest_live setting. */ void item_flush_expired() { int i;   item *iter, *next; if (! settings.oldest_live) return;  for (i 0; i LARGEST_ID; i) { /* The LRU is sorted in decreasing time order, and an item’s timestamp * is never newer than its last access time, so we only need to walk * back until we hit an item older than the oldest_live time. * The oldest_live checking will auto-expire the remaining items. */ for (iter heads[i]; iter ! NULL; iter next) {  if (iter-time settings.oldest_live) { next iter-next; if ((iter-it_flags ITEM_SLABBED) 0) {  item_unlink(iter); }        } else { /* We’ve hit the first old item. Continue to the next queue. */ break;   }        }        } } CODE: /* wrapper around assoc_find which does the lazy expiration/deletion logic */ item *get_item_notedeleted(char *key, size_t nkey, int *delete_locked) { item *it assoc_find(key, nkey); if (delete_locked) *delete_locked 0; if (it (it-it_flags ITEM_DELETED)) { /* it’s flagged as delete-locked.  let’s see if that condition is past due, and the 5-second delete_timer just hasn’t gotten to it yet… */ if (! item_delete_lock_over(it)) { if (delete_locked) *delete_locked 1; it 0;  }        } if (it settings.oldest_live settings.oldest_live current_time it-time settings.oldest_live) { item_unlink(it); it 0;  } if (it it-exptime it-exptime current_time) { item_unlink(it); it 0;  } return it; } Memcached的内存管理方式是非常精巧和高效的它很大程度上减少了直接alloc系统内存的次数降低函数开销和内存碎片产生几率虽然这种方式会造成一些冗余浪费但是这种浪费在大型系统应用中是微不足道的。 ◎Memcached的理论参数计算方式 影响 memcached 工作的几个参数有  常量REALTIME_MAXDELTA 60*60*24*30 最大30天的过期时间 conn_init()中的freetotal200 最大同时连接数 常量KEY_MAX_LENGTH 250 最大键长 settings.factor1.25 factor将影响chunk的步进大小 settings.maxconns1024 最大软连接 settings.chunk_size48 一个保守估计的keyvalue长度用来生成id1中的chunk长度1.2。id1的chunk长度等于这个数值加上item结构体的长度32即默认的80字节。 常量POWER_SMALLEST 1 最小classid1.2 常量POWER_LARGEST 200 最大classid1.2 常量POWER_BLOCK 1048576 默认slab大小 常量CHUNK_ALIGN_BYTES (sizeof(void *)) 保证chunk大小是这个数值的整数倍防止越界void *的长度在不同系统上不一样在标准32位系统上是4 常量ITEM_UPDATE_INTERVAL 60 队列刷新间隔 常量LARGEST_ID 255 最大item链表数这个值不能比最大的classid小 变量hashpower在1.1中是常量HASHPOWER 决定hashtable的大小 根据上面介绍的内容及参数设定可以计算出的一些结果 1、在memcached中可以保存的item个数是没有软件上限的之前我的100万的说法是错误的。 2、假设NewHash算法碰撞均匀查找item的循环次数是item总数除以hashtable大小由hashpower决定是线性的。 3、Memcached限制了可以接受的最大item是1MB大于1MB的数据不予理会。 4、Memcached的空间利用率和数据特性有很大的关系又与DONT_PREALLOC_SLABS常量有关。 在最差情况下有198个slab会被浪费所有item都集中在一个slab中199个id全部分配满。  ◎Memcached的定长优化 根据上面几节的描述多少对memcached有了一个比较深入的认识。在深入认识的基础上才好对它进行优化。 Memcached本身是为变长数据设计的根据数据特性可以说它是“面向大众”的设计但是很多时候我们的数据并不是这样的“普遍”典型的情况中一种是非均匀分布即数据长度集中在几个区域内如保存用户 Session另一种更极端的状态是等长数据如定长键值定长数据多见于访问、在线统计或执行锁。 这里主要研究一下定长数据的优化方案1.2集中分布的变长数据仅供参考实现起来也很容易。 解决定长数据首先需要解决的是slab的分配问题第一个需要确认的是我们不需要那么多不同chunk长度的slab为了最大限度地利用资源最好chunk和item等长所以首先要计算item长度。 在之前已经有了计算item长度的算法需要注意的是除了字符串长度外还要加上item结构的长度32字节。 假设我们已经计算出需要保存200字节的等长数据。 接下来是要修改slab的classid和chunk长度的关系。在原始版本中chunk长度和classid是有对应关系的现在如果把所有的chunk都定为200个字节那么这个关系就不存在了我们需要重新确定这二者的关系。一种方法是整个存储结构只使用一个固定的id即只使用199个槽中的1个在这种条件下就一定要定义DONT_PREALLOC_SLABS来避免另外的预分配浪费。另一种方法是建立一个hash关系来从item确定classid不能使用长度来做键可以使用key的NewHash结果等不定数据或者直接根据key来做hash定长数据的key也一定等长。这里简单起见选择第一种方法这种方法的不足之处在于只使用一个id在数据量非常大的情况下slab链会很长因为所有数据都挤在一条链上了遍历起来的代价比较高。 前面介绍了三种空间冗余设置chunk长度等于item长度解决了第一种空间浪费问题不预申请空间解决了第二种空间浪费问题那么对于第一种问题slab内剩余如何解决呢这就需要修改POWER_BLOCK常量使得每一个slab大小正好等于chunk长度的整数倍这样一个slab就可以正好划分成n个chunk。这个数值应该比较接近1MB过大的话同样会造成冗余过小的话会造成次数过多的alloc根据chunk长度为200选择1000000作为POWER_BLOCK的值这样一个slab就是100万字节不是1048576。三个冗余问题都解决了空间利用率会大大提升。 修改 slabs_clsid 函数让它直接返回一个定值比如 1 CODE: unsigned int slabs_clsid(size_t size) { return 1; } 修改slabs_init函数去掉循环创建所有classid属性的部分直接添加slabclass[1] CODE: slabclass[1].size 200;                //每chunk200字节 slabclass[1].perslab 5000;        //1000000/200 ◎Memcached客户端 Memcached是一个服务程序使用的时候可以根据它的协议连接到memcached服务器上发送命令给服务进程就可以操作上面的数据。为了方便使用memcached有很多个客户端程序可以使用对应于各种语言有各种语言的客户端。基于C语言的有libmemcache、APR_Memcache基于Perl的有Cache::Memcached另外还有Python、Ruby、Java、C#等语言的支持。PHP的客户端是最多的不光有mcache和PECL memcache两个扩展还有大把的由PHP编写的封装类下面介绍一下在PHP中使用memcached的方法 mcache扩展是基于libmemcache再封装的。libmemcache一直没有发布stable版本目前版本是1.4.0-rc2可以在这里找到。libmemcache有一个很不好的特性就是会向stderr写很多错误信息一般的作为lib使用的时候stderr一般都会被定向到其它地方比如Apache的错误日志而且libmemcache会自杀可能会导致异常不过它的性能还是很好的。 mcache扩展最后更新到1.2.0-beta10作者大概是离职了不光停止更新连网站也打不开了~_~只能到其它地方去获取这个不负责的扩展了。解压后安装方法如常phpize configure make make install一定要先安装libmemcache。使用这个扩展很简单 CODE: ?php $mc memcache();    // 创建一个memcache连接对象注意这里不是用new $mc-add_server(‘localhost’, 11211);    // 添加一个服务进程 $mc-add_server(‘localhost’, 11212);    // 添加第二个服务进程 $mc-set(‘key1′, ‘Hello’);    // 写入key1 Hello $mc-set(‘key2′, ‘World’, 10);    // 写入key2 World10秒过期 $mc-set(‘arr1′, array(‘Hello’, ‘World’));    // 写入一个数组 $key1 $mc-get(‘key1′);    // 获取’key1′的值赋给$key1 $key2 $mc-get(‘key2′);    // 获取’key2′的值赋给$key2如果超过10秒就取不到了 $arr1 $mc-get(‘arr1′);    // 获取’arr1′数组 $mc-delete(‘arr1′);    // 删除’arr1′ $mc-flush_all();    // 删掉所有数据 $stats $mc-stats();    // 获取服务器信息 var_dump($stats);    // 服务器信息是一个数组 ? 这个扩展的好处是可以很方便地实现分布式存储和负载均衡因为它可以添加多个服务地址数据在保存的时候是会根据hash结果定位到某台服务器上的这也是libmemcache的特性。libmemcache支持集中hash方式包括CRC32、ELF和Perl hash。 PECL memcache是PECL发布的扩展目前最新版本是2.1.0可以在pecl网站得到。memcache扩展的使用方法可以在新一些的PHP手册中找到它和mcache很像真的很像 CODE: ?php $memcache new Memcache; $memcache-connect(‘localhost’, 11211) or die (“Could not connect”); $version $memcache-getVersion(); echo “Server’s version: ”.$version.“n”; $tmp_object new stdClass; $tmp_object-str_attr ‘test’; $tmp_object-int_attr 123; $memcache-set(‘key’, $tmp_object, false, 10) or die (“Failed to save data at the server”); echo “Store data in the cache (data will expire in 10 seconds)n”; $get_result $memcache-get(‘key’); echo “Data from the cache:n”; var_dump($get_result); ? 这个扩展是使用php的stream直接连接memcached服务器并通过socket发送命令的。它不像libmemcache那样完善也不支持add_server这种分布操作但是因为它不依赖其它的外界程序兼容性要好一些也比较稳定。至于效率差别不是很大。 另外有很多的PHP class可以使用比如MemcacheClient.inc.phpphpclasses.org上可以找到很多一般都是对perl client API的再封装使用方式很像。 ◎BSM_Memcache 从C client来说APR_Memcache是一个很成熟很稳定的client程序支持线程锁和原子级操作保证运行的稳定性。不过它是基于APR的APR将在最后一节介绍没有libmemcache的应用范围广目前也没有很多基于它开发的程序现有的多是一些Apache Module因为它不能脱离APR环境运行。但是APR倒是可以脱离Apache单独安装的在APR网站上可以下载APR和APR-util不需要有Apache可以直接安装而且它是跨平台的。 BSM_Memcache是我在BS.Magic项目中开发的一个基于APR_Memcache的PHP扩展说起来有点拗口至少它把APR扯进了PHP扩展中。这个程序很简单也没做太多的功能只是一种形式的尝试它支持服务器分组。 和mcache扩展支持多服务器分布存储不同BSM_Memcache支持多组服务器每一组内的服务器还是按照hash方式来分布保存数据但是两个组中保存的数据是一样的也就是实现了热备它不会因为一台服务器发生单点故障导致数据无法获取除非所有的服务器组都损坏例如机房停电。当然实现这个功能的代价就是性能上的牺牲在每次添加删除数据的时候都要扫描所有的组在get数据的时候会随机选择一组服务器开始轮询一直到找到数据为止正常情况下一次就可以获取得到。 BSM_Memcache只支持这几个函数 CODE: zend_function_entry bsm_memcache_functions[] { PHP_FE(mc_get,          NULL) PHP_FE(mc_set,          NULL) PHP_FE(mc_del,          NULL) PHP_FE(mc_add_group,    NULL) PHP_FE(mc_add_server,   NULL) PHP_FE(mc_shutdown,     NULL) {NULL, NULL, NULL} }; mc_add_group函数返回一个×××其实应该是一个object我偷懒了~_~作为组IDmc_add_server的时候要提供两个参数一个是组ID一个是服务器地址ADDRORT。 CODE: /** * Add a server group */ PHP_FUNCTION(mc_add_group) { apr_int32_t group_id; apr_status_t rv; if (0 ! ZEND_NUM_ARGS()) { WRONG_PARAM_COUNT; RETURN_NULL(); } group_id free_group_id(); if (-1 group_id) { RETURN_FALSE; } apr_memcache_t *mc; rv apr_memcache_create(p, MAX_G_SERVER, 0, mc); add_group(group_id, mc); RETURN_DOUBLE(group_id); } CODE: /** * Add a server into group */ PHP_FUNCTION(mc_add_server) { apr_status_t rv; apr_int32_t group_id; double g; char *srv_str; int srv_str_l; if (2 ! ZEND_NUM_ARGS()) { WRONG_PARAM_COUNT; } if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, “ds”, g, srv_str, srv_str_l) FAILURE) { RETURN_FALSE; } group_id (apr_int32_t) g; if (-1 is_validate_group(group_id)) { RETURN_FALSE; } char *host, *scope; apr_port_t port; rv apr_parse_addr_port(host, scope, port, srv_str, p); if (APR_SUCCESS rv) { // Create this server object apr_memcache_server_t *st; rv apr_memcache_server_create(p, host, port, 0, 64, 1024, 600, st); if (APR_SUCCESS rv) { if (NULL mc_groups[group_id]) { RETURN_FALSE; } // Add server rv apr_memcache_add_server(mc_groups[group_id], st); if (APR_SUCCESS rv) { RETURN_TRUE; } } } RETURN_FALSE; } 在set和del数据的时候要循环所有的组 CODE: /** * Store item into all groups */ PHP_FUNCTION(mc_set) { char *key, *value; int key_l, value_l; double ttl 0; double set_ct 0; if (2 ! ZEND_NUM_ARGS()) { WRONG_PARAM_COUNT; } if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, “ss|d”, key, key_l, value, value_l, ttl) FAILURE) { RETURN_FALSE; } // Write data into every object apr_int32_t i 0; if (ttl 0) { ttl 0; } apr_status_t rv; for (i 0; i MAX_GROUP; i) { if (0 is_validate_group(i)) { // Write it! rv apr_memcache_add(mc_groups[i], key, value, value_l, (apr_uint32_t) ttl, 0); if (APR_SUCCESS rv) { set_ct; } } } RETURN_DOUBLE(set_ct); } 在mc_get中首先要随机选择一个组然后从这个组开始轮询 CODE: /** * Fetch a item from a random group */ PHP_FUNCTION(mc_get) {                char *key, *value NULL; int key_l; apr_size_t value_l; if (1 ! ZEND_NUM_ARGS()) { WRONG_PARAM_COUNT; } if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, “s”, key, key_l) FAILURE) { RETURN_MULL(); } // I will try … // Random read apr_int32_t curr_group_id random_group(); apr_int32_t i 0; apr_int32_t try 0; apr_uint32_t flag; apr_memcache_t *oper; apr_status_t rv; for (i 0; i MAX_GROUP; i) { try i curr_group_id; try try % MAX_GROUP; if (0 is_validate_group(try)) { // Get a value oper mc_groups[try]; rv apr_memcache_getp(mc_groups[try], p, (const char *) key, value, value_l, 0); if (APR_SUCCESS rv) { RETURN_STRING(value, 1); } } } RETURN_FALSE; } CODE: /** * Random group id * For mc_get() */ apr_int32_t random_group() { struct timeval tv; struct timezone tz; int usec; gettimeofday(tv, tz); usec tv.tv_usec; int curr usec % count_group(); return (apr_int32_t) curr; } BSM_Memcache的使用方式和其它的client类似 CODE: ?php $g1 mc_add_group();    // 添加第一个组 $g2 mc_add_group();    // 添加第二个组 mc_add_server($g1, ‘localhost:11211′);    // 在第一个组中添加第一台服务器 mc_add_server($g1, ‘localhost:11212′);    // 在第一个组中添加第二台服务器 mc_add_server($g2, ‘10.0.0.16:11211′);    // 在第二个组中添加第一台服务器 mc_add_server($g2, ‘10.0.0.17:11211′);    // 在第二个组中添加第二台服务器 mc_set(‘key’, ‘Hello’);    // 写入数据 $key mc_get(‘key’);    // 读出数据 mc_del(‘key’);    // 删除数据 mc_shutdown();    // 关闭所有组 ? APR_Memcache的相关资料可以在这里找到BSM_Memcache可以在本站下载。 ◎APR环境介绍 APR的全称Apache Portable Runtime。它是Apache软件基金会创建并维持的一套跨平台的C语言库。它从Apache httpd1.x中抽取出来并独立于httpd之外Apache httpd2.x就是建立在APR上。APR提供了很多方便的API接口可供使用包括如内存池、字符串操作、网络、数组、hash表等实用的功能。开发Apache2 Module要接触很多APR函数当然APR可以独立安装独立使用可以用来写自己的应用程序不一定是Apache httpd的相关开发。 ◎后记 这是我在农历丙戌年我的本命年的最后一篇文章由于Memcached的内涵很多仓促整理一定有很多遗漏和错误。感谢新浪网提供的研究机会感谢部门同事的帮助。 转载于:https://blog.51cto.com/dason/498955
http://www.zqtcl.cn/news/375748/

相关文章:

  • 石嘴山北京网站建设h5网站建设
  • 滨州区建设局网站中国建行官网首页
  • 网站建设服务网站网站建设销售实习
  • 网站注册都需要什么给装修公司做推广的网站
  • me域名的网站wordpress 扩展字段
  • 新开三端互通传奇网站企业推广方式有哪些
  • 怎么制作网站页面做理论的网站
  • 哪家公司做跳转网站wordpress 网页缩放
  • 小说网站建设的支柱深圳建设发展集团有限公司
  • 陕西高速公路建设网站做网站不用编程
  • wordpress网站秒开网站建设设计理念
  • html5 网站模板永久免费的仓库管理软件
  • 贵州网站seo厦门网站设计多少钱
  • 哈市哪里网站做的好合作网站seo
  • 找苏州网站建设网站维护提醒php文件
  • 哪些网站做推广效果好与市场营销有关的网站
  • 有什么网站可以做设计赚钱吗专业vi设计公司哪家强
  • 一般的网站是由什么语言做的网站建设怎么问问题
  • 开源系统 网站阿里云虚拟主机网站
  • 摄影师作品网站网站怎么做搜素引擎
  • 做网站定金是多少钱开网站建设公司心得
  • 网站不备案怎么做网页淘宝客电子商务的网站建设的可用性
  • 傻瓜自助建站软件怎样进网站空间服务器
  • 黑龙江网站建站建设wordpress 邮件
  • 免费发布信息网站有哪些豆芽网站建设
  • 无锡做网站优化公司互动营销用在哪些推广上面
  • 每一个网站都是响应式吗销售渠道策略
  • 凡科平台网站怎么建设广州网站建设信科网络
  • 网站建设公司的服务特点seo实战密码电子书
  • 网站开发保密协议范本北京市建设工程信息网查询