一、Memcached群集


  • Memcached由Memcached服务端和Memcache客户端组成,其中分布式缓存效果必须由客户端实现,但其分布式是个伪集群,Memcached各节点之间不通信,无数据备份,负载均衡功能由客户端实现
  • Memcached本身是基于内存的缓存,设计本身没有冗余机制;如果一个Memcached节点失去了所有的数据(如断电、重启等),理论上后端的应用程序可以从数据库中再次获取到数据,但当访问量大时会大大加重数据库的负担,这时可以通过增加更多的节点来减少丢失一个节点的影响,热备节点在其他节点宕机的时候接管VIP;但是多台节点无法同步数据,容易造成单点故障

二、实现Memcached缓存集群的方式


1.Repcached

  • Repcached(replication cached,高可用技术,简称复制缓冲区技术),Repcached主要优点是数据冗余,两台Memcached都可以进行读写操作,但只支持单Master、单Slave的解决方案,所以受到的局限性很大;使用过程中Repcached必须与Memcached版本一致(当然也可下载整合包Memcached,包里自带Repcached) 注:虽然Repcached解决方案单独使用有局限性,但一般环境中,会配合其余软件一起使用(如Repcached+Magent+Monit:Repcached负责单主单从的同步备份,Magent代理实现N主N备,Monit监听以上组件的各个实例端口,保证故障自动重启)
  • (日本人发明的memcached的高可用性技术,简称复制缓冲区技术。单master单slave的方案,但master/slave都是可读写的,而且可以相互同步。如果master宕机,slave侦测到连接断了,它会自动listen而成为master,并且等待新的从节点加入。如原来挂掉的master恢复之后,只能人工手动以从节点的方式去启动,原来的主节点并不能抢占成为新的主节点,除非新的主节点(即slave)挂掉;这也就意味着,基于Repcached实现的 Memcached主从,针对主节点并不具备抢占功能。假设主从节点都挂掉,则数据就丢失了!因此,这是Repcached的一个短板,不过可以通过结合其它的工具来弥补这个缺点(如Keepalived)。而如果slave坏掉,master也会侦测到连接断开,会重新listen等待新的slave加入)

2.Magent

  • Magent每次写数据都会写到主Memcached和从Memcached上,并且向主从Memcached写的算法一样
  • 当主Memcached冗机,Magent会向从Memcached中读取数据
  • 当主Memcached恢复后,Magent将重新向主Memcached中读数据;此时由于主Memcached刚恢复,其中并无数据,因此会导致部分数据无法读取,这也是Magent的一大缺点

注:Magent用于实现多主多从结构,但各节点间不会同步数据,并且从节点只用于备用,请求时不会从从节点请求数据(主节点损坏除外)

  • 在生产环境中主Memcached宕掉的可能性非常小,大部分时间都是工作的;而从Memcached只是在主Memcached宕掉后才使用,因此从Memcached分配的空间不可能和主Memcached一样,这样无疑是在浪费宝贵的内存空间
  • 既然从Memcached分配空间较小,而随着存入的数据会越来越多,会导致缓存的数据不断被过期驱逐出内存,因此在主Memcached宕掉后,从Memcached只能暂时起到缓解数据库压力的作用
  • 主Memcached宕掉后,不宜直接将其启动,应在数据库压力较小的时候再启动,当预热缓存(大量的同时请求会造成节点拥堵)
  • 还可部署两台Magent节点,实现Memcached入口的负载均衡,也就是说读写请求按照一定的算法分配到两个Magent入口上,一个专处理读请求、另一个专处理写请求,既能达到高可用,还能起到负载均衡
  • 通过Magent缓存代理,防止单点现象,通过客户端(Memcache)连接到缓存代理服务器(Magent),缓存代理服务器(Magent)连接缓存服务器(Memcached),缓存代理服务器(Magent)可以连接多个Memcached,但是如果缓存代理服务器(Magent)故障,那么缓存代理服务器(Magent)将无法继续提供服务,所以配合Keepalived软件实现高可用

注:Memcache前面已经说过一般和Web一起部署,而Magent一般和Memcached一起部署,关于Memcache怎么和Magent调用合作,由开发人员通过JAVA调用写入(或自学JAVA配置)

三、Magent实现方案


Magent

  • M1(Memcached)做主,M2(Memcached)做备份,此时利用MA(Magent)调度两个Memcached,可以实现当M1宕机时,MA可以从M2中获取数据,对用户来说无影响
  • 缺点:
  • M1宕机时可以从M2获取数据,但当M1恢复时,MA无法从中获取数据(M1由于故障,再启动缓存数据丢失)。虽然当有N个主机时,Memcached数据丢失的量仅为1/N,但M1宕机时从M2获取数据效率不高
  • M1重启时无法从M2中同步到数据

Magent+Repcached

  • 首先,通过MA-USER(Memcache)到MA-1(Magent)、MA-2(Magent)这层实现负载分担(一读、一写或者轮询),通过多MA-n(Magent)来有效利用系统的硬件资源来快速响应数据访问的请求
  • 其次通过MA-1到M1-S(Memcached)、M1-B(Memcached)这层来实现单点故障恢复
  • 具体来说,当M1-S宕机时,M1-B将自动成为主机,这样当M1-S重启恢复时有两点好处。第一:对于MA-USER来说,MA-1/MA-2的逻辑顺序没有发生改变,这对于存在MA-MGR的系统是有好处的,因为MA-USER在分配key到MA-n上时使用的是简单的散列余数算法。第二:对于MA-1来说,由于MA-1在初始化的时候就已经指定主备关系了,使用过程中并不会修改MA-1的设置,所以无论MA-1中的主备关系如何倒换,都会屏蔽在MA-n层面,将不会影响后续相关的扩展和移植 案例:Repcached+Magent+Monit+Memcached

案例:Magent+Memcached群集


主机 系统 IP 网卡 软件
Magent_1 Centos 6.7 64Bit 192.168.1.10 vmnet1(桥接) Magent、Keepalived
Magent_2 Centos 6.7 64Bit 192.168.1.20 vmnet1(桥接) Magent、Keepalived
Memcached_1 Centos 6.7 64Bit 192.168.1.100 vmnet1 memcached、libevent
Memcached_2 Centos 6.7 64Bit 192.168.1.200 vmnet1 memcached、libevent
测试机 Centos 6.7 64Bit 192.168.1.111 vmnet1 telnet

Memcached_1(Server)

1.环境准备(Memcached_1)

vim /etc/sysconfig/network-scripts/ifcfg-eth0
DEVICE=eth0								//网卡名称
TYPE=Ethernet							//网卡类型为以太网
ONBOOT=yes							//开机自启该网卡
NM_CONTROLLED=no						//关闭NetworkManager
BOOTPROTO=static						//网卡设置为静态方式
IPADDR=192.168.1.100					//IP地址配置
NETMASK=255.255.255.0					//子网掩码配置
/etc/init.d/network restart     //重启网络服务

2.安装Memcached(Memcached_1) 安装libevent libevent是memcached所依赖的异步时间通知库,作为Memcached的依赖需要先完成安装

tar -zxvf libevent-1.4.9-stable.tar.gz -C /usr/src/
cd /usr/src/libevent-1.4.9-stable/
./configure --prefix=/usr/local/libevent
make && make install

安装Memcached

tar -zxvf memcached-1.2.6.tar.gz -C /usr/src/
cd /usr/src/memcached-1.2.6/
./configure --prefix=/usr/local/memcached --with-libevent=/usr/local/libevent

选项: --with-libevent:指定libevent事件库位置

make && make install
echo "PATH=$PATH:/usr/local/memcached/bin">>/etc/profile
source /etc/profile
vim /etc/ld.so.conf				//打开系统额外加载库定义文件
/usr/local/libevent/lib			//增加libevent事件库文件夹路径
ldconfig							//重新读取/etc/ld.so.conf文件内容

3.启动Memcached服务(Memcached_1)

memcached -d -m 1 -u root -l 192.168.1.100 -p 11211
netstat -utpln |grep 11211

Memcached_2(Server)

1.环境准备(Memcached_2)

vim /etc/sysconfig/network-scripts/ifcfg-eth0
DEVICE=eth0								//网卡名称
TYPE=Ethernet							//网卡类型为以太网
ONBOOT=yes							//开机自启该网卡
NM_CONTROLLED=no						//关闭NetworkManager
BOOTPROTO=static						//网卡设置为静态方式
IPADDR=192.168.1.200					//IP地址配置
 NETMASK=255.255.255.0					//子网掩码配置
/etc/init.d/network restart     //重启网络服务

2.安装Memcached(Memcached_2) 安装libevent libevent是memcached所依赖的异步时间通知库,作为Memcached的依赖需要先完成安装

tar -zxvf libevent-1.4.9-stable.tar.gz -C /usr/src/
cd /usr/src/libevent-1.4.9-stable/
./configure --prefix=/usr/local/libevent
make && make install

安装Memcached

tar -zxvf memcached-1.2.6.tar.gz -C /usr/src/
cd /usr/src/memcached-1.2.6/
./configure --prefix=/usr/local/memcached --with-libevent=/usr/local/libevent

选项: --with-libevent:指定libevent事件库位置

make && make install
echo "PATH=$PATH:/usr/local/memcached/bin">>/etc/profile
source /etc/profile
vim /etc/ld.so.conf				//打开系统额外加载库定义文件
/usr/local/libevent/lib			//增加libevent事件库文件夹路径
ldconfig							//重新读取/etc/ld.so.conf文件内容

3.启动Memcached服务(Memcached_2)

memcached -d -m 1 -u root -l 192.168.1.200 -p 11211
netstat -utpln |grep 11211

Magent_1(Client)

1.环境准备(Magent_1)

vim /etc/sysconfig/network-scripts/ifcfg-eth0
DEVICE=eth0								//网卡名称
TYPE=Ethernet							//网卡类型为以太网
ONBOOT=yes							//开机自启该网卡
NM_CONTROLLED=no						//关闭NetworkManager
BOOTPROTO=static						//网卡设置为静态方式
IPADDR=192.168.1.10						//IP地址配置
NETMASK=255.255.255.0					//子网掩码配置
cp /etc/sysconfig/network-scripts/ifcfg-eth0 /etc/sysconfig/network-scripts/ifcfg-eth1
DEVICE=eth1								//网卡名称
TYPE=Ethernet							//网卡类型为以太网
ONBOOT=yes							//开机自启该网卡
NM_CONTROLLED=no						//关闭NetworkManager
BOOTPROTO=dhcp						//网卡设置为动态方式
/etc/init.d/network restart     //重启网络服务

2.依赖安装(Magent_1)

yum clean all && yum repolist			//清除YUM缓存并重新生成
wget -O /etc/yum.repos.d/CentOS-Base.repo http://mirrors.aliyun.com/repo/Centos-6.repo
yum -y install libevent-devel

3.安装Magent(Magent1)

mkdir /usr/src/magent-0.5			//创建源码解压目录
tar -zxvf magent-0.5.tar.gz -C /usr/src/magent-0.5
cd /usr/src/magent-0.5
ls -l /usr/src/magent-0.5

注:Magent软件为源码程序,源码中默认已经执行.configure并生成一个Makefile文件,用户只需按需修改头部和Makefile文件,然后直接make即可

报错一:未寻找到event.h

由于编译编译libevent指定自定义安装路径,而软件一般寻找.h头文件搜寻是在/usr/include或者/usr/local/include的头文件目录中,所以找不到自定义的event.h头文件,只需将自定义路径下的event.h软链接或拷贝到/usr/include或者/usr/local/include的头文件目录即可

scp root@192.168.1.100:/usr/local/libevent/include/* /usr/local/include/

报错二:未寻找到event-config.h、evutil.h

报错原因与解决方案如上,不再阐述

scp root@192.168.1.100:/usr/local/libevent/include/* /usr/local/include/

报错三:未声明SSIZE_MAX函数

这是由于程序中需要调用SSIZE_MAX常量,而文件中未定义该常量,所以在声明文件中定义该常量,并设置值即可

vim ketama.h
#ifndef SSIZE_MAX			//测试SSIZE_MAX是否被宏定义过
#define SSIZE_MAX 32767		//如果没有被宏定义过,定义并编译
#endif			//如已经定义,则忽视define内容,继续执行文件剩余内容
        ...							//上面三行在文件开头增加

注:编辑文件时,会有两个同名的ketama文件,但后缀不同,ketama.c一般存储具体功能的实现、ketama.h称为头文件,一般存储类型的定义,函数的声明等(当然两个文件本质没有区别,只是方便开发人员识别),所以应该编辑的是ketama.h,不要编辑错了

报错四:未定义undefined函数

floor是数学库里提供的函数,默认gcc不会自动链接math库(数学库),因此需要手动在Makefile文件中指定-lm,编译程序时需使用数学库中函数

vim Makefile
        LIBS = -levent -lm					//在原选项后增加新参数
make								//编译成功后,在源码目录生成可执行文件
cp magent /usr/bin					//将可执行程序拷贝到PATH搜索路径下

安装Keepalived(Magent_1)

yum -y install keepalived				//YUM安装Keepalived软件包
vim /etc/keepalived/keepalived.conf
global_defs {
//配置故障发生时的通知对象以及机器标识
router_id MASTER	
//标识本节点的字条串,通常为hostname
}
vrrp_script magent {
//健康检查(防止脑裂)
script "/opt/magent.sh"
//检查执行的脚本或命令,这里主要判断keepalived状态,正常则启动Magent
interval 1
//脚本运行间隔
weight -10
//当检测脚本执行失败,priority的优先级会减少10个点;不设置从节点也可抢占
}
vrrp_instance VI_1 {
//定义对外提供服务的VIP区域及其相关属性
state MASTER
//可以是MASTER或BACKUP,不过当其他节点keepalived启动时会将priority比较大的节点选举为MASTER,因此该项其实没有实质用途
interface eth0
//节点固有IP的网卡,用来发VRRP包
virtual_router_id 51
//取值在0-255之间,用于区分多个instance(节点)的VRRP组播
priority 100
//选举master,要成为master,那么这个选项的值最好高于其他机器50个点,该项取值范围是1-255(在此范围之外会被识别成默认值100)
advert_int 1
//发VRRP包的时间间隔,即多久进行一次master选举(可以认为是健康查检时间间隔)
authentication {
//认证区域,认证类型有PASS和HA(IPSEC),推荐使用PASS(密码只识别前8位)
auth_type PASS
//指定认证类型为PASS
auth_pass 1111
//指定认证密码,多个节点密钥相当
}
track_script {
//定义需要追踪执行的脚本
magent
//执行的脚本名
}
virtual_ipaddress {
//VIP地址定义
192.168.1.254
//指定使用的VIP
}
}

编写magent 监测脚本(Magent_1)

vim /opt/magent.sh
#!/bin/bash
//定义Shell解释器
KEEPALIVED=`ps -ef |grep keepalived |grep -v grep |wc -l`
//定义变量,用于统计当前主机keepalived服务进程数量
if [ $KEEPALIVED -gt 0 ];then
//判断,如KEEPALIVED变量值大于KEEPALIVED=`ps -ef |grep keepalived |grep -v grep |wc -l`0,则表示已产生keepalived进程、服务已经状态开启
magent -u root -n 51200 -l 192.168.1.254 -p 12000 -s 192.168.1.100:11211 -b 192.168.1.200:11211
//当Keepalived服务开启时,启动magent进程,指定群集IP地址及端口,并定义后端主Memcached和从Memcached服务器节点及端口,当前主机开始提供服务
else
//如KEEPALIVED变量值小于0,则表示Keppalived停止或未运行
pkill -9 magent
//杀死magent进程(如果之前启动过的话),以让从节点启动magent并继续提供服务
fi
chmod +x /opt/magent.sh
/etc/init.d/keepalived start
ip a
ps aux | grep magent

Magent_2

1.环境准备(Magent_2)

vim /etc/sysconfig/network-scripts/ifcfg-eth0
DEVICE=eth0								//网卡名称
TYPE=Ethernet							//网卡类型为以太网
ONBOOT=yes							//开机自启该网卡
NM_CONTROLLED=no						//关闭NetworkManager
BOOTPROTO=static						//网卡设置为静态方式
IPADDR=192.168.1.200					//IP地址配置
NETMASK=255.255.255.0					//子网掩码配置
cp /etc/sysconfig/network-scripts/ifcfg-eth0  /etc/sysconfig/network-scripts/ifcfg-eth1
DEVICE=eth1								//网卡名称
TYPE=Ethernet							//网卡类型为以太网
ONBOOT=yes							//开机自启该网卡
NM_CONTROLLED=no						//关闭NetworkManager
BOOTPROTO=dhcp						//网卡设置为动态方式
/etc/init.d/network restart     //重启网络服务

2.依赖安装(Magent_2)

yum clean all && yum repolist			//清除YUM缓存并重新生成
wget -O /etc/yum.repos.d/CentOS-Base.repo http://mirrors.aliyun.com/repo/Centos-6.repo
yum -y install libevent-devel

3.安装Magent(Magent_2) 由于Magent_1主机已经安装并生成了magent命令,因此Magent_2只需拷贝命令到本地PATH路径即可

scp root@192.168.1.10:/usr/bin/magent /usr/bin/

注:注意检查两边主机是否安装openssh-clients软件包

安装Keepalived(Magent_2)

yum -y install keepalived				//YUM安装Keepalived软件包

一样,由于Magent_1的Keepalived已经配置好,这里只需拷贝到本地,修改即可使用

scp root@192.168.1.10:/etc/keepalived/keepalived.conf /etc/keepalived/
vim /etc/keepalived/keepalived.conf

编写magent监控脚本(Magent_2)

vim /opt/magent.sh
#!/bin/bash
//定义Shell解释器
VIP=$(ip a |grep 192.168.1.254 | wc -l)
//定义变量,用于查看当前是否存在VIP(有即代表主故障,VIP转移到从主机)
if [ $VIP -gt 0 ];then
//判断,如VIP变量值大于0,则表示VIP已经转移,当前主机继续提供服务
magent -u root -n 51200 -l 192.168.1.254 -p 12000 -s 192.168.1.100:11211 -b 192.168.1.200:11211
//当VIP监听时,启动magent进程,指定群集IP地址及端口,并定义后端主Memcached和从Memcached服务器节点及端口,当前主机开始提供服务
else
//如VIP变量值小于0,则表示VIP未监听,主节点依然正常工作着
pkill -9 magent
//杀死magent进程(如果之前启动过的话),以让主节点启动magent并继续提供服务
fi
chmod +x /opt/magent.sh
/etc/init.d/keepalived start

测试

1.测试高可用

/etc/init.d/keepalived stop					//停止主节点Keepalived,VIP转移
ip a									//主节点故障,VIP自动转移到从节点

2.环境准备(测试机)

vim /etc/sysconfig/network-scripts/ifcfg-eth0
DEVICE=eth0								//网卡名称
TYPE=Ethernet							//网卡类型为以太网
ONBOOT=yes							//开机自启该网卡
NM_CONTROLLED=no						//关闭NetworkManager
BOOTPROTO=static						//网卡设置为静态方式
IPADDR=192.168.1.111					//IP地址配置
NETMASK=255.255.255.0					//子网掩码配置
/etc/init.d/network restart     //重启网络服务

3.缓存测试

yum clean all && yum repolist			//清除YUM缓存并重新生成
mount /dev/cdrom /mnt/				//挂载光盘到/mnt/目录
yum -y install telnet					//安装远程登陆软件

4.VIP测试

telnet 192.168.1.254 12000			//连接Magent的VIP及端口
        Trying 192.168.1.254...
        Connected to 192.168.1.254.
        Escape character is '^]'.
set test 0 0 4				//新建键值test,长度4位                
hehe							//test键值数据内容
        STORED
quit								//退出
        Connection closed by foreign host.
telnet 192.168.1.254 12000			//再次连接Magent的VIP及端口
        Trying 192.168.1.254...
        Connected to 192.168.1.254.
        Escape character is '^]'.
get test							//查看刚插入test键值内容
VALUE test 0 4
hehe
        END
quit								//退出
        Connection closed by foreign host.

5.主节点测试

telnet 192.168.1.100 11211					//连接主节点及端口
        Trying 192.168.1.100...
        Connected to 192.168.1.100.
        Escape character is '^]'.
get test							//查看主节点是否能得到数据
VALUE test 0 4
hehe
        END
quit								//退出
        Connection closed by foreign host.

6.从节点测试

telnet 192.168.1.200 11211					//连接从节点及端口
        Trying 192.168.1.200...
        Connected to 192.168.1.200.
        Escape character is '^]'.
get test							//查看从节点是否能得到数据
VALUE test 0 4
hehe
        END
quit								//退出
        Connection closed by foreign host.

注:可以看见,往VIP插入的数据,主、备缓存节点都有了手动插入的值 模拟主节点故障

 pkill memcached						//杀死主节点Memcached进程
telnet 192.168.1.100 12000			//连接Magent的VIP及端口
        Trying 192.168.1.100...
        Connected to 192.168.1.100.
        Escape character is '^]'.
get test							//验证VIP是否还能查询到数据
VALUE test 0 4
hehe
        END
quit								//退出
        Connection closed by foreign host.
    以上说明 memcached 单节点故障,缓存依然存在

注意:如果将故障的memcached节点修复后,缓存是不会再通过到已修复的节点上的,如果是magent指定的主节点故障,那么主节点的缓存数据会丢失,修复后不能立刻重启 memcached服务,如果重启,客户端会去查询主节点的数据,并发高的网站会拖死数据库; 因此,建议一般业务低峰期的时候再启动memcached主节点服务,然后通过magent再指 定主缓存节点和备缓存节点