2013年2月17日星期日

在Ubuntu上安装并C++使用libmemcached




最近要用C++开发项目需要操作memcached,查找到libmemcached是专门为C/C++提供的memcached客户端决定用它。



1. 在命令行下: wget https://launchpad.net/libmemcached/1.0/1.0.7/+download/libmemcached-1.0.7.tar.gz 下载文件。



2. 输入:sudo apt-get install libevent-dev 安装libevent-dev (据说这个很重要,不然libmemcached就不能运行,最好安装)



3. 解压缩下载文件:sudu tar -zxvf libmemcached-1.0.7.tar.gz



4. 编译:  ./configure



5. 输入:make  

    这里很可能会出下面错误:

libmemcached/backtrace.cc: In function 'void custom_backtrace()':
libmemcached/backtrace.cc:57:6: sorry, unimplemented: Graphite loop optimizations can only be used if the libcloog-ppl0 package is installed

  解决办法是修改Makefile,查找并去掉 floop-parallelize-all(应该有两处需要去掉),看了网上资料说是去掉后表示不使用Graphite loop 优化。

   成功编译。



6. 安装:make install



7. 编写文件 MemCachedClient.h 输入下面内容:


#ifndef MEMCACHEDCLIENT

#define MEMCACHEDCLIENT

#include <libmemcached/memcached.h>
#include<iostream>
#include<string>
#include<time.h>
using std::string;
using std::cout;
using std::endl;

class MemCachedClient
{
    public:
        ~MemCachedClient()
        {
            memcached_free(memc);
        };

        MemCachedClient()
        {
            memcached_return rc;                                                                  
            memcached_server_st *server = NULL;

            memc = memcached_create(NULL);
           
            server =memcached_server_list_append(server, "192.168.45.144", 11211, &rc);

            rc=memcached_server_push(memc,server);                                        


            if (MEMCACHED_SUCCESS != rc)                                                              
            {
                 cout <<"memcached_server_push failed! rc: " << rc << endl;
            }

            memcached_server_list_free(server);
        };


        int Insert(const char* key, const char* value,time_t expiration = 3)
        {
            if (NULL == key || NULL == value)
            {
                return -1;
            }

            uint32_t flags = 0;

            memcached_return rc;

            rc = memcached_set(memc, key, strlen(key),value, strlen(value), expiration, flags);

            // insert ok
            if (MEMCACHED_SUCCESS == rc)
            {
                return 1;
            }
            else
            {
                return 0;
            }
        };
       
       
        string Get(const char* key)
        {
             if (NULL == key)
             {
                return "";
             }

            uint32_t flags = 0;

            memcached_return rc;

            size_t value_length;
            char* value = memcached_get(memc, key, strlen(key), &value_length, &flags, &rc);

            // get ok
            if(rc == MEMCACHED_SUCCESS)
            {
                return value;
            }

            return "";
        };

    private:
        memcached_st* memc;                                                                
};

#endif



    再编写 test.cc  文件


#include<iostream>
#include"MemCachedClient.h"
using std::cout;
using std::endl;

int main()
{
    MemCachedClient mc;

    mc.Insert("kingcat","value123");
    cout << mc.Get("kingcat") << endl;
    return 1;
};



8. 用g++编译: g++ test.cc -o test -lmemcached (一定别把库链接忘了否则编译不通过)



9. 执行./test

    执行时会报错:libmemcached.so.10: cannot open shared object file: No such file or director

    原因是 libmemcached.so.10 被安装到了 /usr/local/lib下,而共享库默认位置是 /usr/lib 网上说可以把目录 /usr/local/lib 设置到环境变量,但我设置完后不起作用。

    于是用建立一个链接: ln /usr/local/lib/libmemcached.so.10  /usr/lib/libmemcached.so.10

    重新执行,顺利通过!


使用libmemcached提速memcached的操作



memcachedCacheRailsRubyGoogle
libmemcached是一个基于C/C++实现的memcached client library,memcached gem给这个library提供了ruby封装,根据原作者的性能评测,它比目前常用的memcache-client gem最多要快150倍。

因为对memcache的操作在rails处理整个http请求中所占时间比例并不多,而且JavaEye网站大部分是做cache读取操作,于是做了2个试验测试一下2者对于提升整个网站的性能到底有多少帮助:
1. 测试访问JavaEye网站的首页,在这个请求中只有一个缓存读取操作,使用ab简单地跑单次请求10000次:
使用memcache-client,平均完成每个请求是9.247 ms
使用memcached,平均完成每个请求是8.982 ms

对于整体性能提升在3%左右

2. 测试访问某个论坛版面的页面,整个请求有超过60次的缓存读取操作,使用ab简单跑单次请求1000次:
使用memcache-client,平均完成每个请求是95.872 ms
使用memcached,平均完成每个请求是91.591 ms

对于整体性能提升在5%左右

从试验可以看出,对于整体性能还是有一定提高的。JavaEye用的cache插件是cache_fu,它只支持memcache-client,不过通过简单的修改就可以支持这个新gem,我提交了一个补丁,有兴趣的话可以从这里下载到补丁。

--分割线--
cache_fu的作者已经将这个补丁添加到代码树,可以从github checkout:  http://github.com/defunkt/cache_fu/tree/master

libmemcached使用(c 客户端连接 memcached) 2011-07-14 15:07:13



libmemcached 下载地址。https://launchpad.net/libmemcached/


//g++ -o testmemcached testmemcached.cpp -lmemcached


#include <iostream>
#include <string>
#include <libmemcached/memcached.h>

using namespace std;

int main(int argc,char *argv[])
{
   //connect server

   memcached_st *memc;
   memcached_return rc;
   memcached_server_st *server;
   time_t expiration;
   uint32_t flags;

   memc = memcached_create(NULL);
   server = memcached_server_list_append(NULL,"localhost",11211,&rc);
   rc=memcached_server_push(memc,server);
   memcached_server_list_free(server);

   string key = "key";
   string value = "value";
   size_t value_length = value.length();
   size_t key_length = key.length();


   //Save data

   rc=memcached_set(memc,key.c_str(),key.length(),value.c_str(),value.length(),expiration,flags);
   if(rc==MEMCACHED_SUCCESS)
   {
       cout<<"Save data:"<<value<<" sucessful!"<<endl;
   }

   //Get data

   char* result = memcached_get(memc,key.c_str(),key_length,&value_length,&flags,&rc);
   if(rc == MEMCACHED_SUCCESS)
   {
       cout<<"Get value:"<<result<<" sucessful!"<<endl;
   }

   //Delete data

   rc=memcached_delete(memc,key.c_str(),key_length,expiration);
   if(rc==MEMCACHED_SUCCESS)
   {
       cout<<"Delete key:"<<key<<" sucessful!"<<endl;
   }

   //free

   memcached_free(memc);
   return 0;
}



在下载客户端 libmemcached-0.50/tests 下有测试列子,也可参考。比如cpp_example.cc

memcached简单的使用教程


这是工程应用性质的文章:memcached是数据快速存取的工具,wikipedia和facebook就用了它提高速度。具体情况可以wiki.

c/c++里面使用memcached本来是很简单的事情,但就是没有人说,非要我去读libmemcache的实例代码才行,manpage也没有说清楚(但还不得不看)。如果要使用memcached,步骤如下:

1 安装memcached
  你可以去官网 上下载,也可以sudo apt-get install memcached
2 测试你刚才的安装,
  比如运行 memcached -d -m 64 -l 127.0.0.1 -p 12300
  -d表示后台, -m 64表示使用64MB内存,-l 127.0.0.1 -p 12300 表示监听127.0.0.1:12300端口
  没有任何消息就是运行成功
3 安装libmemcached
  这个就不要apt了,因为没有精确的libmemcached包,乖乖去官网 下载吧。这个东西还需要libevent,这个没法apt,libevent安装的时候最好装到/usr/lib里面,不然libmemcached还不能运行。
4 测试libmemcached
  找一个文件比如test,然后运行memcp test --servers=127.0.0.1:12300
  这样就把test拷贝到memcached管理的内存里面了。
  memcat test --servers=127.0.0.1:12300
  看看是不是刚才的那个文件。

该装的全装了,我们可以改代码了。
(以下引自:wikipedia)

将纯粹使用数据库查询的程式码加上memcached支援是很简单的,假设这是原来的程式码:

function get_foo (int userid) {

   result = db_select("SELECT * FROM users WHERE userid = ?", userid);

   return result;

}

加上memcached的快取机制后:

function get_foo (int userid) {

    result = memcached_fetch("userrow:" + userid);

    if (!result) {

        result = db_select("SELECT * FROM users WHERE userid = ?", userid);

        memcached_add("userrow:" + userid,  result);

    }

    return result;

}

(wikipedia 引述完毕)

但我们需要初始化,而且需要c/c++的表述形式:
首先修改makefile,在LIBS里面加上-lmemcached,比如原来 gcc test.c,现在 gcc test.c -lmemcached。这个库就是libmemcached提供的。
然后添加#include<libmemcached/memcached.h>,这个文件也是libmemcached提供的。
主函数里面需要添加:
    memcached_st *memc;
    uint32_t  flags;
    memcached_return rc;
    memcached_server_st *servers;
    memc= memcached_create(NULL);

    servers= memcached_servers_parse("127.0.0.1:12300");
    memcached_server_push(memc, servers);
    memcached_server_list_free(servers);
    memcached_behavior_set(memc, MEMCACHED_BEHAVIOR_BINARY_PROTOCOL, 0);
   
这就是初始化的全部代码,可以设置很多东西,不过我现在只要一个服务器,就看一句话:
    servers= memcached_servers_parse("127.0.0.1:12300");
这就是指明要连接到位于127.0.0.1监听12300端口的memcached程序. 其他都不管。

我最后用一段修改后的代码介绍两个函数,memcached_get和memcached_set,都可以man出来。

        char * result_str; /*这个就是查询的结果*/
        std::string query="要查询的数据名";

        result_str= memcached_get(memc, query.c_str(), strlen(query.c_str()),
                &result_str_length, &flags, &rc);

        if (rc == MEMCACHED_SUCCESS)
        {
            printf("%s",result_str);
            free(result_str); /*一定要free掉 memcached传回的数据*/

            continue;
        }
        else if (rc == MEMCACHED_NOTFOUND)
        {
            /*读取数据*/
            ...
            rc= memcached_set(memc, query.c_str(), strlen(query.c_str()),
                    str_result.c_str(), strlen(str_result.c_str()),
                    0, 0);
        }

这东西设计得还是很好用的,libmemcached好像是面向对象c语言风格的东西,好不好用就让我们拭目以待吧。
/////////////////////////////////////////////////////////////////////////////////
完整的例子:
#include <stdio.h>
#include <stdlib.h>
#include <libmemcached/memcached.h>
#include <iostream>
#include <string>
using namespace std;
int main(int argc, char *argv[])
{
 memcached_server_st *servers = memcached_servers_parse("127.0.0.1:22222");
 memcached_st *memc = memcached_create(NULL);
 memcached_server_push(memc, servers);
 memcached_server_list_free(servers);
 memcached_behavior_set(memc, MEMCACHED_BEHAVIOR_BINARY_PROTOCOL, 0);
 string strKey = "1";
 string strValue = "hello.world";
 memcached_return rc = memcached_set(memc, strKey.c_str(), strlen(strKey.c_str()), strValue.data(), strValue.size(), 0, 0);
 if (rc == MEMCACHED_SUCCESS)
 {
  printf("memcached_set ok! \n");
  size_t ValueLength = 0;
  uint32_t flags;
  char *pValue = memcached_get(memc, strKey.c_str(), strlen(strKey.c_str()), &ValueLength, &flags, &rc);
  if (rc == MEMCACHED_SUCCESS)
  {
   printf("memcached_get key=%s value=%s \n", strKey.c_str(), pValue);
   free(pValue);
  }
 }
 memcached_free(memc);
 return 0;
}
////////////////////////////////////////////////////////////////////////////////////////////
memcached 命令
memcached 命令



comments 类型



1. storage commands: set add replace

2. retrieval commands: get

3. other commands: incr delete



error strings

1. "ERROR\r\n"

客户端发送了一个不存在的command



2. "CLIENT_ERROR <error>\r\n"

客户端的错误:客户端发送的数据或者格式方面有问题。 <error>表示具体错误的原因。



3. "SERVER_ERROR <error>\r\n"

服务器端的错误:服务器一系列的错误阻止了命令的执行。<error>表示具体错误的原因。当发生此错误时。服务器发送完了"SERVER_ERROR <error>\r\n"之后将会关闭connection。(这是唯一的关闭connection的情况)



descriptions of individual commands



1. storage commands

首先客户端向服务器按照如下格式发送命令行:

<command name> <key> <flags> <exptime> <bytes>\r\n



a) <command name> 可以是"set", "add", "replace"。

"set"表示按照相应的<key>存储该数据。

"add"表示按照相应的<key>添加该数据,但是如果该<key>已经存在则会操作失败。

"replace"表示按照相应的<key>替换数据,但是如果该<key>不存在则操作失败



b) <key> 客户端需要保存数据的key。



c) <flags> 是一个16位的无符号的整数(以十进制的方式表示)。该标志将和需要存储的数据一起存储,并在客户端get数据时返回。客户可以将此标志用做特殊用途,此标志对服务器来说是不透明的。



d) <exptime> 过期的时间。如果该数值为0表示存储的数据永远不过时(但是,该数据有可能被其他项所替换掉。因为服务器采用了LRU(最近最久没有使用)的算法替换)。如果非0(unix时间或者距离此时的秒数),当过期后,服务器可以保证用户得不到该数据(以服务器时间为标准)。



e) <bytes> 需要存储的字节数(不包含最后的"\r\n"),当用户希望存储空数据时,<bytes>可以为0



f) 最后客户端需要加上"\r\n"作为"命令头"的结束标志。



<data block>\r\n

紧接着"命令头"结束之后就要发送数据块(即希望存储的数据内容),最后加上"\r\n"作为此次通讯的结束。



reply

当以上数据发送结束之后,服务器将返回一个应答。可能有如下的情况:



a) "STORED\r\n"

表示存储成功



b) "NOT_STORED\r\n"

表示存储失败,但是该失败不是由于错误。通常这是由于"add"或者"replace"命令本身的要求所引起的,或者该项在删除队列之中(见delete命令)。



2 retrieval command

获取数据的格式:



get <key>*\r\n



a) <key>* 表示一个或者多个key(以空格分开)



b) "\r\n" 命令头的结束



reply

服务器端将返回0个或者多个的数据项。每个数据项都是由一个文本行和一个数据块组成。当所有的数据项都接收完毕将收到"END\r\n"



每一项的数据结构:

VALUE <key> <flags> <bytes>\r\n

<data block>\r\n



a) <key> 希望得到存储数据的key



b) <falg> 发送set命令时设置的标志项



c) <bytes> 发送数据块的长度(不包含"\r\n")



d) "\r\n" 文本行的结束标志



e) <data block> 希望接收的数据项。



f) "\r\n" 接收一个数据项的结束标志。



如果有些key出现在get命令行中但是没有返回相应的数据,这意味着服务器中不存在这些项,这些项过时了,或者被删除了。



3. delete

delete 命令格式:

delete <key> <time>\r\n



a) <key> 需要被删除数据的key



b) <time> 客户端希望服务器将该数据删除的时间(unix时间或者从现在开始的秒数)



c) "\r\n" 命令头的结束



reply



a) "DELETED\r\n" 删除成功



b) "NOT_FOUND\r\n" 需要删除的key不存在



ps:"flush_all" command 可以所有项都无效。



4. increment/decrement



command "incr" "decr" 是用来改变数据项数值的命令。设置数据项的数据被认为是一个10进制的无符号的整数。如果当前设置的值不能够被转换则认为是0.如果该数据项的key不存在,则执行命令失败!



命令格式:



incr <key> <value>\r\n

or

decr <key> <value>\r\n



a) <key> 数据项的key



b) <value> 用户希望增加/减少的数据的数值.该数值是一个32位十进制的无符号整形变量。



c) "\r\n" 命令行结束标志



reply



a) "NOT_FOUND\r\n" 没有找到需要操作的项。



b) "<value>\r\n" <value>数据项有效期的最新剩余时间。



注意:

a) 如果一个数据项的有效期被设置为0,这时使用decr命令是无法减少数据。



b) 如果要执行 incr key -1 的操作不会有什么问题,结果和你希望的一样。但是,执行decr -1时的结果一定会让你觉得很意外,因为它的结果无论key的数据是什么结果的都是0.原因是:在这两个命令的执行过程中都是吧-1当做一个无符号的整形处理的。



c) 执行decr命令时数据的长度不会随之而减小,而是在返回数据的后面填补空格。但是执行incr命令越界后会自动的增加数据的位数。





5. 其他命令



"stats": 用来查询服务器提供的内部数据。



stats <args>\r\n

查询特定的数据



reply



STAT <name> <value>\r\n

...

END\r\n



"quit"

"quit\r\n": 退出程序


///////////////////////////////////////////////////////////////////////////////////////////////////


对于 Memcached 缓存系统的基本介绍、安装以及应用 之前有一编文章说过,下面主要是对使用Memcached系统的基本命令以及协议作个简单的分析,个人学习的笔记,呵呵。还希望能给需要的筒子一点参考:

一、Memcache面向对象的常用接口包括:
Memcache::connect -- 打开一个到Memcache的连接
Memcache::pconnect -- 打开一个到Memcache的长连接
Memcache::close -- 关闭一个Memcache的连接
Memcache::set -- 保存数据到Memcache服务器上
Memcache::get -- 提取一个保存在Memcache服务器上的数据
Memcache::replace -- 替换一个已经存在Memcache服务器上的项目
Memcache::delete -- 从Memcache服务器上删除一个保存的项目
Memcache::flush -- 刷新所有Memcache服务器上保存的项目(类似于删除所有的保存的项目)
Memcache::getStats -- 获取当前Memcache服务器运行的状态

For More: http://cn.php.net/memcache

二、查看系统的运行状态:


pid               Process id of this server process (memcache服务器的进程ID)
uptime            Number of seconds this server has been running (服务器已经运行的秒数)
time              Current UNIX time according to the server (服务器当前的UNIX时间)
version           Version string of this server (memcache版本)
pointer_size      Current system pointer 当前操作系统的指针大小(32位系统一般是32bit)
rusage_user       Accumulated user time for this process (该进程累计的用户时间(秒:微妙))
rusage_system     Accumulated system time for this process (该进程累计的系统时间(秒:微妙))
curr_items        Current number of items stored by the server (服务器当前存储的内容数量)
total_items       Total number of items stored by this server ever since it started (服务器启动以来存储过的内容总数)
bytes             Current number of bytes used by this server to store items (服务器当前存储内容所占用的字节数)
curr_connections  Number of open connections (当前打开着的连接数量)
total_connections Total number of connections opened since the server started running (服务器运行以来接受的连接总数)
connection_structures Number of connection structures allocated by the server (服务器分配的连接结构的数量)
cmd_get             Cumulative number of retrieval requests (get命令(获取)总请求次数)
cmd_set             Cumulative number of storage requests (set命令(保存)总请求次数)
get_hits            Number of keys that have been requested and found present (请求成功的总次数)
get_misses          Number of items that have been requested and not found (请求失败的总次数)
threads             Current number of thread (当前线程数)
bytes_read          Total number of bytes read by this server from network (服务器从网络读取到的总字节数)
bytes_written       Total number of bytes sent by this server to network (服务器向网络发送的总字节数)
limit_maxbytes      Number of bytes this server is allowed to use for storage. (服务器在存储时被允许使用的字节总数)
evictions           Number of valid items removed from cache to free memory for new items (为获取空闲内存而删除的items数(分配给memcache的空间用满后需要删除旧的items来得到空间分配给新的items))

其中,最关注最多的几个参数:
uptime:是memcached运行的秒数。
cmd_get:是查询缓存的次数。
cmd_get/uptime 结果是平均每秒请求缓存的次数——结果值越大,说明Memcached的利用率越高,站点的访问量大,如果太低,用文件系统缓存就可以了,根本不会体现出使用memcached的强大性能。
cmd_set:是设置key=>value的次数。整个memcached是个大hash,用cmd_get没有找到的内容,就会调用一下cmd_set写进缓存里。
get_hits:是缓存命中的次数。所谓的命中率 = get_hits/cmd_get * 100%。
get_misses:是缓存未命中的次数。get_misses加上get_hits就等于cmd_get。
stats:显示服务器信息、统计数据等
stats reset:清空统计数据

stats slabs:显示各个slab的信息,包括chunk的大小、数目、使用情况等
stats items:显示各个slab中item的数目和存储时长(最后一次访问距离现在的秒数)
quit:退出

三、利用shell命令操作Memcached
1、数据存储(key为wan,value为123)

2、数据取回

3、替换数据(将以wan为key存储的值替换为122)

4、数值增加 1

5、数值减少 2

6、数据删除

7、查看Memcached当时状态
printf "stats\r\n" | nc 127.0.0.1 11211
8、查看Memcached实时状态

watch "printf 'stats\r\n' | nc 127.0.0.1 11211"



Memcached protocol 中英文档可以参考:

http://blog.s135.com/book/memcached/

四. 查看slabs的使用状况
使用memcached的创造着Brad写的名为 memcached-tool 的Perl脚本,可以方便地获得slab的使用情况(它将memcached的返回值整理成容易阅读的格式)。可以从下面的地址获得脚本:
http://code.sixapart.com/svn/memcached/trunk/server/scripts/memcached-tool
[root@localhost html]# vim memcached-tool
[root@localhost html]# chmod +x memcached-tool
[root@localhost html]# ./memcached-tool 127.0.0.1:11211
  #  Item_Size   Max_age  1MB_pages Count   Full?
  1      80 B        0 s               1           0      no
  2     104 B       12175 s         1           1      no
  3     176 B    1339587 s       33       196567  yes

各列的含义:
#: slab class编号
Item_Size: Chunk大小
Max_age: LRU内最旧的记录的生存时间
1MB_pages: 分配给Slab的页数
Count: Slab内的记录数
Full?: Slab内是否含有空闲chunk

五. 也可以图形化监控 Memcached 的运行状态
http://livebookmark.net/journal/2008/05/21/memcachephp-stats-like-apcphp/
是一个PHP源文件,只需要修改源码中的用户名、密码以及数组$MEMCACHE_SERVERS 就可以了。

我的apache php mysql libmemcached memcached 安装实践


参考http://www.hao32.com/webserver/313.html
/sbin/ldconfig 这个命令在安装了新的动态链接库后运行一下,避免依赖此链接库的程序找不到它。
安装apache
wget "http://apache.freelamp.com/httpd/httpd-2.2.15.tar.gz"
tar -xzf httpd-2.2.15.tar.gz
cd httpd-2.2.15
./configure --prefix=/home/zhaigy/apache_n --enable-so
make
make install
安装php
wget "http://cn.php.net/distributions/php-5.3.2.tar.gz"
tar -xzf php-5.3.2.tar.gz
cd php-5.3.2
./configure --prefix=/home/zhaigy/php_n --with-apxs2=/home/webadm/bin/apxs
make
make install
修改httpd.conf
user zhaigy
group zhaigy
LoadModule php4_module modules/libphp4.so (这一项在装完php后会自动添加)
AddType application/x-httpd-php .php (这一项手工添加)
chown -R zhaigy.zhaigy /home/zhaigy/apache_n
启动apache
cd /home/zhaigy/apache_n
./bin/apachectl start
测试
netstat -npl
浏览器打开首页测试
查看phpinfo信息 (可获知:Configuration File (php.ini) Path /home/zhaigy/php_n/lib )
给php添加mysql扩展
cd /home/zhaigy/php-5.3.2/ext/mysql
/home/zhaigy/php_n/bin/phpize
./configure --help
./configure --with-php-config=/home/zhaigy/php_n/bin/php-config --with-mysql="/home/zhaigy/mysql-5.1.22-rc-linux-x86_64-glibc23"
或者./configure --with-php-config=/home/zhaigy/php_n/bin/php-config --with-mysql="mysqlnd"
make
make install 显示 Installing shared extensions:     /home/zhaigy/php_n/lib/php/extensions/no-debug-non-zts-20090626/
cd /home/zhaigy/php_n/
mkdir modules
cp /home/zhaigy/php_n/lib/php/extensions/no-debug-non-zts-20090626/mysql.so ./modules
编写php.ini 参考 http://lamp.linux.gov.cn/PHP/php-5_2-ini.html
修改extension_dir = "/home/zhaigy/php_n/modules"
extension = mysql.so
chown -R zhaigy.zhaigy /home/zhaigy/php_n
重启apache
cd /home/zhaigy/apache_n
./bin/apachectl restart
netstat -npl|grep httpd
php安装源码中不包含的其它扩展,例如 memcached
################################
这个可以不安装
安装 memcached
wget "http://memcached.googlecode.com/files/memcached-1.4.5.tar.gz"
tar -xzvf memcached-1.4.5.tar.gz
cd memcached-1.4.5
./configure --prefix=/home/zhaigy/memcached_n --enable-64bit
make
make install
################################
需要先安装libmemcached
cd /home/zhaigy
wget "http://launchpad.net/libmemcached/1.0/0.42/+download/libmemcached-0.42.tar.gz"
tar -xzf libmemcached-0.42.tar.gz
cd libmemcached-0.42
./configure --prefix=/home/zhaigy/libmemcached_n --without-memcached
--非常的痛苦啊,编译安装在这里总是失败,从网上找的解决方法也不能用
痛苦的3小时
--
无意中使用了旧的版本,发现编译错误少了几个,嗯,找更旧的
cd /home/zhaigy
wget "http://launchpadlibrarian.net/33299675/libmemcached-0.28.tar.gz"
tar xzvf libmemcached-0.28.tar.gz
cd libmemcached-0.28
./configure --prefix=/home/zhaigy/libmemcached_n --without-memcached
make
make install
--这次比较顺利,估计是因为机器上的gcc版本低吧

cp /home/zhaigy/php_n
./bin/pecl -h
./bin/pecl download memcached
tar -xf memcached-1.0.2.tar
cd memcached-1.0.2
/home/zhaigy/php_n/bin/phpize
./configure --with-php-config=/home/zhaigy/php_n/bin/php-config --with-libmemcached-dir="/home/zhaigy/libmemcached_n"
make 这里也遇到编译错误
降低版本
/home/zhaigy/php_n/bin/pecl download memcached-1.0.0
tar -xvf memcached-1.0.0.tar
cd memcached-1.0.0
/home/zhaigy/php_n/bin/phpize
./configure --with-php-config=/home/zhaigy/php_n/bin/php-config --with-libmemcached-dir="/home/zhaigy/libmemcached_n"
make
make install
--顺利
安装到php
cd /home/zhaigy/php_n/
mkdir modules
cp /home/zhaigy/php_n/lib/php/extensions/no-debug-non-zts-20090626/memcached.so ./modules
php.ini中添加
extension = memcached.so
cd /home/zhaigy/apache_n
./bin/apachectl restart
从phpinfo中查看,正常

memcache 服务器端分析--libevent队列 线程模型


目前网上关于memcached的分析主要是内存管理部分,下面对memcached的线程模型做下简单分析 

有不对的地方还请大家指正,对memcahced和libevent不熟悉的请先google之 
先看下memcahced启动时线程处理的流程 

memcache 服务器端分析--libevent队列 线程模型 - zhengjunwei2007 - 我的博客
 
memcached的多线程主要是通过实例化多个libevent实现的,分别是一个主线程和n个workers线程 
无论是主线程还是workers线程全部通过libevent管理网络事件,实际上每个线程都是一个单独的libevent实例 
主线程负责监听客户端的建立连接请求,以及accept 连接 
workers线程负责处理已经建立好的连接的读写等事件 
先看一下大致的图示: 
memcache 服务器端分析--libevent队列 线程模型 - zhengjunwei2007 - 我的博客

linux软件开发常见安装(JDK,JBOSS,ACTIVEMQ,libevent,memcached)



ActiveMQLinuxmemcachedJBossJDK 
一、软件安装包清单(存放目录/usr/tmp) 
1.jdk-6u22-linux-x64.bin 
2.jboss-5.1.0.GA.zip    
3.apache-activemq-5.4.1-bin.tar 
4.libevent-2.0.6-rc.tar  
5.memcached-1.4.5.tar 
二、安装 
1. 安装JDK 
a)cp  /usr/tmp/jdk-6u22-linux-x64.bin  /usr 
b)cd  /usr 
c)chmod  755  jdk-6u22-linux-x64.bin 
d)./jdk-6u22-linux-x64.bin 
e)环境变量配置 
vi  /etc/profile (注:文件末添加以下三行) 
export  JAVA_HOME=/usr/jdk1.6.0_22 
export  PATH=$JAVA_HOME/bin:$PATH 
export  CLASSPATH=.:$JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar 
注:1.查看jdk版本 java –version 
    2.查看环境变量 echo $JAVA_HOME , echo $PATH 
3.有时候profile环境变量不会即刻生效,则执行source profile 
2. 安装jboss 
a)cp  /usr/tmp/jboss-5.1.0.GA.zip  /usr 
b)cd  /usr 
c)unzip  jboss-5.1.0.GA 
d) 修改外网访问配置 
vi server.xml(修改以下内容) 
修改前 
<Connector protocol="HTTP/1.1" port="8080" address="${jboss.bind.address}" 
connectionTimeout="20000" redirectPort="8443" /> 
修改后 
<Connector protocol="HTTP/1.1" port="8080" address="0.0.0.0" 
connectionTimeout="20000" redirectPort="8443" /> 
e)使用命令netstat -nlp 查看一下。 
Local Address 更改为 0.0.0.0:8080 而不是以前的127.0.0.1:8080 
f)启动服务: 
./run.sh & 
g)测试:wget http://10.100.120.185:8080/ 
h)关闭服务: 
./shutdown.sh –S 
3. 安装activemq 
a) cp /usr/tmp/apache-activemq-5.4.1-bin.tar  /usr 
b)cd  /usr 
c)tar  vxf  apache-activemq-5.4.1-bin.tar 
d)cd  /usr/apache-activemq-5.4.1/bin 
e)./activemq start & 
f)测试:wget  http://10.100.120.185:8161/admin  下载成功 
g)设置开机启动服务 
vi  /etc/rc.d/rc.local(文件末追加以下内容) 
/usr/apache-activemq-5.4.1/bin/activemq start & 
4. 安装libevent 
a) cp /usr/tmp/libevent-2.0.6-rc.tar /usr/ 
b)cd  /usr 
c)tar  vxf  libevent-2.0.6-rc.tar 
d)cd  libevent-2.0.6-rc 
e)./configure  -prefix=/usr/local/ 
make 
make install 
f)查看libevent:ls -al /usr/local/lib | grep libevent 
5. 安装memcached 
a)cp /usr/tmp/memcached-1.4.5.tar /usr/ 
b)cd  /usr 
c)tar  vxf  memcached-1.4.5.tar 
d)cd  memcached-1.4.5 
e) ./configure -with-libevent=/usr/loacl/ 
make 
make install 
f).查看是否安装好:ls -al /usr/local/bin/mem* 
g)cd /usr/local/bin/ 
./memcached -d -m 10 -u root -l 10.100.120.185 -p 11211 -c 256 -P /tmp/memcached.pid 
h)参数说明 
-d 以守护程序(daemon)方式运行 memcached; 
-m 设置 memcached 可以使用的内存大小,单位为 M; 
-u 指定用户,如果当前为 root 的话,需要使用此参数指定用户。 
-l 设置监听的 IP 地址,如果是本机的话,通常可以不设置此参数; 
-p 设置监听的端口,默认为 11211,所以也可以不设置此参数; 
-c选项是最大运行的并发连接数,默认是1024,这里设置了256,按照你服务器的负载量来设定, 
-P是设置保存memcache的pid文件,这里是保存在 /tmp/memcached.pid, 
注:查看详细参数说明命令man memcached 
三、安装中常见问题及处理方法 
1.  JDK安装好后,用echo $JAVA_HOME查看,并用java –version查看JDK版本是否正确,如果未生效,有时候profile环境变量不会即刻生效,则先执行source profile 
2.  jboss解压后即可启动使用,但如果只能访问http://127.0.0.1:8080,但不能用外网地址访问(如:http://172.16.46.100:8080),则需修改配置文件(/$JBOSS_HOME$/ server/default/deploy/jbossweb.sar/server.xml)如下所示: 
vi server.xml(修改以下内容) 
修改前 
<Connector protocol="HTTP/1.1" port="8080" address="${jboss.bind.address}" 
connectionTimeout="20000" redirectPort="8443" /> 
修改后 
<Connector protocol="HTTP/1.1" port="8080" address="0.0.0.0" 
connectionTimeout="20000" redirectPort="8443" /> 
3.  如果启动jboss异常且报linux jboss java.net.UnknownHostException异常则需修改本地hosts配置(/etc/hosts)如下: 
    127.0.0.1 EPS-01  localhost.localdomain localhost 
::1  localhost6.localdomain6 localhost6 
注:此处EPS-01是本机的主机名称 
4. 如果启动activemq后wget http://172.16.48.100:8161/admin不正常,查看日志如下: 
tail –f /usr/apache-activemq-5.4.1/data/activemq.log 
java.io.EOFException: Chunk stream does not exist at page:异常解决办法: 
这个错误是由于 5.4.1 这个版本引入了 延迟发送的功能引起的 
修改%ActiveMQ_HOME%/conf/activemq.xml 文件, 在<broker>中添加 schedulerSupport="false" 属性,禁掉scheduler功能即可. 
schedulerSupport="false" 
brokerName="localhost" 
dataDirectory="${activemq.base}/data" 
destroyApplicationContextOnStop="true"> 
<!-- ...... --> 
</broker> 
5.  如果memcached启动不正常使用以下方式处理 
cd /usr/local/bin 
ldd memcached(查看libevent文件的软链接如不正常重新做软链接,示例如下所示 
libevent-2.0.so.2 => not found 
libpthread.so.0 => /lib64/libpthread.so.0 (0x0000003f00e00000) 
libc.so.6 => /lib64/libc.so.6 (0x0000003f00200000) 
/lib64/ld-linux-x86-64.so.2 (0x0000003eff200000) 
ls  /usr/local/lib/libevent*(查看自己安装的libevent库文件) 
libevent-2.0.so.2 -> libevent-2.0.so.2.0.0(库文件存在) 
重做软连接 
ln -s /usr/local/lib/libevent-2.0.so.2 /lib64/libevent-2.0.so.2 
h)测试:telnet 10.100.120.185 11211 
i)结束memcache进程 
more  /tmp/memcached.pid(如进行号为:25585) 
kill  25585 
重新 telnet 10.100.120.185 11211 连接失败 
四、常用linux命令 
1. ps aux|grep active(查看进程状态) 
2. scp -r root@10.100.120.158:/usr/tmp/* /usr/tmp/(局域网A、B机器间拷贝文件) 
3. ln -s /usr/local/lib/libevent-2.0.so.2 /lib64/libevent-2.0.so.2(软连接) 
4. ssh -l root 172.16.46.100(用ssh登录) 
5.locate查询文件数据库,如:locate jdk,如果locate不能用,要更新下,用:updatedb命令。 
6.查看环境变量 echo $JAVA_HOME , echo $PATH 

libmemcached 安装使用

什么是 libmemcached ?
顾名思义,libmemcached 是一个 memcached 的库,客户端库,C 和 C++ 语言实现的客户端库,官方网站: http://tangent.org/552/libmemcached.html

需要注意的是, libmemcached 不是 libmemcache ,它们是两个不同的客户端库,前者现在的开发比较活跃,后者已经有很长时间没有更新了。

memcached 是一个高性能、分布式的内存对象缓存系统,通过内存的数据缓存来降低对数据库的访问,从而提高动态内容应用网站的速度。memcached 官方开发释放出来的,只是应用的服务器端程序,它发布了服务器端的连接读写协议,客户端的实现,则根据动态内容应用网站使用的动态脚本的不同,而有多种,具体列表,可查看官方网站。

libmemcached 就是使用 C 和 C++ 语言实现的 memcached 协议的客户端函数库。新版中,还添加了几个命令行工具,方便使用。编译安装 libmemcached 时,需要 memcached 安装在默认系统路径中,如果 memcached 安装在了其他路径,则可能因为找不到 memcached 文件而安装失败。

安装 libmemcached :

$ tar -xzvf libmemcached-0.31.tar.gz
$ cd libmemcached-0.31
$ ./configure 
$ make

# make install

安装完成后,libmemcached 的文件包括:
/usr/local/bin/ 目录下
memcat memcp memdump memerror memflush memrm memslap memstat
都是可执行文件,是一些命令行工具,具体使用,可参考官方文档,或帮助。

/usr/local/include/libmemcached 目录下是该函数库的一些头文件

/usr/local/lib 目录下
libmemcached* 等文件,都是库文件。

/usr/local/share/man1 目录下,有 memcat 等命令行工具的 man 帮助文件。
/usr/local/share/man3 目录下,是函数库的一些帮助文件。

命令行工具中,memstat 可在命令行查看 memcached 服务器的情况,比如:

$ memcat --servers=127.0.0.1:11211

输出的为 memcached 服务器的一些统计数据等。

实战Memcached缓存系统(5)Memcached的CAS程序实例


尊重知识,转载请注明本文来自:编程艺术家Poechant的CSDN博客 http://blog.csdn.net/poechant

1. 非CAS
首先看一个不是CAS的Memcached程序实例。实例的问题原型,见上一篇博文。
程序实例:
package com.sinosuperman.memcached;
 
import java.io.IOException;
import java.net.InetSocketAddress;
 
import net.spy.memcached.MemcachedClient;
 
public class Test {
    public static void main(String[] args) throws IOException {
        MemcachedClient cache = new MemcachedClient(new InetSocketAddress("127.0.0.1", 11211));
         
        cache.set("x", 1800, "Love");
 
        String obj1 = (String) cache.get("x");
        String obj2 = (String) cache.get("x");
        obj2 = "Michael";
         
        cache.set("x", 1800, obj2);
        System.out.println("Non-CAS 2:\t" + obj2);
        System.out.println("Non-CAS 1:\t" + obj1);
    }
}



运行结果:
2011-12-18 23:12:39.836 INFO net.spy.memcached.MemcachedConnection:  Added {QA sa=/127.0.0.1:11211, #Rops=0, #Wops=0, #iq=0, topRop=null, topWop=null, toWrite=0, interested=0} to connect queue
2011-12-18 23:12:39.843 INFO net.spy.memcached.MemcachedConnection:  Connection state changed for sun.nio.ch.SelectionKeyImpl@b09e89
Non-CAS 2:  Michael
Non-CAS 1:  Love


可见在多个Client操作时,一定会引起写不一致性的问题。

2. CAS
程序实例:
package com.sinosuperman.memcached;
 
import java.io.IOException;
import java.net.InetSocketAddress;
 
import net.spy.memcached.CASValue;
import net.spy.memcached.MemcachedClient;
 
public class Test {
    @SuppressWarnings("unchecked")
    public static void main(String[] args) throws IOException {
        MemcachedClient cache = new MemcachedClient(new InetSocketAddress("127.0.0.1", 11211));
         
        cache.set("y", 1800, "Love");
 
        CASValue casValue1 = cache.gets("y");
        CASValue casValue2 = cache.gets("y");
        cache.cas("y", casValue2.getCas(), casValue2.getValue());
         
        System.out.println("CAS 2:\t" + casValue2.getCas());
        System.out.println("Value 2:\t" + casValue2.getValue());
         
        System.out.println("CAS 1:\t" + casValue1.getCas());
        System.out.println("Value 1:\t" + casValue1.getValue());
    }
}

运行结果:
2011-12-18 23:07:14.528 INFO net.spy.memcached.MemcachedConnection:  Added {QA sa=/127.0.0.1:11211, #Rops=0, #Wops=0, #iq=0, topRop=null, topWop=null, toWrite=0, interested=0} to connect queue
2011-12-18 23:07:14.541 INFO net.spy.memcached.MemcachedConnection:  Connection state changed for sun.nio.ch.SelectionKeyImpl@1621e42
CAS 2:  11
Value 2:    Love
CAS 1:  11
Value 1:    Love

实战Memcached缓存系统(7)Memcached的一些基础FAQ



尊重知识,转载请注明本文来自:编程艺术家Poechant的CSDN博客 http://blog.csdn.net/poechant

1. Memcached是什么?
Memcached是分布式的内存对象缓存系统。


2. Memcached的基本数据结构是什么?
Memcached是基于Key/Value对的HashMap。每一对,都可以设定过期时间。


3. Memcached用什么实现?
服务端程序由C语言编写,客户端可以用任何语言编写。客户端通过Memcached协议与服务端通信。


4. Memcached特点
(1)无备份/冗余:
各Memcached节点的数据之间没有互相备份,一旦某个节点挂掉,缓存中的数据就会丢失。


5. 开发团队
Memcached由Danga Interactive开发。


6. 相关下载
(1)Memcached服务端程序
http://memcached.org/


(2)Memcached的客户端程序
http://code.google.com/p/memcached/wiki/Clients


(3)Memcached可视化管理系统(PHP和jQuery编写)
http://www.junopen.com/memadmin

7. 如何理解Memcached的分布式特点?
Memcached Server并不具有分布式特征,每个Server都是独立运行的,各Server之间不存在通信获知其他节点状态和数据备份的功能。那么Memcached为什么还是分布式的缓存系统呢?其实说到Memcached的分布式,是将Memcached Client结合在一起考虑的。具体的分布式策略,由Client实现。也就是说Memcached的分布式,不是系统层的,而是应用层的。

实战Memcached缓存系统(4)Memcached的CAS协议


尊重知识,转载请注明本文来自:编程艺术家Poechant的CSDN博客 http://blog.csdn.net/poechant

1. 什么是CAS协议
很多中文的资料都不会告诉大家CAS的全称是什么,不过一定不要把CAS当作中国科学院(China Academy of Sciences)的缩写。Google.com一下,CAS是什么?CAS是Check And Set的缩写。

2. CAS协议原文
http://code.sixapart.com/svn/memcached/trunk/server/doc/protocol.txt

3. CAS的基本原理
基本原理非常简单,一言以蔽之,就是“版本号”。每个存储的数据对象,多有一个版本号。我们可以从下面的例子来理解:
如果不采用CAS,则有如下的情景:
第一步,A取出数据对象X;
第二步,B取出数据对象X;
第三步,B修改数据对象X,并将其放入缓存;
第四步,A修改数据对象X,并将其放入缓存。
我们可以发现,第四步中会产生数据写入冲突。

如果采用CAS协议,则是如下的情景。
第一步,A取出数据对象X,并获取到CAS-ID1;
第二步,B取出数据对象X,并获取到CAS-ID2;
第三步,B修改数据对象X,在写入缓存前,检查CAS-ID与缓存空间中该数据的CAS-ID是否一致。结果是“一致”,就将修改后的带有CAS-ID2的X写入到缓存。
第四步,A修改数据对象Y,在写入缓存前,检查CAS-ID与缓存空间中该数据的CAS-ID是否一致。结果是“不一致”,则拒绝写入,返回存储失败。

这样CAS协议就用了“版本号”的思想,解决了冲突问题。

实战Memcached缓存系统(2)Memcached Java API基础之MemcachedClient




尊重知识,转载请注明本文来自:编程艺术家Poechant的CSDN博客 http://blog.csdn.net/poechant

1. 构造函数
public MemcachedClient(InetSocketAddress[] ia) throws IOException;

public MemcachedClient(List<InetSocketAddress> addrs) throws IOException;

public MemcachedClient(ConnectionFactory cf, List<InetSocketAddress> addrs) throws IOException;

其中最简单的构造函数就是第一个,可以直接传递一个InetSocketAddress,也可以是InetSocketAddress的数组。其实InetSocketAddress也是被转换成数组的。
比如:
MemcachedClient cache = new MemcachedClient(new InetSocketAddress("127.0.0.1", 11211));



2. 常用方法
一般缓存数据的常用操作有:set(add+replace)、get、replace、add

public Future<Boolean> set(String key, int exp, Object o)

第一个参数:键(key)
第二个参数:过期时间(单位是秒)
第三个参数:要设置缓存中的对象(value),如果没有则插入,如果有则修改。

public Object get(String key)

第一个参数:键(key)

public Future<Boolean> replace(String key, int exp, Object o)

第一个参数:键(key)
第二个参数:过期时间(单位是秒)
第三个参数:该键的新值(new value),如果有则修改。

public Future<Boolean> add(String key, int exp, Object o)

第一个参数:键(key)
第二个参数:过期时间(单位是秒)
第三个参数:该键的值(value),如果没有则插入。

尊重知识,转载请注明本文来自:编程艺术家Poechant的CSDN博客 http://blog.csdn.net/poechant

实战Memcached缓存系统(3)Memcached配置参数初解



尊重知识,转载请注明本文来自:编程艺术家Poechant的CSDN博客 http://blog.csdn.net/poechant

一、基本参数
在我们第一次安装Memcached时,一般都是用过这个命令:
memcached -m 512 -u root -d -l 127.0.0.1 -p 11211

我们先来解释这几个参数的含义吧。

-m 指定缓存所使用的最大内存容量,单位是Megabytes,默认是64MB
-u 只有以root身份运行时才指定该参数
-d 以daemon的形式运行
-l 指定监听的地址
-p 指定监听的TCP端口号,默认是11211

二、其他常用的参数
-t 指定线程数,默认是4个
-h 打印帮助信息
-c 最大同时连接数,默认是1024.
-U 指定监听的UDP端口号,默认是11211
-M 内存耗尽时显示错误,而不是删除项

一开始说的“-d”参数需要进行进一步的解释
-d install 安装memcached
-d uninstall 卸载memcached
-d start 启动memcached服务
-d restart 重启memcached服务
-d stop 停止memcached服务
-d shutdown 停止memcached服务

三、更多参数
使用"-h"去了解吧~

四、使用服务软件的经验
写到这里,最后说一句。在使用任何软件的时候,首先要去关注这样几点:

1. 打印帮助信息:
这个一般都是“-h”,当然也有需要“-help”才可以的。

2. 启动、停止、重启/重载配置的命令:
要注意,是同一个命令的不同参数来标识的,还是用不同的命令或脚本。

3. 配置
首先,启动是否需要配置文件。
其次,如果是比较大型的软件,一般都需要配置文件,那么默认的配置文件在哪里要清楚。
再次,指定加载配置文件的命令格式如何。

4. 指定使用内存容量、线程数、硬盘容量等
是否有这方面的配置参数需要指定,以及如何设置。

5. 日志文件
首先,是否有日志文件需要存储。
其次,如果有日志文件,默认的存储目录是什么。
最后,手动指定日志文件的命令格式如何。

6. 最大连接数
首先,是否提供监听的连接服务。
其次,如果提供,默认的最大连接数是多少。
最后,手动指定最大连接数的命令格式如何。

尊重知识,转载请注明本文来自:编程艺术家Poechant的CSDN博客 http://blog.csdn.net/poechant
-

实战Memcached缓存系统(1)Memcached基础及示例程序


尊重知识,转载请注明本文来自:编程艺术家Poechant的CSDN博客 http://blog.csdn.net/potent

1、Cache定义
(1)狭义概念:用于CPU的相对高速处理与主存(Main Memory)的相对低速处理的之间起到协调功能的硬件设备。
(2)广义概念:用于速度相差较大的两种硬件之间,起到协调两者数据传输速度差异的结构。
狭义概念来自于Cache自1967出现以来较长时间内的应用场景。因为CPU的数据处理速度,要远远高于主存,所以在CPU和主存之间会有高速缓存设备,甚至是多级缓存设备。而广义概念,则是目前已经被广泛接受的一种定义,且广义概念中,Cache不再只局限于硬件,也可以是软件。比如用于网络相对低速传输与磁盘相对高速传输之间的速度差异协调。


2、Cache的本质原理
可以一句话概括,就是:Cache把要到慢速设备中取的数据预先放到快速设备中。


3、几种类型的Cache
(1)CPU Cache:置于CPU和主存之间,用于加速CPU对主存的相对慢速操作。
(2)Browser Cache:置于客户端与服务器之间,用于加速客户端对服务器的相对慢速操作。
(3)Server Cache:置于网络请求与本地文件之间,用于加速网络请求对本地文件的相对慢速操作。
(4)CDN:CDN即Content Delivery Network,在各地设置的节点Cache,加速用户对服务网络的相对慢速操作。
(5)Database Cache
(6)OS Cache:内存中存在的对于硬盘读写的缓冲区域。

4、What is Memcached?
Memcached是一个免费开源、高性能、分布式的内存对象缓存系统。Memcached是在内存中,为特定数据(字符串或对象)构建key-value的小块数据存储。

5、下载Memcached的服务器端软件
Windows平台版本下载:http://splinedancer.com/memcached-win32/memcached-1.2.4-Win32-Preview-20080309_bin.zip
Linux平台版本下载:http://memcached.googlecode.com/files/memcached-1.4.10.tar.gz

6、在服务器上部署Memcached Server
以下以Windows平台为例:
参考:http://www.codeforest.net/how-to-install-memcached-on-windows-machine
下载下来的Windows版本解压到C:/memcached/
在控制台输入命令安装:
c:/memcached/memcached.exe  -d install

启动:
c:/memcached/memcached.exe -d  start

或:
net start "memcached Server"

默认的缓存大小为64M,如果不够用,请打开注册表,找到:
HKEY_LOCAL_MACHINE/SYSTEM/CurrentControlSet/Services/memcached  Server .

将其内容修改为:
“C:/memcached/memcached.exe” -d runservice -m 512


7、下载Memcached的客户端API包
下载地址:http://spymemcached.googlecode.com/files/memcached-2.5.jar

8、编写一个Java数据类
package com.sinosuperman.memcached;
 
import java.io.Serializable;
 
 
public class User implements Serializable{  
 
    private static final long serialVersionUID = -372274003834027815L;
 
    String userId;
     
    public User(String userId) {  
        super();  
        this.userId = userId;  
    }
     
    public String getUserId() {  
        return userId;  
    }  
     
    public void setUserId(String userId) {  
        this.userId = userId;  
    }  
         
    @Override  
    public String toString() {  
        // TODO Auto-generated method stub  
        StringBuffer sb=new StringBuffer();  
        sb.append("userId="+this.userId);  
        return sb.toString();  
    }  
}  


9、编写一个Memcached的客户端
package com.sinosuperman.memcached;
 
import java.io.IOException;
import java.net.InetSocketAddress;
 
import net.spy.memcached.MemcachedClient;
 
public class TestMemcached {
    public static void main(String[] args) throws IOException {
        MemcachedClient cache = new MemcachedClient(new InetSocketAddress("127.0.0.1", 11211));
        for (int i = 1; i < 10; i++) {
            cache.set("T0001" + i, 3600, new User(i + ""));  
        }
        User myObject = (User) cache.get("T00011");
        System.out.println("Get object from mem :" + myObject);  
    }  
}

10、运行测试
运行结果应该如下:
2011-12-15 17:25:30.276 INFO net.spy.memcached.MemcachedConnection:  Added {QA sa=/127.0.0.1:11211, #Rops=0, #Wops=0, #iq=0, topRop=null, topWop=null, toWrite=0, interested=0} to connect queue
2011-12-15 17:25:30.292 INFO net.spy.memcached.MemcachedConnection:  Connection state changed for sun.nio.ch.SelectionKeyImpl@c62080
Get object from mem :userId=1

简单介绍libevent快速上手


简介
libevent是一个事件出发的网络库,使用与 windows,linux,bad, mac os 等的高性能跨平台网络库 ,支持的多种I/O 网络模型 epoll poll dev/poll select kqueue 等

Libevent安装过程

Version:libevent-2.0.16
#http://cloud.github.com/downloads/libevent/libevent/libevent-2.0.16-stable.tar.gz
#tar -xzvf libevent-2.0.16.tar.gz
#cd libevent-2.0.16
#./configure -prefix=/usr/local
#make
#sudo make install
源代码文件组织结构

头文件:
1.libevent共用的头文件都在event2目录里                                                                                    2.正常头文件后面没有特效后缀                                                                                                      3.比如后缀“xx_struct.h”这种类型的文件里的任何结构体要是直接以来的话会破坏程序对其  他版本libevent的二进制前荣幸,有时候是以非常难以调试的方式出现
内部头文件:
1.xxx-internal.h 后最的文件内部使用的头文件                                                                                    2.目的是内部数据结构和函数,信息隐藏
libevent框架
1.event.c里有对event的整体框架实现
对系统I/O多路复用机制的封装
1.epool.c  : 对epoll的封装
2.select.c : 对select的封装
3.devpoll.c : 对dev/poll的封装
4.kqueue.c : 对kqueue的封装
定时事件管理
1.min-heap.h  定时器事件管理 堆结构
信号事件管理
signal.c : 对新号事件的处理
补助功能函数
evutil.h 和 evutil.c  util.h : 一些补助功能函数,包括创建socket pair 和一些时间操作函数 加,减,等
缓冲区管理
evbuffer.h  bufferevent.h 对缓冲区的封装
lievent 里用到的基本数据结构
compat\sys  下的queue.h 文件对 链表,双向链表,
Libevent 库的结构

Libevent_core
包含所有核心的事件和缓冲功能
event_base,evbuffer,bufferevent,和几个附加补助功能
Libevent_extra
定义了程序可能需要,也可能不需要的协议特定功能,包括HTTP、DNS和RPC
 Libevent
这个库没用以后版本会删掉
Libevent_pthreads
pthread可一直线程库的线程和锁定实现
Libevent_openssl这个库为使用bufferevent和OpenSSL进行加密的通信提供支持。注意bufferevent 目前只支持 tcp 不支持udp


Libevent主要功能主键

Evutil  : 网络补助工具
Event,eventbase  : Libevent核心事件和事件管理
Bufferevent :为libevent基于事件的核心提供使用方便的封装除了通知程序套接字(注意 : 目前只针对于tcp  udp不支持)
Evbuffer : 在bufferevent层之下实现了缓冲功能,并且提供了方便有效的访问函数。


Libevent普通简单例子实现

 
int main( )
{
int listen_fd;
struct event ev_accept;
//--相当于创建一个事件堆 以后事件可以往这里注册了--
base = event_base_new();
listen_fd = socket( AF_INET,SOCK_STREAM,0 );
if( listen_fd <0 )
return;
int reuseaddr_on = 1;
if( setsockopt( listen_fd,SOL_SOCKET,SO_REUSEADDR,&reuseaddr_on,sizeof(reuseaddr_on) ) == -1 )
{
cout<<"Error : Setsockopt failed" <<endl;
}
//--SetSocket_listenaddr--
struct sockaddr_in listen_addr;
memset( &listen_addr,0,sizeof( listen_addr ) );
listen_addr.sin_family = AF_INET;
listen_addr.sin_addr.s_addr = INADDR_ANY;
listen_addr.sin_port = htons( 80800 );
if( bind( listen_fd,(struct sockaddr*)&listen_addr,sizeof( listen_addr ) ) < 0)
{
cout<<"Error : Bind failed"<<endl;
}
if( listen( listen_fd,500 ) < 0 )
{
cout<<"Error : Listen failed"<<endl; return ;
}
if( SetNonBlock(listen_fd) < 0 )
{
cout<<"Error : SetNonBlock failed"<<endl; return;
}
//--初始化事件ev_accept 设置accept回调函数和 和事件类型--
event_set( &ev_accept,listen_fd,EV_READ|EV_PERSIST,on_accept,NULL );
//--设置完的ev_accept事件注册到 base里--
event_base_set( base,&ev_accept );
//--正事添加事件 相当于注册完的事件激活--
event_add( &ev_accept,NULL );
//--事件堆run部分--
event_base_dispatch(base);
return 1;
}


accpet callback code



 
void on_accept(int fd,short ev,void* arg)
{
int client_fd;
struct sockaddr_in client_addr;
socklen_t client_len = sizeof( client_addr );
client_fd = accept( fd,(struct sockaddr*)&client_addr,&client_len );
if( client_fd == -1 )
{
cout<<"Error : accept client failed"<<endl;
}
if (SetNonBlock(client_fd) < 0)
{
cout<<"Error : Set client socket nonblock"<<endl;
}
static int index = 0;
cout << "客户端 "<< index <<"-----" << inet_ntoa( client_addr.sin_addr ) <<" 已链接 ~~~~~" <<endl;
index++;
//--accept到的 新客户端 注册一个recv 事件--
stMyClient* pClient = new stMyClient();
//--使用刚连接客户端的文件描述符 监视 recv 消息--
event_set( &pClient->ev_read,client_fd,EV_READ|EV_PERSIST,on_read,pClient );
event_base_set( base,&pClient->ev_read );
event_add( &pClient->ev_read,NULL );
}
recv callback code

Source code  
void on_read( int fd,short ev,void* arg )
{
struct stMyClient* client = (stMyClient*)arg;
char buff[65535];
memset( buff,0x00,sizeof( buff ) );
int nSize = read( fd,buff,65535 );
if( nSize == 0 )
{
cout<<"Client disconnected "<<endl;
close(fd);
event_del( &client->ev_read );
delete client;
return;
}
else if( nSize < 0 )
{
cout<<"Socket failed disconnected "<<endl;
close(fd);
event_del( &client->ev_read );
delete client;
return;
}
cout<<"Read :"<<buff<<endl;
}




关于使用Bufferevent 和 多线程用法



 
//--线程回调--
void* ProcessThread( void* pthread )
{
CVitNetThread* p = (CVitNetThread*)pthread;
//--p->GetEvQueue() 这里获取到的是event_base对象--
//--这里相当于把事件堆绑定在当前线程里--
event_base_dispatch(p->GetEvQueue());
return NULL;
}
void main()
{
//----listen bind 部分同上---
//
//----------------------------
m_pEvQueue = event_base_new();
event_assign( &m_incEvent, m_pEvQueue,fd, evType, accept_cb, this);
event_base_set( m_pEvQueue,&m_incEvent );
event_add( &m_incEvent,NULL );
//--注意这里event_base堆里一个事件都没注册情况下 不能创建线程--
//--因为event_base_dispatch();此函数判断没有事件注册了的就会退出线程的--
int ret;
if ((ret = pthread_create(&m_incThreadID, NULL, ProcessThread,this)) != 0)
{
s_pLog->Log(LOG_ERROR, "%s %s => CreateThread failed",__FILE__,__FUNCTION__) ;return false;
}
}
Source code  
void accept_cb( int fd,short ev,void* arg )
{
CVitNetThread *p = (CVitNetThread*)arg;
int client_fd = -1;
struct sockaddr_in client_addr;
socklen_t client_len = sizeof( client_addr );
client_fd = accept( fd,(struct sockaddr*)&client_addr,&client_len );
if( client_fd == -1 )
{
s_pLog->Log(LOG_ERROR, "%s %s => accept client failed fd = [%d]",__FILE__,__FUNCTION__,client_fd) ;return;
}
if (evutil_make_socket_nonblocking(client_fd) < 0)
{
s_pLog->Log(LOG_ERROR, "%s %s => Set client socket nonblock is failed",__FILE__,__FUNCTION__,client_fd) ;return;
}
//--这里创建一个缓存事件 对象 设置 recv write error 回调函数--
//--这里系统要是有recv到得信息的时候会自动调用recv_cb回调函数的
//--这里系统要是有send信息的时候自动调用write_cb回调函数
//--这里系统要是有出错或延迟的时候回调用此error_cb回调函数
bufferevent* bufferev = bufferevent_new( client_fd,recv_cb,write_cb,error_cb,p );
if( bufferev == NULL )
{
s_pLog->Log(LOG_ERROR, "%s %s => bufferev bis NULL",__FILE__,__FUNCTION__) ;return;
}
//--bufferev 事件 注册到 消息堆里 event_base 这里( p->GetEvQueue()返回一个event_base对象)
bufferevent_base_set( p->GetEvQueue(),bufferev );
bufferevent_enable( bufferev, EV_READ | EV_WRITE );
}
bufferevent recv,error

Source code  
void recv_cb( struct bufferevent *ev, void *arg )
{
CVitNetThread *p = (CVitNetThread*)arg;
if( p == NULL )
{
s_pLog->Log(LOG_ERROR, "%s %s => argument is NULL",__FILE__,__FUNCTION__) ;return;
}
int fd = bufferevent_getfd(ev);
char buffer[READ_MAX]; memset( buffer, 0x00,sizeof( buffer ) );
int ret = bufferevent_read(ev, &buffer,evbuffer_get_length( ev->input ));
cout<<buffer<<endl;
}
void error_cb( struct bufferevent *ev, short events ,void *arg )
{
CVitNetThread *p = (CVitNetThread*)arg;
int fd = bufferevent_getfd(ev);
if( events & BEV_EVENT_CONNECTED )
cout<<"Connect ok"<<endl;
else if( events & (BEV_EVENT_ERROR | BEV_EVENT_EOF) )
cout<<"disconnect"<<endl;
else if( events & BEV_EVENT_TIMEOUT )
cout<<"TimeOut"<<endl;
else if( events & BEV_EVENT_WRITING )
cout<<"Wrting"<<endl;
bufferevent_free(ev);
}

机器学习与人工智能学习资源导引



TopLanguage(https://groups.google.com/group/pongba/)

我经常在 TopLanguage 讨论组上推荐一些书籍,也经常问里面的牛人们搜罗一些有关的资料,人工智能、机器学习、自然语言处理、知识发现(特别地,数据挖掘)、信息检索 这些无疑是 CS 领域最好玩的分支了(也是互相紧密联系的),这里将最近有关机器学习和人工智能相关的一些学习资源归一个类:
首先是两个非常棒的 Wikipedia 条目,我也算是 wikipedia 的重度用户了,学习一门东西的时候常常发现是始于 wikipedia 中间经过若干次 google ,然后止于某一本或几本著作。
第一个是“人工智能的历史”(History of Artificial Intelligence),我在讨论组上写道:
而今天看到的这篇文章是我在 wikipedia 浏览至今觉得最好的。文章名为《人工智能的历史》,顺着 AI 发展时间线娓娓道来,中间穿插无数牛人故事,且一波三折大气磅礴,可谓"事实比想象更令人惊讶"。人工智能始于哲学思辨,中间经历了一个没有心理学(尤其是认知神经科学的)的帮助的阶段,仅通过牛人对人类思维的外在表现的归纳、内省,以及数学工具进行探索,其间最令人激动的是 Herbert Simon (决策理论之父,诺奖,跨领域牛人)写的一个自动证明机,证明了罗素的数学原理中的二十几个定理,其中有一个定理比原书中的还要优雅,Simon 的程序用的是启发式搜索,因为公理系统中的证明可以简化为从条件到结论的树状搜索(但由于组合爆炸,所以必须使用启发式剪枝)。后来 Simon 又写了 GPS (General Problem Solver),据说能解决一些能良好形式化的问题,如汉诺塔。但说到底 Simon 的研究毕竟只触及了人类思维的一个很小很小的方面 —— Formal Logic,甚至更狭义一点 Deductive Reasoning (即不包含 Inductive Reasoning , Transductive Reasoning (俗称 analogic thinking)。还有诸多比如 Common Sense、Vision、尤其是最为复杂的 Language 、Consciousness 都还谜团未解。还有一个比较有趣的就是有人认为 AI 问题必须要以一个物理的 Body 为支撑,一个能够感受这个世界的物理规则的身体本身就是一个强大的信息来源,基于这个信息来源,人类能够自身与时俱进地总结所谓的 Common-Sense Knowledge (这个就是所谓的 Emboddied  Mind 理论。 ),否则像一些老兄直接手动构建 Common-Sense Knowledge Base ,就很傻很天真了,须知人根据感知系统从自然界获取知识是一个动态的自动更新的系统,而手动构建常识库则无异于古老的 Expert System 的做法。当然,以上只总结了很小一部分我个人觉得比较有趣或新颖的,每个人看到的有趣的地方不一样,比如里面相当详细地介绍了神经网络理论的兴衰。所以我强烈建议你看自己一遍,别忘了里面链接到其他地方的链接。
顺便一说,徐宥同学打算找时间把这个条目翻译出来,这是一个相当长的条目,看不动 E 文的等着看翻译吧:)
第二个则是“人工智能”(Artificial Intelligence)。当然,还有机器学习等等。从这些条目出发能够找到许多非常有用和靠谱的深入参考资料。

然后是一些书籍
书籍:
1. 《Programming Collective Intelligence》,近年出的入门好书,培养兴趣是最重要的一环,一上来看大部头很容易被吓走的:P
2. Peter Norvig 的《AI, Modern Approach 2nd》(无争议的领域经典)。
3. 《The Elements of Statistical Learning》,数学性比较强,可以做参考了。
4. 《Foundations of Statistical Natural Language Processing》,自然语言处理领域公认经典。
5. 《Data Mining, Concepts and Techniques》,华裔科学家写的书,相当深入浅出。
6. 《Managing Gigabytes》,信息检索好书。
7. 《Information Theory:Inference and Learning Algorithms》,参考书吧,比较深。
相关数学基础(参考书,不适合拿来通读):
1. 线性代数:这个参考书就不列了,很多。
2. 矩阵数学:《矩阵分析》,Roger Horn。矩阵分析领域无争议的经典。
3. 概率论与统计:《概率论及其应用》,威廉·费勒。也是极牛的书,可数学味道太重,不适合做机器学习的。于是讨论组里的 Du Lei 同学推荐了《All Of Statistics》并说到
机器学习这个方向,统计学也一样非常重要。推荐All of statistics,这是CMU的一本很简洁的教科书,注重概念,简化计算,简化与Machine Learning无关的概念和统计内容,可以说是很好的快速入门材料。
4. 最优化方法:《Nonlinear Programming, 2nd》非线性规划的参考书。《Convex Optimization》凸优化的参考书。此外还有一些书可以参考 wikipedia 上的最优化方法条目。要深入理解机器学习方法的技术细节很多时候(如SVM)需要最优化方法作为铺垫。
王宁同学推荐了好几本书:
《Machine Learning, Tom Michell》, 1997.
老书,牛人。现在看来内容并不算深,很多章节有点到为止的感觉,但是很适合新手(当然,不能"新"到连算法和概率都不知道)入门。比如决策树部分就很精彩,并且这几年没有特别大的进展,所以并不过时。另外,这本书算是对97年前数十年机器学习工作的大综述,参考文献列表极有价值。国内有翻译和影印版,不知道绝版否。
《Modern Information Retrieval, Ricardo Baeza-Yates et al》. 1999
老书,牛人。貌似第一本完整讲述IR的书。可惜IR这些年进展迅猛,这本书略有些过时了。翻翻做参考还是不错的。另外,Ricardo同学现在是Yahoo Research for Europe and Latin Ameria的头头。
《Pattern Classification (2ed)》, Richard O. Duda, Peter E. Hart, David G. Stork
大约也是01年左右的大块头,有影印版,彩色。没读完,但如果想深入学习ML和IR,前三章(介绍,贝叶斯学习,线性分类器)必修。
还有些经典与我只有一面之缘,没有资格评价。另外还有两本小册子,论文集性质的,倒是讲到了了不少前沿和细节,诸如索引如何压缩之类。可惜忘了名字,又被我压在箱底,下次搬家前怕是难见天日了。
(呵呵,想起来一本:《Mining the Web - Discovering Knowledge from Hypertext Data》 )
说一本名气很大的书:《Data Mining: Practical Machine Learning Tools and Techniques》。Weka 的作者写的。可惜内容一般。理论部分太单薄,而实践部分也很脱离实际。DM的入门书已经不少,这一本应该可以不看了。如果要学习了解 Weka ,看文档就好。第二版已经出了,没读过,不清楚。
信息检索方面,Du Lei 同学再次推荐:
信息检索方面的书现在建议看Stanford的那本《Introduction to Information Retrieval》,这书刚刚正式出版,内容当然up to date。另外信息检索第一大牛Croft老爷也正在写教科书,应该很快就要面世了。据说是非常pratical的一本书。
对信息检索有兴趣的同学,强烈推荐翟成祥博士在北大的暑期学校课程,这里有全slides和阅读材料:http://net.pku.edu.cn/~course/cs410/schedule.html
maximzhao 同学推荐了一本机器学习:
加一本书:Bishop, 《Pattern Recognition and Machine Learning》. 没有影印的,但是网上能下到。经典中的经典。Pattern Classification 和这本书是两本必读之书。《Pattern Recognition and Machine Learning》是很新(07年),深入浅出,手不释卷。

最后,关于人工智能方面(特别地,决策与判断),再推荐两本有意思的书,
一本是《Simple Heuristics that Makes Us Smart》
另一本是《Bounded Rationality: The Adaptive Toolbox》
不同于计算机学界所采用的统计机器学习方法,这两本书更多地着眼于人类实际上所采用的认知方式,以下是我在讨论组上写的简介:
这两本都是德国ABC研究小组(一个由计算机科学家、认知科学家、神经科学家、经济学家、数学家、统计学家等组成的跨学科研究团体)集体写的,都是引起领域内广泛关注的书,尤其是前一本,後一本则是对 Herbert Simon (决策科学之父,诺奖获得者)提出的人类理性模型的扩充研究),可以说是把什么是真正的人类智能这个问题提上了台面。核心思想是,我们的大脑根本不能做大量的统计计算,使用fancy的数学手法去解释和预测这个世界,而是通过简单而鲁棒的启发法来面对不确定的世界(比如第一本书中提到的两个后来非常著名的启发法:再认启发法(cognition heuristics)和选择最佳(Take the Best)。当然,这两本书并没有排斥统计方法就是了,数据量大的时候统计优势就出来了,而数据量小的时候统计方法就变得非常糟糕;人类简单的启发法则充分利用生态环境中的规律性(regularities),都做到计算复杂性小且鲁棒。
关于第二本书的简介:
1. 谁是 Herbert Simon
2. 什么是 Bounded Rationality
3. 这本书讲啥的:
我一直觉得人类的决策与判断是一个非常迷人的问题。这本书简单地说可以看作是《决策与判断》的更全面更理论的版本。系统且理论化地介绍人类决策与判断过程中的各种启发式方法(heuristics)及其利弊 (为什么他们是最优化方法在信息不足情况下的快捷且鲁棒的逼近,以及为什么在一些情况下会带来糟糕的后果等,比如学过机器学习的都知道朴素贝叶斯方法在许多情况下往往并不比贝叶斯网络效果差,而且还速度快;比如多项式插值的维数越高越容易overfit,而基于低阶多项式的分段样条插值却被证明是一个非常鲁棒的方案)。
在此提一个书中提到的例子,非常有意思:两个团队被派去设计一个能够在场上接住抛过来的棒球的机器人。第一组做了详细的数学分析,建立了一个相当复杂的抛物线近似模型(因为还要考虑空气阻力之类的原因,所以并非严格抛物线),用于计算球的落点,以便正确地接到球。显然这个方案耗资巨大,而且实际运算也需要时间,大家都知道生物的神经网络中生物电流传输只有百米每秒之内,所以 computational complexity 对于生物来说是个宝贵资源,所以这个方案虽然可行,但不够好。第二组则采访了真正的运动员,听取他们总结自己到底是如何接球的感受,然后他们做了这样一个机器人:这个机器人在球抛出的一开始一半路程啥也不做,等到比较近了才开始跑动,并在跑动中一直保持眼睛于球之间的视角不变,后者就保证了机器人的跑动路线一定会和球的轨迹有交点;整个过程中这个机器人只做非常粗糙的轨迹估算。体会一下你接球的时候是不是眼睛一直都盯着球,然后根据视线角度来调整跑动方向?实际上人类就是这么干的,这就是 heuristics 的力量。
相对于偏向于心理学以及科普的《决策与判断》来说,这本书的理论性更强,引用文献也很多而经典,而且与人工智能和机器学习都有交叉,里面也有不少数学内容,全书由十几个章节构成,每个章节都是由不同的作者写的,类似于 paper 一样的,很严谨,也没啥废话,跟 《Psychology of Problem Solving》类似。比较适合 geeks 阅读哈。
另外,对理论的技术细节看不下去的也建议看看《决策与判断》这类书(以及像《别做正常的傻瓜》这样的傻瓜科普读本),对自己在生活中做决策有莫大的好处。人类决策与判断中使用了很多的 heuristics ,很不幸的是,其中许多都是在适应几十万年前的社会环境中建立起来的,并不适合于现代社会,所以了解这些思维中的缺点、盲点,对自己成为一个良好的决策者有很大的好处,而且这本身也是一个非常有趣的领域。
(完)
P.S. 大家有什么好的资料请至讨论组上留言。

libevent(源于维基百科,自由的百科全书)



原作者 Nick Mathewson, Niels Provos
稳定版本 2.0.10/
2010年12月16日
平台 跨平台
语言 C语言、C++
类型 函式库
许可协议 BSD许可证
网站 http://www.monkey.org/~provos/libevent/
libevent是一个异步事件处理软件函式库,以BSD许可证发布。
libevent提供了一组应用程序编程接口(API),让程式设计师可以设定某些事件发生时所执行的函式,也就是说,libevent可以用来取代网络服务器所使用的事件循环检查框架。
由于可以省去对网络的处理,且拥有不错的效能,有些软件使用libevent作为网络底层的函式库,如:memcached、Tor。

目前libevent支持以下的方式判断IO事件:
poll(2)
select(2)
几乎所有的Unix平台都有提供的函式。
/dev/pool
以Solaris平台为主。
kqueue(2)
以BSD平台为主。
epoll(2)
以Linux平台为主。

【笔记】来自MIT人工智能实验室:如何做研究



一 阅读
阅读论文:找最领域内本质的论文。通过本领域的建议阅读列表、高年级前辈的建议获得。
阅读期刊:最有价值的。AI领域:Artificial Intelligence(the Journal of Artificial Intelligence), Computational Intelligence, Machine Learning, IEEE PAMI(Pattern Analysis and Machine Intelligence)
阅读论文三个阶段:
1 寻找兴趣点
2 寻找真正的内容点,精华
3 有必要再精读
问题:”我该如何利用这篇文章“,”真的像作者宣称的那样么“,”如果……会发生什么“
理解论文,要了解论文的目的、作者做的假设、选择,论文相关领域有哪些问题,作者观点等。
如果对该领域感兴趣,应该实现论文中的demo

二 建立关系
加入secret paper pass network。
有想法->草稿论文->发给朋友看->意见->修改->发表
常讨论

三 学习新领域(物理学、哲学、语言学、心理学……)
读课本
读最棒期刊
读著名学者书籍

四 笔记
记录下自己的想法、遇到的问题、解决方法
定期翻阅

五 写作
整理想法,写成论文
获得反馈
方法:
1 列大纲->逐步细化
2 写下大脑里想到的所有东西,即使是垃圾
3 先写核心内容,而非顺序写
要给别人看,提意见

六 讲演

七 程序设计

八 心理因素
常会遇到失败、失去兴趣,有时会完全陷住,毫无进展
Alan Lakien的书《How to Get Control of Your Time and Your Life》
坚持的作用一般大于天资
交流,获得反馈
诺贝尔获奖者经常怀疑自己的工作的正确性和价值性

(原文:http://www.cnblogs.com/jasonasm/archive/2004/09/01/38465.aspx)

libevent源码深度剖析二——Reactor模式


前面讲到,整个libevent本身就是一个Reactor,因此本节将专门对Reactor模式进行必要的介绍,并列出libevnet中的几个重要组件和Reactor的对应关系,在后面的章节中可能还会提到本节介绍的基本概念。

1 Reactor的事件处理机制

首先来回想一下普通函数调用的机制:程序调用某函数?函数执行,程序等待?函数将结果和控制权返回给程序?程序继续处理。
Reactor释义“反应堆”,是一种事件驱动机制。和普通函数调用的不同之处在于:应用程序不是主动的调用某个API完成处理,而是恰恰相反,Reactor逆置了事件处理流程,应用程序需要提供相应的接口并注册到Reactor上,如果相应的时间发生,Reactor将主动调用应用程序注册的接口,这些接口又称为“回调函数”。使用Libevent也是想Libevent框架注册相应的事件和回调函数;当这些时间发声时,Libevent会调用这些回调函数处理相应的事件(I/O读写、定时和信号)。
    用“好莱坞原则”来形容Reactor再合适不过了:不要打电话给我们,我们会打电话通知你。
    举个例子:你去应聘某xx公司,面试结束后。
“普通函数调用机制”公司HR比较懒,不会记你的联系方式,那怎么办呢,你只能面试完后自己打电话去问结果;有没有被录取啊,还是被据了;

“Reactor”公司HR就记下了你的联系方式,结果出来后会主动打电话通知你:有没有被录取啊,还是被据了;你不用自己打电话去问结果,事实上也不能,你没有HR的留联系方式。

2 Reactor模式的优点

Reactor模式是编写高性能网络服务器的必备技术之一,它具有如下的优点:
    1)响应快,不必为单个同步时间所阻塞,虽然Reactor本身依然是同步的;
    2)编程相对简单,可以最大程度的避免复杂的多线程及同步问题,并且避免了多线程/进程的切换开销;
    3)可扩展性,可以方便的通过增加Reactor实例个数来充分利用CPU资源;
    4)可复用性,reactor框架本身与具体事件处理逻辑无关,具有很高的复用性;

3 Reactor模式框架

    使用Reactor模型,必备的几个组件:事件源、Reactor框架、多路复用机制和事件处理程序,先来看看Reactor模型的整体框架,接下来再对每个组件做逐一说明。
 components 

1) 事件源
Linux上是文件描述符,Windows上就是Socket或者Handle了,这里统一称为“句柄集”;程序在指定的句柄上注册关心的事件,比如I/O事件。

2) event demultiplexer——事件多路分发机制
由操作系统提供的I/O多路复用机制,比如select和epoll。
    程序首先将其关心的句柄(事件源)及其事件注册到event demultiplexer上;
当有事件到达时,event demultiplexer会发出通知“在已经注册的句柄集中,一个或多个句柄的事件已经就绪”;
    程序收到通知后,就可以在非阻塞的情况下对事件进行处理了。
对应到libevent中,依然是select、poll、epoll等,但是libevent使用结构体eventop进行了封装,以统一的接口来支持这些I/O多路复用机制,达到了对外隐藏底层系统机制的目的。

3) Reactor——反应器
    Reactor,是事件管理的接口,内部使用event demultiplexer注册、注销事件;并运行事件循环,当有事件进入“就绪”状态时,调用注册事件的回调函数处理事件。
对应到libevent中,就是event_base结构体。
一个典型的Reactor声明方式
  1. class Reactor  
  2. {  
  3. public:  
  4.     int register_handler(Event_Handler *pHandler, int event);  
  5.     int remove_handler(Event_Handler *pHandler, int event);  
  6.     void handle_events(timeval *ptv);  
  7.     // ...  
  8. };  

4) Event Handler——事件处理程序
    事件处理程序提供了一组接口,每个接口对应了一种类型的事件,供Reactor在相应的事件发生时调用,执行相应的事件处理。通常它会绑定一个有效的句柄。
对应到libevent中,就是event结构体。
下面是两种典型的Event Handler类声明方式,二者互有优缺点。
  1. class Event_Handler  
  2. {  
  3. public:  
  4.     virtual void handle_read() = 0;  
  5.     virtual void handle_write() = 0;  
  6.     virtual void handle_timeout() = 0;  
  7.     virtual void handle_close() = 0;  
  8.     virtual HANDLE get_handle() = 0;  
  9.     // ...  
  10. };  
  11. class Event_Handler  
  12. {  
  13. public:  
  14.     // events maybe read/write/timeout/close .etc  
  15.     virtual void handle_events(int events) = 0;  
  16.     virtual HANDLE get_handle() = 0;  
  17.     // ...  
  18. };  

4 Reactor事件处理流程

前面说过Reactor将事件流“逆置”了,那么使用Reactor模式后,事件控制流是什么样子呢?
可以参见下面的序列图。
 reactor sequences

5 小结

上面讲到了Reactor的基本概念、框架和处理流程,对Reactor有个基本清晰的了解后,再来对比看libevent就会更容易理解了,接下来就正式进入到libevent的代码世界了,加油!

参考资料:
Pattern-Oriented Software Architecture, Patterns for Concurrent and Networked Objects, Volume 2