传统 WEB 架构的问题
许多 WEB 应用都将数据保存到 RDBMS(关系型数据库→磁盘)中,应用服务器从数据 库中读取数据并在浏览器中显示,随着数据量的增大,用户访问的集中,网站架构中数据库 服务器的负担加重,数据库响应恶化,出现网站显示延迟等居多问题.
哪些数据需要存储在内存里?
Memcached 是一套高性能内存对象缓存系统(KEY-VALUE,键值对),用于解决一些高 负载的 Web 网站,主要作用是通过缓存数据库查询结果,减少数据库访问次数,以提高动 态 Web 应用的响应速度、提高可扩展性。缓存方式是将缓存结果存储在内存中,通过内存来 维护一个 hash 表。
Memcached 是典型的 C/S 架构,默认通过端口 11211 工作。因此需要安装服务器端与 客户端。服务器端是用 C 语言编写的,客户端可用任何语言来编写,如 PHP、Python、 Perl 等。
为了提高性能 Memcached 中保存的数据都存储在 memcached 内置的内存存储空间中, 读取速度快。由于数据仅存在于内存中,因此重启 memcached、重启操作系统会导致全部 数据消失。另外,缓存的数据量达到指定值或者过期之后,就基于 LRU(Least Recently Used)算法自动删除不使用的缓存。memcached 本身是为缓存而设计的服务器,因此并没 有过多考虑数据的永久性问题。
官方网站:http://www.memcached.org/
首先,Web 客户端通过浏览器发出数据的请求到 Web 服务器的应用程序上,应用程序调用 Memcached 客户端程序库接口,连接 Memcached 服务器。
如果此时 Web 客户端所请求的数据在 Memcached 中已经缓存,则 Memcached 将数据
返回给 Web 客户端。
如果 Memcached 没有缓存 Web 客户端所请求的数据,则由 Memcache 客户端程序将请求转发给数据库服务器,数据库服务器将数据返回给 Memcached 客户端程序,然后返回给 Web 客户端,同时 Memcache 客户端程序将数据缓存到 Memcached 服务器,以备下次有客户端请求同样的数据。
memcached 是以守护程序方式运行于一个或多个服务器中,随时会接收客户端的连接和操作。
Apache Php: 192.168.6.10 httpd、php、memcache
Memcached: 192.168.6.11 libevent、memcached
Memcached: 192.168.6.12 libevent、memcached
memcache客户端(192.168.6.10)
[root@apache ~]# cd /etc/yum.repos.d/
[root@apache yum.repos.d]# mv backup/CentOS-Base.repo ./
[root@apache yum.repos.d]# yum -y install php apached php-devel
上传memcache包
[root@apache ~]# tar xf memcache-2.2.7.tgz
[root@apache ~]# cd memcache-2.2.7/
[root@apache memcache-2.2.7]# phpize
Configuring for:
PHP Api Version: 20100412
Zend Module Api No: 20100525
Zend Extension Api No: 220100525
[root@apache memcache-2.2.7]# which php-config
/usr/bin/php-config
[root@apache memcache-2.2.7]# ./configure --enable-memcache --with-php-config=/usr/bin/php-config && make && make install
编辑php主配置文件
[root@apache ~]# vim /etc/php.ini
extension_dir = "/usr/lib64/php/modules/"
extension = memcache.so
[root@apache ~]# ls /usr/lib64/php/modules/ | grep mem
memcache.so
编辑测试页面
[root@apache ~]# vim /var/www/html/mem.php
<?php
$memcache = new Memcache();
$memcache->connect('192.168.6.11',11211);
$memcache->set('key','Mencache test successful!',0,60);
$result = $memcache->get('key');
unset($memcache);
echo $result;
?>
[root@apache ~]# systemctl start httpd
两台memcached配置(6.11 6.12)
[root@memcached1 ~]# yum -y install memcached libevent
[root@memcached1 ~]# memcached -d -m 32m -p 11211 -u root
[root@memcached1 ~]# netstat -anpt | grep 11211
tcp 0 0 0.0.0.0:11211 0.0.0.0:* LISTEN 67942/memcached
tcp6 0 0 :::11211 :::* LISTEN 67942/memcached
#/usr/local/memcached/bin/memcached -d -m 2048 -u root -l 192.168.6.11 -p 12111 -c
1024 -P /tmp/memcached.pid
参数说明:
-d 启动为守护进程
-m 分配给 Memcached 使用的内存数量,单位是 MB,默认为 64MB
-u 运行 Memcached 的用户,仅当作为 root 运行时
-l ( 小写 L)监听的服务器 IP 地址,默认为环境变量 INDRR_ANY 的值
-p 设置 Memcached 监听的端口,最好是 1024 以上的端口
-c 设置最大并发连接数,默认为 1024
-P 设置保存 Memcached 的 pid 文件,与-d 选择同时使用
Memcached 每个被存取的对象都有唯一的标识符“key”,存取操作均通过 key 进行,例如可以把后端数据库中的 select 操作提取出来,然后对相应的 SQL 进行 hash 计算得出 key,然后以这个 key 在 memcached 中查找数据,如果数据不存在,说明其尚未被写入缓存中,并设置一个失效时间(比如 1 小时),在失效时间内的数据都是从缓存中提取,这样就有效地减少了数据库的压力。
数据存取命令
set:是保存一个叫做 key 的数据到服务器上
add:是添加一个数据到服务器,但是服务器必须这个 key 是不存在的,能够保证数据不会被覆盖
replace:是替换一个已经存在的数据,如果数据不存在,就是类set 功能
get:格式是:get <键>*<键>key 是一个不为空的字符串组合,发送这个指令以后,等待服务器的返回。如果服务器端没有任何数据,则是返回:END,证明没有不存在这个 key,没有任何数据,如果存在数据,则返回指定格式:
VALUE <键> <标记> <数据长度>
delete:删除指令
delete <键> <超时时间>
<超时时间> - timeout
按照秒为单位,这个是个可选项,如果你没有指定这个值,那么服务器上 key 数据将马上被删除,如果设置了这个值,那么数据将在超时时间后把数据清除,该项缺省值是 0 ,就是马上被删除,删除数据后,服务器端会返 DELETED 删除数据成功,NOT_FOUND\r\n:这个 key 没有在服务器上找到
flush_all:这个指令执行后,服务器上所有缓存的数据都被删除,并且返回在 php 里也可以用 getStats()来查看。
quit :退出
示例
[root@Memcached-1 ~] yum -y install telnet
[root@Memcached-1 ~] telnet 192.168.6.11 11211
Trying 192.168.6.11...
Connected to 192.168.6.10.
Escape character is '^]'.
set key 0 60 10
I love you
STORED
get key
VALUE key 0 10
I love you
END
stats
STAT pid 12863 //memcached 启动的进程 ID
STAT uptime 4008 //到目前为止启动了多少秒
STAT time 1464713917 //服务器当前的 unix 时间戳
STAT version 1.2.6 //memcached 的版本信息
STAT pointer_size 64 //当前操作系统的指针大小(64 位系统一般是 64bit)
STAT rusage_user 0.278957 //进程的累计用户时间
STAT rusage_system 0.371943 //进程的累计系统时间
STAT curr_items 1 //服务器当前存储的 items 数量
STAT total_items 2 //从服务器启动以后存储的 items 总数量
STAT bytes 79 //当前服务器存储 items 占用的字节数
STAT curr_connections 2 //当前的并发连接数
STAT total_connections 4 //从服务器启动以后曾经打开过的连接数
STAT connection_structures 3 //服务器分配的连接构造数
STAT cmd_get 2 //执行 get 命令的次数
STAT cmd_set 2 //执行 set 命令的次数
STAT get_hits 2 //get 的命中次数
STAT get_misses 0 //get 的非命中数
STAT evictions 0 //为获取空闲内存而删除的 items 数(分配给 memcache
的空间用满后需要删除旧的 items 来得到空间分配给新的 items)
STAT bytes_read 98 //总读取字节数(请求字节数)
STAT bytes_written 97 //总发送字节数(结果字节数)
STAT limit_maxbytes 33554432 //分配给 memcache 的内存大小(字节)
STAT threads 1 //当前进程数
END
quit //退出
Connection closed by foreign host.
1、Memcached 的协议的错误部分主要是三个错误提示之提示指令:
普通错误信息:ERROR
客户端错误:CLIENT_ERROR <错误信息>
服务器端错误:SERVER_ERROR <错误信息>
2 、数据保存指令
数据保存是基本的功能,就是客户端通过命令把数据返回过来,服务器端接收后进行处理。
指令格式:<命令> <键> <标记> <有效期> <数据长度>
set key 0 60 10
<键 key> :就是保存在服务器上唯一的一个表示符
<标记 flag> 一个 16 位的无符号整形,用来设置服务器端跟客户端一些交互的操作
<有效期>是数据在服务器上的有效期限,如果是 0,则数据永远有效,单位是秒,Memcached服务器端会把一个数据的有效期设置为当前 Unix 时间+设置的有效时间
<数据长度>块数据的长度,一般在这个个长度结束以后下一行跟着 block data 数据内容,发送完数据以后,客户端一般等待服务器端的返回,服务器端的返回:数据保存成功(STORED),数据保存失败(NOT_STORED),一般是因为服务器端这个数据 key 已经存在了
案例环境
设备名称 | 主机名 | ip | 安装的服务 |
---|---|---|---|
主缓存节点 | Master | 192.168.1.111 | magent、memcached、libevent、keepalived |
备缓存节点 | Backup | 192.168.1.112 | magent、memcached、libevent、keepalived |
客户端 | Localhost | 192.168.1.113 | telnet |
实验前前先关闭三台机器的防火墙和selinux。
[root@localhost ~] iptables -F
[root@localhost ~] systemctl stop firewalld
[root@localhost ~] systemclt diable firewalld
[root@localhost ~] setenforce 0
[root@localhost ~] sed -i '/SELINUX/ s/enforcing/disabled/g' /etc/sysconfig/selinux
libevent是memcached所依赖的异步时间通知库,需要先完成安装
[root@Master ~] tar xf libevent-2.1.10-stable.tar.gz
[root@Master ~] cd /usr/src/libevent-2.1.10-stable
[root@Master libevent-2.1.10-stable] ./configure --prefix=/usr && make && make install
[root@Master ~] yum -y install memcached
[root@Master ~] ./memcached -d -m 32m -p 11211 -u root
[root@Master ~] netstat -anpt | grep 11211
tcp 0 0 0.0.0.0:11211 0.0.0.0:* LISTEN 9173/memcached
tcp6 0 0 :::11211 :::* LISTEN 9173/memcached
由于libevent已经安装,考虑到安装位置的问题,所以客户机节点安装magent,但是客户机本身不需要,目的只是为了产生magent程序文件(绿色的magent)。
[root@localhost ~] yum -y remove libevent
[root@localhost ~] rz #上传libevent-2.1.10-stable.tar.gz这个包
[root@localhost ~] tar xf libevent-2.1.10-stable.tar.gz -C /usr/src/
[root@localhost ~] cd /usr/src/libevent-2.1.10-stable/
[root@localhost libevent-2.1.10-stable] ./configure --prefix=/usr && make && make install
[root@localhost ~] mkdir magent
[root@localhost ~] rz #上传magent-0.5.tar.gz这个包
[root@localhost ~] tar xf magent-0.5.tar.gz -C magent/
[root@localhost ~] cd /magent
[root@localhost magent] ls
ketama.c ketama.h magent.c Makefile
[root@Localhost magent] vim ketama.h
1 #ifndef SSIZE_MAX //手动添加此三行在文件开头处 手动添加此三行在文件开头处(不要加数字)
2 #define SSIZE_MAX 32767
3 #endif
[root@Localhost magent] ldconfig
[root@Localhost magent] sed -i '1 s/$/ -lm/' Makefile
[root@Localhost magent] head -1 Makefile
LIBS = -levent -lm
[root@Localhost magent] make
gcc -Wall -O2 -g -c -o magent.o magent.c
gcc -Wall -O2 -g -c -o ketama.o ketama.c
gcc -Wall -O2 -g -o magent magent.o ketama.o -levent -lm
#如果make报错就是libevent安装路径指错了,从新编译libevent
[root@Localhost magent] ls
ketama.c ketama.h ketama.o magent magent.c magent.o Makefile
[root@Localhost magent] scp magent 192.168.1.111:/usr/bin/
[root@Localhost magent] scp magent 192.168.1.112:/usr/bin/
主缓存节点配置
[root@Master ~] chmod +x /usr/bin/magent
[root@Master ~] yum -y install keepalived
[root@Master ~] cd /etc/keepalived/
[root@Master keepalived] cp keepalived.conf keepalived.conf.bak
[root@Master keepalived] vim keepalived.conf
! Configuration File for keepalived
global_defs {
notification_email {
[email protected]
}
notification_email_from [email protected]
smtp_server 127.0.0.1
smtp_connect_timeout 30
router_id LVS_MASTER
}
vrrp_script magent {
script "/opt/magent.sh"
interval 2
}
vrrp_instance VI_1 {
state MASTER
interface ens32 #网卡名称
virtual_router_id 51
priority 100
advert_int 1
authentication {
auth_type PASS
auth_pass 1111
}
track_script {
magent
}
virtual_ipaddress {
192.168.1.100 #VIP
}
}
备缓存节点配置
[root@Backup ~] chmod +x /usr/bin/magent
[root@Backup ~] yum -y install keepalived
[root@Backup ~] cd /etc/keepalived/
[root@Backup keepalived] cp keepalived.conf keepalived.conf.bak
[root@Backup keepalived] vim keepalived.conf
! Configuration File for keepalived
global_defs {
notification_email {
[email protected]
}
notification_email_from [email protected]
smtp_server 127.0.0.1
smtp_connect_timeout 30
router_id LVS_BACKUP
}
vrrp_script magent {
script "/opt/magent.sh"
interval 2
}
vrrp_instance VI_1 {
state BACKUP
interface ens32
virtual_router_id 51
priority 99
advert_int 1
authentication {
auth_type PASS
auth_pass 1111
}
track_script {
magent
}
virtual_ipaddress {
192.168.1.100
}
}
[root@Master ~] systemctl start keepalived
[root@Backup ~] systemctl start keepalived
magent参数详情:
-u:指定用户,以root用户运行
-n:最大的连接数,默认不指定的话是4096
-l:(小写L)magent对外监听的ip地址
-p:magent对外监听的端口
-s:设置memcached主缓存的ip地址和端口
-b:设置memcached备缓存的ip地址和端口
[root@Master ~] vim /opt/magent.sh
#!/bin/bash
k=`ps -ef |grep keepalived |grep -v grep |wc -l`
if [ $k -gt 0 ];then
magent -u root -n 51200 -l 192.168.1.100 -p 12000 -s 192.168.1.111:11211 -b 192.168.1.112:11211
else
pkill -9 magent
fi
[root@Master ~] chmod +x /opt/magent.sh
[root@Backup ~] vim /opt/magent.sh
#!/bin/bash
k=`ip a |grep 192.168.1.100 |wc -l`
if [ $k -gt 0 ];then
magent -u root -n 51200 -l 192.168.1.100 -p 12000 -s 192.168.1.111:11211 -b 192.168.1.112:11211
else
pkill -9 magent
fi
[root@Backup ~] chmod +x /opt/magent.sh
主缓存节点已经启动keepalived查看magent是否启动
[root@Master ~] bash /opt/magent.sh
magent: error while loading shared libraries: libevent-2.1.so.6: cannot open shared object file: No such file or directory
[root@Master ~] ldd /usr/bin/magent
linux-vdso.so.1 => (0x00007fff0e583000)
libevent-2.1.so.6 => not found
libm.so.6 => /lib64/libm.so.6 (0x00007f4310dc6000)
libc.so.6 => /lib64/libc.so.6 (0x00007f43109f9000)
/lib64/ld-linux-x86-64.so.2 (0x00007f43110c8000)
libevent-2.1.so.6 => not found发现magent缺少库文件(Backup同理)
[root@Master ~] ldd /usr/bin/magent
linux-vdso.so.1 => (0x00007fff0e583000)
libevent-2.1.so.6 => not found
libm.so.6 => /lib64/libm.so.6 (0x00007f4310dc6000)
libc.so.6 => /lib64/libc.so.6 (0x00007f43109f9000)
/lib64/ld-linux-x86-64.so.2 (0x00007f43110c8000)
[root@Master ~] find / -name libevent-2.1.so.6
/usr/src/libevent-2.1.10-stable/.libs/libevent-2.1.so.6
[root@Master ~] ln -s /usr/src/libevent-2.1.10-stable/.libs/libevent-2.1.so.6 /usr/lib
[root@Master ~] find / -name libevent-2.1.so.6
/usr/lib/libevent-2.1.so.6
/usr/src/libevent-2.1.10-stable/.libs/libevent-2.1.so.6
[root@Master ~] vim /etc/ld.so.conf
/usr/lib/libevent-2.1.so.6 #在最后一行加入改内容
[root@Master ~] ldconfig
[root@Master ~] ldd /usr/bin/magent
linux-vdso.so.1 => (0x00007ffe43dbc000)
libevent-2.1.so.6 => /lib/libevent-2.1.so.6 (0x00007f1349bf6000)
libm.so.6 => /lib64/libm.so.6 (0x00007f13498f4000)
libc.so.6 => /lib64/libc.so.6 (0x00007f1349527000)
libcrypto.so.10 => /lib64/libcrypto.so.10 (0x00007f13490c6000)
libpthread.so.0 => /lib64/libpthread.so.0 (0x00007f1348eaa000)
/lib64/ld-linux-x86-64.so.2 (0x00007f1349e4a000)
libdl.so.2 => /lib64/libdl.so.2 (0x00007f1348ca6000)
libz.so.1 => /lib64/libz.so.1 (0x00007f1348a90000)
[root@Master ~] systemctl restart keepalived
[root@Master ~] ip a | grep ens32
2: ens32: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
inet 192.168.1.111/24 brd 192.168.1.255 scope global noprefixroute ens32
inet 192.168.1.100/32 scope global ens32
测试高可用,停掉主的keepalived和magent(注意:当你停掉了keepalived你需要手动关闭magent,因为keepalived关闭时不会调用你写的/opt/magent.sh 的脚本)
[root@Master ~] systemctl stop keepalived
[root@Master ~] pkill -9 magent
查看备缓存节点
[root@Backup ~] ps aux | grep magent
root 78915 0.0 0.0 20684 532 ? Ss 22:40 0:00 magent -u root -n 51200 -l 192.168.6.200 -p 12000 -s 192.168.1.111:11211 -b 192.168.1.112:11211
root 78929 0.0 0.0 112724 988 pts/0 R+ 22:40 0:00 grep --color=auto magent
[root@Backup ~] netstat -anpt | grep 11211
tcp 0 0 0.0.0.0:11211 0.0.0.0:* LISTEN 15113/memcached
tcp6 0 0 :::11211 :::* LISTEN 15113/memcached
[root@Backup ~] ip a | grep ens32
2: ens32: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
inet 192.168.1.112/24 brd 192.168.1.255 scope global noprefixroute ens32
inet 192.168.1.100/32 scope global ens32