吐槽memcached proxy 之memagent

前言:

本人在网络上并未找到magent的实际应用的例子,都是一些测试,本人想通过java调用代理magent来解决单点故障的问题,但是没有相关的文档API介绍,后来采用java memcached client直接调用magent服务,居然可以的。其实本人在学习memcached与magent之后,发现网络上,很多人并没有把magent理解对,并不知道它是什么一个角色,只知道是个memcached代理,然后就互相抄袭拷贝对方的文章,有的压根都没有自己亲手测试过。就算自己真正动手测试过magent的,但是并没有把它运用到实际的项目中,起码在本人看来,没有相关的客户端API,但是magent其实跟memcached的调用方法一样,magent的简介里面也介绍说,调用magent服务的方法跟调用memcached服务的方法一样,而且我们可以看到magent的命令模式与memcached的都差不多。

其实对于magent,大家可以把它理解成是memcached客户端的一个扩展,它同样有java client、spymemcached、xmemcached它们的功能,支持ketama一致性hash算法,多了一个memcached备份功能而已。在linux系统下,我们可以用telnet进入magent启动的进程服务set、get值的,写代码的时候,可以直接用java memcached client调用magent,只需要把调用memcached服务的地址改成magent的服务地址。

很多人都理解成了,我只要在memcached服务器上配置好magent,客户端还是采用如memcached-client-for-java来调用memcached,当set、get值的时候,magent会自动分配并自动把值保存到备份服务器上,其实错了,既然你还是采用的memcached-client-for-java,那么就是运用的memcached-client-for-java里面的一致性hash算法了,而不是采用的magent里面的一致性hash算法了。这样做,其实还是撇开了magent。所以必须改成调用magent服务

这也是:http://www.oschina.net/question/858822_77565 中提出:

但是测试结果告诉,当A或者B,或者AB都当掉之后,取值为null。也就是说,magent代理服务并没有将值set到备份服务上。

的答案

就如一个公司的创始人(客户端-java memcached client)一开始有很多任务(key value)要分配(set、get)给各员工(memcached服务器)做,后来请来了一个CEO(magent),想让他来代理分配 (set、get) 工作,解决员工离职(单点故障)问题。所以正常情况下,应该是,创始人(客户端)把所有的任务(key value)直接交给CEO(magent)就行了,而不是创始人(客户端-java memcached client)还亲自再分配工作给员工(memcached服务器),应该直接让CEO(magent-java memcached client)自己去分配工作,解决员工离职(单点故障)。所以只需要创始人(客户端-java memcached client)指示CEO(magent-java memcached client)去做就行了,也就是java memcached client调用magent就行了,其他的都不用管了

magent优于java memcached client、spymemcached、xmemcached解决了单点故障, 但是它本身很不完善,譬如一个缓存服务器memcached上面存了(1,2,3这些值),现在它宕机了,但magent还能通过备份memcached服务器上获取到(1,2,3)数据。但是把宕机的memcached服务器重启后,虽然备份的memcached服务器上有(1,2,3)数据,但magent会再次去重新启动的memcached服务器上取数据,却又由于重启的memcached服务器数据丢失,所以取不到相应的数据了。所以说其应用在实际项目中并不现实

以上纯属个人的理解,技术有限,如有错误,还望大家指出纠正,互相学习

-------------------------------------------------------------------------------------------------------------------------------------

memcached不提供集群功能,因为集群的要素是负载均衡和单点恢复;
memcached在server端之间是不会进行通讯的,目前比较流行的有下面第几种替代的解决方案,虽不是很完美,但是能满足一些基本需求。

1、通过客户端进行hash算法 存到不同的mamcached server上
  该方法是在客户端存值之前先对key进行hash,把算出的hash对应的不同的memcached server上,这样保证了系统中的数据是存放到不同的mamcached server上,分散了在单台机器上的风险,提高了性能,缺点是单一台机器down后,它上面的数据将会丢失,没法恢复,不能动态增加机器,动态增加机器后,本地key对应server会发生改变,以前的老数据将不能取到,只能系统全部重启
 当前的memcached java client就提供了该功能,代码如下
  String[] serverlist = {"10.10.9.116:11211","10.10.9.116:11212"}; //定义了两台cache机器
  SockIOPool pool = SockIOPool.getInstance();
  pool.setServers(serverlist);

  Integer weights[]=new Integer{1,3};//可以根据每台机器的性能设置不同的权重

  pool.setWeights(weights);
  pool.setInitConn(initConn);
  pool.setMinConn(minConn);
  pool.setMaxConn(maxConn);
  pool.setMaintSleep(mainSleep);
  pool.setNagle(false);
  pool.setFailover( true );
  pool.initialize();
  instance = this;

2、现在通用的使用一致hash算法 ,可以最大限度避免第一条中的缺点,可以动态的增加或减少机器而对现有的其他机器上的数据保持不变,只会影响小部分的数据的存取。
目前 java client、spymemcached、xmemcached都支持一致hash,同时还采用了虚拟节点的一致性hash算法,使memcached尽量的负载均衡,可以参考:http://blog.csdn.net/fdipzone/article/details/7170045
下面是memcached java client的一段代码,需要设置的几个关键属性:
  pool.setNagle(true);//这是开启一个nagle 算法。改算法避免网络中充塞小封包,提高网络的利用率 ;
  pool.setHashingAlg(SockIOPool.CONSISTENT_HASH);//设置为一致性hash算法,在memcached集群时使用
  pool.setFailover( true ); //集群用  设置池的故障转移的标志  当一个memcached服务器失效的时候客户端默认会   failover另一个服务去.如果失效的服务器恢复运行,客户端会返回到原来连接的服务器.一般不要使用该功能
  pool.setAliveCheck(true);//表示在使用Socket以前是否先检查Socket状态

3、使用memagent

上面1、2都是针对客户端在访问时候进行key的hash算法,而3的方法是基于在memcached服务端安装代理进行分发,它可以进行数据的备份,当某台机器down后会自动从备份机器取对应的数据有如下特点:

     A、和每个memcache server保持多个长连接,效果是减少memcache server保持的连接数量及创建销毁连的开销。不过,memcache本身就支持大并发连接,这个功能也就没什么特别的说道。

     B、支持memcache的binary协议命令,实现请求的转发。

     C、和memcache一样,基于libevent的事件驱动来处理IO。

     D、支持ketama 的一致性hash算法。

     E、支持memcache backup集群,当memcache集群有机器挂了,memagent会将get请求转向memcache backup集群。这个功能对于cache的稳定性要求高的场景下会有用武之地。

magent是一款开源的Memcached代理服务器软件,其项目网址为:
http://code.google.com/p/memagent/


优点:系统自动备份,当一台机器down后自动切换到备份机器

------------------------------------------------------------------------------------------------------------------------------------

测试环境:
四台机器:192.168.1.105、192.168.1.151、192.168.1.152、192.168.1.153
192.168.1.105为主机,151、152、153为在105上安装的虚拟机
四台机器上均安装了memcached与magent

1.151、152、153三台机器上,分别启动两个memcached进程,端口均为11211、11212
2.现以105机器为代理机器和备份机器,即在105的机器上启动一个备份的memcached服务,端口为11213,同时再启动一个magent代理进程服务,端口为10000,做主magent服务。
3.另外在151机器上再启动一个magent服务,作为备份的magent服务,万一105机器宕机,则可以启动备份的magent服务,实际配属配置需根据实际应用环境来定,我这边只是测试一下该集群的工作机制与逻辑。


在151机器上启动两个memcached进程:
memcached -m 1 -u root -d -l 192.168.1.151 -p 11211
memcached -m 1 -u root -d -l 192.168.1.151 -p 11212
在152机器上启动两个memcached进程:
memcached -m 1 -u root -d -l 192.168.1.152 -p 11211
memcached -m 1 -u root -d -l 192.168.1.152 -p 11212
在153机器上启动两个memcached进程:
memcached -m 1 -u root -d -l 192.168.1.153 -p 11211
memcached -m 1 -u root -d -l 192.168.1.153 -p 11212
在105机器上启动一个memcached进程:备份用
memcached -m 1 -u root -d -l 192.168.1.105 -p 11213
在105机器上启动一个magent进程:
magent -u root -n 51200 -l 192.168.1.105 -p 10000 -s 192.168.1.151:11211 -s 192.168.1.151:11212 -s 192.168.1.152:11211 -s 192.168.1.152:11212 -s 192.168.1.153:11211 -s 192.168.1.153:11212 -b 192.168.1.105:11213
在151机器上启动一个magent进程:备份用
magent -u root -n 51200 -l 192.168.1.151 -p 10000 -s 192.168.1.151:11211 -s 192.168.1.151:11212 -s 192.168.1.152:11211 -s 192.168.1.152:11212 -s 192.168.1.153:11211 -s 192.168.1.153:11212 -b 192.168.1.105:11213

在105机器上:
[root@localhost ~]# memcached -m 1 -u root -d -l 192.168.1.105 -p 11213
[root@localhost ~]# magent -u root -n 51200 -l 192.168.1.105 -p 10000 -s 192.168.1.151:11211 -s 192.168.1.151:11212 -s 192.168.1.152:11211 -s 192.168.1.152:11212 -s 192.168.1.153:11211 -s 192.168.1.153:11212 -b 192.168.1.105:11213
[root@localhost ~]# telnet 192.168.1.105 10000
Trying 192.168.1.105...
Connected to 192.168.1.105.
Escape character is '^]'.
set key1 0 0 1
1
STORED
set key2 0 0 1
2
STORED
set key3 0 0 1
3
STORED
set key4 0 0 1
4
STORED
set key5 0 0 1
5
STORED
set key6 0 0 1
6
STORED
set key7 0 0 1
7
STORED
quit
Connection closed by foreign host.
[root@localhost ~]# telnet 192.168.1.151 11211
Trying 192.168.1.151...
Connected to 192.168.1.151.
Escape character is '^]'.
get key1
END
get key2
END
get key3
END
get key4
VALUE key4 0 1
4
END
get key5
END
get key6
END
get key7
END
quit
Connection closed by foreign host.
[root@localhost ~]# telnet 192.168.1.151 11212
Trying 192.168.1.151...
Connected to 192.168.1.151.
Escape character is '^]'.
get key1
END
get key2
END
get key3
END
get key4
END
get key5
VALUE key5 0 1
5
END
get key6
END
get key7
END
quit
Connection closed by foreign host.
[root@localhost ~]# telnet 192.168.1.152 11211
Trying 192.168.1.152...
Connected to 192.168.1.152.
Escape character is '^]'.
get key1
END
get key2
END
get key3
END
get key4
END
get key5
END
get key6
VALUE key6 0 1
6
END
get key7
END
quit
Connection closed by foreign host.
[root@localhost ~]# telnet 192.168.1.152 11212
Trying 192.168.1.152...
Connected to 192.168.1.152.
Escape character is '^]'.
get key1
VALUE key1 0 1
1
END
get key2
END
get key3
END
get key4
END
get key5
END
get key6
END
get key7
VALUE key7 0 1
7
END
quit
Connection closed by foreign host.
[root@localhost ~]# telnet 192.168.1.153 11211
Trying 192.168.1.153...
Connected to 192.168.1.153.
Escape character is '^]'.
get key1
END
get key2
VALUE key2 0 1
2
END
get key3
END
get key4
END
get key5
END
get key6
END
get key7
END
quit
Connection closed by foreign host.
[root@localhost ~]# telnet 192.168.1.153 11212
Trying 192.168.1.153...
Connected to 192.168.1.153.
Escape character is '^]'.
get key1
END
get key2
END
get key3
VALUE key3 0 1
3
END
get key4
END
get key5
END
get key6
END
get key7
END
quit
Connection closed by foreign host.
[root@localhost ~]# telnet 192.168.1.105 11213
Trying 192.168.1.105...
Connected to 192.168.1.105.
Escape character is '^]'.
get key1
VALUE key1 0 1
1
END
get key2
VALUE key2 0 1
2
END
get key3
VALUE key3 0 1
3
END
get key4
VALUE key4 0 1
4
END
get key5
VALUE key5 0 1
5
END
get key6
VALUE key6 0 1
6
END
get key7
VALUE key7 0 1
7
END
quit
Connection closed by foreign host.

telnet :No route to host
解决方法:
在目标机器上执行:iptables -F


以上只测试了各值的分布情况,看值是否正确的存进去了,确认无误。至于宕机与重启还有调用备份的magent服务的测试这边不贴出来了。不过接着需要解决一个问题,那就是宕机情况下,可以从备份的机器上获取到宕机上原有的数据,可是宕机重启后,该重启机器上的值为空了,获取不到对应的值,该集群系统不会自动再去备份的机器上去取相关的数据了

magent:处理了备份数据,若其中一台缓存服务器宕机,magent通过访问备份的缓存服务器,同样能获取到数据。但是重启了缓存服务器,magent会再次请求此缓存服务器,不再请求备份服务器,却由于重启的缓存服务器上面的数据丢失,从而取不到相应的数据了。 

附:

magent的操作与memcache的操作一致,但具体部分返回结果所有差别

启动命令
下表仅列出比较常用的参数,更多参数使用memcached –h进行查看


吐槽memcached proxy 之memagent
 1.写入数据
<command name> <key> <flags> <exptime> <bytes>\r <data block>\r
其中“\r”表示回车换行
<command name> 可以是”set”, ”add”, ”replace”
“set”表示按照相应的<key>存储该数据,没有的时候增加,有的覆盖。
“add”表示按照相应的<key>添加该数据,但是如果该<key>已经存在则会操作失败。
“replace”表示按照相应的<key>替换数据,但是如果该<key>不存在则操作失败。
<key> 客户端需要保存数据的key
<flags> 是一个16位的无符号的整数(以十进制的方式表示)
该标志将和需要存储的数据一起存储,并在客户端get数据时返回。
客户可以将此标志用做特殊用途,此标志对服务器来说是不透明的。
<exptime> 过期的时间
若为0表示存储的数据永远不过时(但可被服务器算法:LRU等替换)。
如果非0(unix时间或者距离此时的秒数),当过期后,服务器可以保证用户得不到该数据(以服务器时间为标准)。
<bytes> 需要存储的字节数(不包含最后的”\r”)
当用户希望存储空数据时,<bytes>可以为0,最后客户端需要加上”\r”作为”命令头”的结束标志。
<data block>\r 数据内容
紧接着”命令头”结束之后就要发送数据块(即希望存储的数据内容),最后加上”\r”作为此次通讯的结束。
2.获取数据
get <key>*\r
<key>* 表示一个或者多个key(以空格分开)
“\r” 命令头的结束
3.删除数据
delete <key> <time>\r
<key> 需要被删除数据的key
<time> 客户端希望服务器将该数据删除的时间(unix时间或者从现在开始的秒数)
“\r” 命令头的结束

相关推荐