高性能内存对象缓存Memcached

Memcached 是一套开源的高性能分布式内存对象缓存系统,它将所有的数据都存储在内存中,因为内存中会统一维护一张巨大的 Hash 表,所以支持任意存储类型的数据。很多网站使用 Memcached 提高网站的访问速度,尤其是需要频繁访问数据的大型网站。

Memcached 是典型的 C/S 架构,因此需要构建 Memcached 服务器端与 Memcached API 客户端。Memcached 服务器端是用 C 语言编写的,而 Memcached API 客户端可用任何语言来编 写,如 PHP、Python、Perl 等,并通过 Memcached 协议与 Memcached 服务器端进行通信。 常用典型架构如下图所示:

2. memcache缓存 - 图1

当 Web 客户端发出请求到 Web 服务器的应用程序时,应用程序会调用 Memcached API 客户端程序库接口去连接 Memcached 服务器查询数据。如果 Web 客户端所请求的数据在 Memcached 服务端中已经缓存,则 Memcached 服务端会将数据返回给 Web 客户端;否则,会 将 Web 客户端请求发送至 MySQL 数据库,由数据库查询并返回请求的数据给 Memcached 以及 Web 客户端,与此同时 Memcached 服务器也会将数据进行保存,方便下次用户请求使用。

为何要使用Memcached,官方也给出了原因,主要是针对内存的使用:

2. memcache缓存 - 图2

一. Memcached核心概念

1.1 数据存储方式与数据过期方式

Memcached 具有独特的数据存储方式和数据过期方式。

数据存储方式:Slab Allocation

Slab Allocation即按组分配内存,每次先分配一个Slab,相当于一个大小为1M的页。然后,在 1M的空间里根据数据划分大小相同的Chunk。该方法可以有效解决内存碎片问题,但也可能会使内存空间产生浪费。

2. memcache缓存 - 图3

数据过期方式:LRU、Laxzy Expiration

LRU 和 Laxzy Expiration 是数据过期的两种方式:

  • LRU(最近最少使用):是指追加的数据空间不足时,会根据LRU的情况淘汰最近最少使用的记录;
  • Laxzy Expiration:即惰性过期,是指使用get时查看记录时间,从而检查记录是否已经过期。

1.2 Memcached 缓存机制

缓存是常驻在内存的数据,能够快速进行读取,而 Memcached 就是这样一款非常出色的缓存软件。当程序写入缓存数据请求时,Memcached 的 API 接口将 Key 输入路由算法模块路由到集群中一台服务,之后由 API 接口与服务器进行通信,完成一次分布式缓存写入。

2. memcache缓存 - 图4

1.3 Memcached 分布式

Memcached 分布式部署主要依赖于Memcached的客户来端实现,多个Memcached 服务器是独立的。分布式数据如何存储是由路由算法所决定。当数据到达客户端程序库,客户端的算法就依据路由算法来决定保存的 Memcached 服务器。读取数据时,客户端依据使用保存数据时相同的路由算法选中和存储数据时相同的服务器来读取数据。

2. memcache缓存 - 图5

1.4 Memcached 路由算法

求余数hash算法

求余数hash算法先用key做hash运算得到一个整数,再去做hash算法,根据 余数进行路由,这种算法适合大多数据需求。但是不适合用在动态变化的环境中,比如有大有量机器添加或者删除,这样会导致大量的对象存储位置失效。

一致性hash算法

一致性hash算法适合在动态变化的环境中使用。原理是按照hash算法把对应的 key通过一定的hash算法处理后映射形成一个首尾相接闭合循环,然后通过使用与对象存储一样的hash算法将机器也映射到环中,顺时针方向计算将所有对象存储到离自己最近的机器中。

2. memcache缓存 - 图6

二. 案例实施

本案例使用三台CentOS 7.7系统完成,其中:两台是Memcached服务器,另一台是基于LAMP架构进行 Memcached扩展的Memcached API客户端,具体需根据企业需求进行架构调整。

主机名 IP地址 主要软件
memcached01 192.168.154.101 memcached-1.5.22 libevent-2.1.11 magent-0.5 Keepalived v1.3.5
memcached02 192.168.154.102 memcached-1.5.22 libevent-2.1.11 magent-0.5 Keepalived v1.3.5
web-server 192.168.154.100 httpd-2.4.6 php-7.2.27 memcached-3.0.4 libmemcached-1.0.16

2.1 安装Memcached服务器

2.1.1 基本环境配置

  • IP地址配置
  • 主机名设置
  • 关闭Selinux

2.1.2 安装 Libevent

Libevent是一款跨平台的事件处理接口的封装,可以兼容多个操作系统的事件访问。Memcached的安装依赖于Libevent,因此需要先完成Libevent的安装。

  1. #下载并解压
  2. [root@memcached01 ~]# wget -c https://github.com/libevent/libevent/releases/download/release-2.1.11-stable/libevent-2.1.11-stable.tar.gz
  3. [root@memcached01 ~]# tar zxvf libevent-2.1.11-stable.tar.gz -C /usr/src/
  4. [root@memcached01 ~]# cd /usr/src/libevent-2.1.11-stable/
  5. #进行编译安装
  6. [root@memcached01 libevent-2.1.11-stable]# ./configure --prefix=/usr/local/libevent
  7. [root@memcached01 libevent-2.1.11-stable]# make
  8. [root@memcached01 libevent-2.1.11-stable]# make install

2.1.3 安装 Memcached

  1. #下载最新版本
  2. [root@memcached01 ~]# wget -c https://www.memcached.org/files/memcached-1.5.22.tar.gz
  3. #解压缩
  4. [root@memcached01 ~]# tar zxvf memcached-1.5.22.tar.gz -C /usr/src/
  5. #安装
  6. [root@memcached01 ~]# cd /usr/src/memcached-1.5.22/
  7. [root@memcached01 ~]# ./configure --prefix=/usr/local/memcached --with-libevent=/usr/local/libevent/
  8. [root@memcached01 ~]# make
  9. [root@memcached01 ~]# make install

2.1.4 设置 Memcached 服务脚本

Memcached 服务器安装完成后,可以使用安装目录下的 bin/memcached 来启动服务。但是,为了更加方便的管理 Memcached,编写脚本来管理 Memcached 服务。

  1. #准备工作
  2. [root@memcached01 ~]# groupadd -g 990 memcached
  3. [root@memcached01 ~]# useradd -u 990 -g memcached -s /sbin/nologin memcached
  4. [root@memcached01 ~]# vim /etc/sysconfig/memcached
  5. PORT="11211" #监听端口
  6. USER="memcached" #启动服务的用户
  7. MAXCONN="1024" #最大连接数
  8. CACHESIZE="512" #分配的内存大小
  9. OPTIONS=""
  10. #编写systemd的unit文件
  11. [root@memcached01 ~]# vim /usr/lib/systemd/system/memcached.service
  12. [Unit]
  13. Description=Memcached
  14. Before=httpd.service
  15. After=network.target
  16. [Service]
  17. Type=simple
  18. EnvironmentFile=-/etc/sysconfig/memcached
  19. ExecStart=/usr/local/memcached/bin/memcached -u $USER -p $PORT -m $CACHESIZE -c $MAXCONN $OPTIONS
  20. [Install]
  21. WantedBy=multi-user.target

2.1.5 启动服务,并放行端口

  1. [root@memcached01 ~]# systemctl daemon-reload
  2. [root@memcached01 ~]# systemctl enable memcached.service
  3. [root@memcached01 ~]# systemctl start memcached.service
  4. [root@memcached01 ~]# systemctl status memcached.service
  5. memcached.service - Memcached
  6. Loaded: loaded (/usr/lib/systemd/system/memcached.service; disabled; vendor preset: disabled)
  7. Active: active (running) since 2020-02-03 11:34:12 CST; 6s ago
  8. Main PID: 3382 (memcached)
  9. CGroup: /system.slice/memcached.service
  10. └─3382 /usr/local/memcached/bin/memcached -u memcached -p 11211 -m 512 -c 1024
  11. 2 03 11:34:12 memcached02 systemd[1]: Started Memcached.
  12. #查看端口并放行
  13. [root@memcached01 ~]# netstat -antp | grep memcached
  14. tcp 0 0 0.0.0.0:11211 0.0.0.0:* LISTEN 3382/memcached
  15. tcp6 0 0 :::11211 :::* LISTEN 3382/memcached
  16. [root@memcached01 ~]# firewall-cmd --add-port=11211/tcp --permanent
  17. success
  18. [root@memcached01 ~]# firewall-cmd --reload
  19. success

2.2 Memcached API 客户端

为了使得程序可以直接调用 Memcached 库和接口,可以使用 Memcached 扩展组件将 Memcache 添加为 PHP 的一个模块。此扩展使用了 Libmemcached 库提供的 API 与 Memcached 服务端进行交互。

2.2.1 安装LAMP环境

安装Apache网站服务
  1. [root@web-server ~]# yum install httpd -y
  2. [root@web-server ~]# systemctl enable httpd.service
  3. [root@web-server ~]# systemctl start httpd.service
  4. [root@web-server ~]# firewall-cmd --add-service=http --permanent
  5. [root@web-server ~]# firewall-cmd --add-service=https --permanent
  6. [root@web-server ~]# firewall-cmd --reload

安装mysql数据库
  1. #安装mysql数据库
  2. [root@web-server ~]# wget https://dev.mysql.com/get/mysql80-community-release-el7-3.noarch.rpm
  3. [root@web-server ~]# rpm -ivh mysql80-community-release-el7-3.noarch.rpm
  4. [root@web-server ~]# yum install mysql-community-server -y
  5. #启动数据库并开机启动
  6. [root@web-server ~]# systemctl enable mysqld.service
  7. [root@web-server ~]# systemctl start mysqld.service
  8. #设置root用户新密码
  9. [root@web-server ~]# grep 'temporary password' /var/log/mysqld.log
  10. 2019-05-25T03:30:25.989353Z 5 [Note] [MY-010454] [Server] A temporary password is generated for root@localhost: gw2yrdVKWy,g
  11. [root@web-server ~]#mysql -u root -pgw2yrdVKWy,g
  12. #进入数据库后更改密码
  13. mysql> ALTER USER 'root'@'localhost' IDENTIFIED BY 'MyNewPass4!';
  14. #注意:MySQL的 validate_password 插件默认安装。这将要求密码包含至少一个大写字母,一个小写字母,一个数字和一个特殊字符,并且密码总长度至少为8个字符。
  15. #防火墙放行MySQL服务
  16. [root@web-server ~][root@web-server ~]# firewall-cmd --add-service=mysql --permanent
  17. [root@web-server ~]# firewall-cmd --reload

安装PHP

安装PHP源

  1. [root@web-server ~]# rpm -Uvh https://dl.fedoraproject.org/pub/epel/epel-release-latest-7.noarch.rpm
  2. [root@web-server ~]# rpm -Uvh https://mirror.webtatic.com/yum/el7/webtatic-release.rpm

安装PHP7.2

  1. [root@web-server ~]# yum install php72w php72w-cli php72w-common php72w-gd php72w-ldap php72w-mbstring php72w-mcrypt php72w-mysql php72w-pdo

安装pecl-memcached API 组件

  1. root@web-server html]# yum install php72w-pecl-memcached -y

建立测试网页文件
  1. [root@web-server html]# pwd
  2. /var/www/html
  3. [root@web-server html]# vim index.php
  4. <?php
  5. phpinfo();
  6. ?>
  7. #重启Apache服务
  8. [root@web-server html]# systemctl restart httpd.service

测试访问

使用浏览器进行访问,看到memcached模块,说明已经添加成功。

2. memcache缓存 - 图7

2.2.2 测试 Memcached-API 功能

通过编写简单的 PHP 测试代码调用 Memcache 程序接口来测试是否与 Memcached 服务器协同工作,代码如下:

  1. [root@web-server html]# vim /var/www/html/test.php
  2. <?php
  3. $memcache = new Memcached();
  4. $memcache->addServer('192.168.154.101',11211);
  5. $memcache->set('key','Memcache test successful!');
  6. $result = $memcache->get('key');
  7. unset($memcache);
  8. echo $result;
  9. ?>

此段代码的作用是在客户端连接 Memcached 服务器,设置名为‘key’的键的值为 ‘Memcache test successful!’,并读取显示,成功表示服务器与客户端协同工作正常,使 用浏览器进行访问,测试结果如图所示。

2. memcache缓存 - 图8

三. Memcached 数据库操作与管理

Memcache 协议简单可直接使用 telnet 连接 Memcached 的 11211 端口对 Memcached 数据库进行操作与管理。

  1. [root@web-server html]# telnet 127.0.0.1 11211
  2. Trying 192.168.154.101...
  3. Connected to 192.168.154.101.
  4. Escape character is '^]'.
  5. #输入操作命令即可

操作命令格式:

  1. <command name> <key> <flags> <exptime> <bytes>\r\n<data block>\r\n
  2. #参数说明如下:
  3. command set/add/replace/delete等操作命令
  4. key key用于查找缓存值
  5. flags 可以包括键值对的整型参数,客户机使用它存储关于键值对的额外信息
  6. expiration 在缓存中保存键值对的时间长度(以秒为单位,0 表示永远)
  7. bytes 在缓存中存储的字节数
  8. data block 存储的值(始终位于第二行)

下面是 Memcached 数据库的常见操作指令:

3.1 set

set 命令用于向缓存添加新的键值对。如果键已经存在,则之前的值将被替换。

  1. set userId 0 0 5
  2. 12345
  3. STORED #服务器返回信息
  4. #如果使用 set 命令正确设定了键值对,服务器将使用单词 STORED 进行响应。本示例向缓存中添加了一个键值对,
  5. #其键为userId,其值为12345。并将过期时间设置为 0,这将向 memcached 通知您希望将此值存储在缓存中直到删除它为止。

3.2 add

仅当缓存中不存在键时,add命令才会向缓存中添加一个键值对。如果缓存中已经存在键,则之前的值将仍然保持相同,并且您将获得响应 NOT_STORED。

  1. add userId 0 0 5
  2. 55555
  3. NOT_STORED #已经存在,返回消息
  4. add companyId 0 0 3
  5. 564
  6. STORED #键不存在,服务器返回成功

3.3 replace

仅当键已经存在时,replace命令才会替换缓存中的键。如果缓存中不存在键,那么您将从 memcached 服务器接受到一条 NOT_STORED 响应。

  1. replace accountId 0 0 5
  2. 67890
  3. NOT_STORED #键不存在,所以不能更新
  4. set accountId 0 0 5
  5. 67890
  6. STORED
  7. replace accountId 0 0 5
  8. 55555
  9. STORED #键存在了,可以更新了

3.4 get

get命令用于检索与之前添加的键值对相关的值,您将使用get执行大多数检索操作。

  1. get userId
  2. VALUE userId 0 5
  3. 12345
  4. END
  5. get companyId
  6. VALUE companyId 0 3
  7. 564
  8. END
  9. get username
  10. END
  11. #get命令相当简单,只需使用一个键来调用get,如果这个键存在于缓存中,则返回相应的值。如果不存在,则不返回任何内容。

3.5 delete

delete 命令用于删除memcached中的任何现有值。您将使用一个键调用delete,如果该键存在于缓存中,则删除该值。如果不存在,则返回一条NOT_FOUND 消息。

  1. delete userId
  2. DELETED #成功删除
  3. delete username
  4. NOT_FOUND #无法删除不存在的键值
  5. get userId
  6. END

3.6 gets

gets命令的功能类似于基本的get命令。两个命令之间的差异在于,gets返回的信息稍微多一些:64位的整型值,非常像名称/值对的“版本”标识符。

  1. set userId 0 0 5
  2. 12345
  3. STORED
  4. get userId
  5. VALUE userId 0 5
  6. 12345
  7. END
  8. gets userId
  9. VALUE userId 0 5 5
  10. 12345
  11. END

考虑get和gets命令之间的差异。gets命令将返回一个额外的值:在本例中是整型值5,用于标识名称/值对。如果对此名称/值对执行另一个set命令,则gets返回的额外值将会发生更改,以表明名称/值对已经被更新。

  1. set userId 0 0 5
  2. 33333
  3. STORED
  4. gets userId
  5. VALUE userId 0 5 6
  6. 33333
  7. END
  8. #可以看到gets返回的值已经更新为6,,每次修改名称/值对时,该值都会发生更改。

3.7 cas

cas(check 和 set)是一个非常便捷的memcached命令,用于设置名称/值对的值(如果该名称/值对在您上次执行gets后没有更新过)。它使用与set命令相类似的语法,但包括一个额外的值:gets返回的额外值。

  1. gets userId
  2. VALUE userId 0 5 6 #额外值为6
  3. 33333
  4. END
  5. cas userId 0 0 5 6 #使用额外值6
  6. 88888
  7. STORED
  8. # 执行成功
  9. gets userId
  10. VALUE userId 0 5 8 #额外值为8
  11. 66666
  12. END
  13. set userId 0 0 5 #中间修改过键值
  14. 55555
  15. STORED
  16. gets userId
  17. VALUE userId 0 5 9 #新的额外值为9
  18. 55555
  19. END
  20. cas userId 0 0 5 8 #使用以前的额外值8
  21. 77777
  22. EXISTS #出现失败返回值
  23. #注意,cas并未使用gets最近返回的整型值,所以返回EXISTS值以示失败。
  24. #从本质上说,同时使用gets和cas命令可以防止您使用自上次读取后经过更新的键值对。

3.8 append和prepend

append将数据追加到当前缓存数据后面,当缓存数据存在时才存储;prepend 将数据追加到当前缓存数据的之前,当缓存数据存在时才存储。

  1. set username 0 0 7
  2. Michael
  3. STORED
  4. get username
  5. VALUE username 0 7
  6. Michael
  7. END
  8. append username 0 0 7
  9. _Jordan
  10. STORED
  11. get username
  12. VALUE username 0 14
  13. Michael_Jordan
  14. END
  15. #在键名username原键值前追加数据使用prepend命令
  16. prepend username 0 0 3
  17. 001
  18. STORED
  19. gets username
  20. VALUE username 0 17 13
  21. 001Michael_Jordan
  22. END

3.9 服务器状态信息查看

stats

stats命令显示了关于当前memcached实例的信息:

  1. STAT pid 1139 #进程ID
  2. STAT uptime 4820 #服务器运行秒数
  3. STAT time 1580739313 #服务器当前Unix时间戳
  4. STAT version 1.5.22 #服务器版本
  5. STAT libevent 2.0.21-stable #libevent版本
  6. STAT pointer_size 64 #操作系统字大小(这台服务器是64位的)
  7. STAT rusage_user 0.373755 #该进程累计的用户时间,单位:秒
  8. STAT rusage_system 0.537273 #该进程累计的系统时间,单位:秒
  9. STAT max_connections 1024 #最大连接数
  10. STAT curr_connections 2 #当前打开连接数
  11. STAT total_connections #运行以来接受的连接总数
  12. STAT rejected_connections 2 #拒绝的连接数
  13. STAT connection_structures 3 #Memcached分配的连接结构数量
  14. STAT reserved_fds 20
  15. STAT cmd_get 18 #执行get命令总数
  16. STAT cmd_set 17
  17. STAT cmd_flush 0
  18. STAT cmd_touch 0
  19. STAT cmd_meta 0
  20. STAT get_hits 15 #get命中次数
  21. STAT get_misses 3 #get未命中次数
  22. STAT get_expired 0
  23. STAT get_flushed 0
  24. STAT delete_misses 1
  25. STAT delete_hits 1
  26. STAT incr_misses 0
  27. STAT incr_hits 0
  28. STAT decr_misses 0
  29. STAT decr_hits 0
  30. STAT cas_misses 0
  31. STAT cas_hits 1
  32. STAT cas_badval 1
  33. STAT touch_hits 0
  34. STAT touch_misses 0
  35. STAT auth_cmds 0
  36. STAT auth_errors 0
  37. STAT bytes_read 762 #读取字节总数
  38. STAT bytes_written 712 #写入字节总数
  39. STAT limit_maxbytes 536870912 #分配的内存数(字节)
  40. STAT accepting_conns 1
  41. STAT listen_disabled_num 0
  42. STAT time_in_listen_disabled_us 0
  43. STAT threads 4 #线程数
  44. STAT conn_yields 0
  45. STAT hash_power_level 16
  46. STAT hash_bytes 524288
  47. STAT hash_is_expanding 0
  48. STAT slab_reassign_rescues 0
  49. STAT slab_reassign_chunk_rescues 0
  50. STAT slab_reassign_evictions_nomem 0
  51. STAT slab_reassign_inline_reclaim 0
  52. STAT slab_reassign_busy_items 0
  53. STAT slab_reassign_busy_deletes 0
  54. STAT slab_reassign_running 0
  55. STAT slabs_moved 0
  56. STAT lru_crawler_running 0
  57. STAT lru_crawler_starts 3315
  58. STAT lru_maintainer_juggles 8423
  59. STAT malloc_fails 0
  60. STAT log_worker_dropped 0
  61. STAT log_worker_written 0
  62. STAT log_watcher_skipped 0
  63. STAT log_watcher_sent 0
  64. STAT bytes 298 #存储item字节数
  65. STAT curr_items 4 #当前的item个数
  66. STAT total_items 13 #总的item个数
  67. STAT slab_global_page_pool 0
  68. STAT expired_unfetched 0
  69. STAT evicted_unfetched 0
  70. STAT evicted_active 0
  71. STAT evictions 0
  72. STAT reclaimed 0
  73. STAT crawler_reclaimed 0
  74. STAT crawler_items_checked 18
  75. STAT lrutail_reflocked 0
  76. STAT moves_to_cold 13
  77. STAT moves_to_warm 3
  78. STAT moves_within_lru 2
  79. STAT direct_reclaims 0
  80. STAT lru_bumps_dropped 0
  81. END

stats items

显示各个slab中item的数目和存储时长(最后一次访问距离现在的秒数)。

  1. STAT items:1:number 4
  2. STAT items:1:number_hot 0
  3. STAT items:1:number_warm 0
  4. STAT items:1:number_cold 4
  5. STAT items:1:age_hot 0
  6. STAT items:1:age_warm 0
  7. STAT items:1:age 4498
  8. STAT items:1:mem_requested 298
  9. STAT items:1:evicted 0
  10. STAT items:1:evicted_nonzero 0
  11. STAT items:1:evicted_time 0
  12. STAT items:1:outofmemory 0
  13. STAT items:1:tailrepairs 0
  14. STAT items:1:reclaimed 0
  15. STAT items:1:expired_unfetched 0
  16. STAT items:1:evicted_unfetched 0
  17. STAT items:1:evicted_active 0
  18. STAT items:1:crawler_reclaimed 0
  19. STAT items:1:crawler_items_checked 22
  20. STAT items:1:lrutail_reflocked 0
  21. STAT items:1:moves_to_cold 13
  22. STAT items:1:moves_to_warm 3
  23. STAT items:1:moves_within_lru 2
  24. STAT items:1:direct_reclaims 0
  25. STAT items:1:hits_to_hot 0
  26. STAT items:1:hits_to_warm 2
  27. STAT items:1:hits_to_cold 13
  28. STAT items:1:hits_to_temp 0
  29. END

stats cachedump slabs_id limit_num

  • slabs_id:由stats items返回的结果(STAT items后面的数字)决定的
  • limit_num:返回的记录数,0表示返回所有记录
  • 通过stats items、stats cachedump slab_id limit_num 配合get命令可以遍历memcached的记录。
  1. stats cachedump 1 0
  2. ITEM username [17 b; 0 s]
  3. ITEM userId [5 b; 0 s]
  4. ITEM accountId [5 b; 0 s]
  5. ITEM companyId [3 b; 0 s]
  6. END
  7. stats cachedump 1 2
  8. ITEM username [17 b; 0 s]
  9. ITEM userId [5 b; 0 s]
  10. END

stats slabs

显示各个slab的信息,包括chunk的大小、数目、使用情况等。

  1. STAT 1:chunk_size 96
  2. STAT 1:chunks_per_page 10922
  3. STAT 1:total_pages 1
  4. STAT 1:total_chunks 10922
  5. STAT 1:used_chunks 4
  6. STAT 1:free_chunks 10918
  7. STAT 1:free_chunks_end 0
  8. STAT 1:get_hits 15
  9. STAT 1:cmd_set 17
  10. STAT 1:delete_hits 1
  11. STAT 1:incr_hits 0
  12. STAT 1:decr_hits 0
  13. STAT 1:cas_hits 1
  14. STAT 1:cas_badval 1
  15. STAT 1:touch_hits 0
  16. STAT active_slabs 1
  17. STAT total_malloced 1048576
  18. END

stats sizes

输出所有item的大小和个数。

stats reset

清空统计数据。

flush_all

清除所有缓存数据。

四. Memcached+Magent+Keepalived实现高可用群集

4.1 magent代理的用途

如果memcached其中一个缓存节点的机器down机,那么客户端存入的缓存数据将会丢失一部分,我们可以采用 Magent 缓存代理,防止单点现象,缓存代理也可以做备份,通过客户端连接到缓存代理服务器,缓存代理服务器连接缓存服务器,缓存代理服务器可以连接多台Memcached机器,这样就能实现实现高可用以及防止单点故障。

4.2 keepalived的用途

但是如果magent代理服务器宕机了,就不能继续提供服务,所以就用到了keepalived,通过keepalived配置文件设置优先级来决定谁做主magent或者从magent,当主magent正常运行时vip在主magent,当主magent宕机vip自动换到从magent,主magent恢复后vip自动回到主magent上。

4.3 安装magent

  1. [root@memcached01 ~]# mkdir /opt/magent/
  2. [root@memcached01 ~]# tar zxvf magent-0.5.tar.gz -C /opt/magent/
  3. [root@memcached01 ~]# cd /opt/magent/
  4. #编辑文件
  5. [root@memcached01 magent]# vim ketama.h #在最开头添加如下3行内容
  6. #ifndef SSIZE_MAX
  7. #define SSIZE_MAX 32767
  8. #endif
  9. [root@memcached01 magent-0.5]# vim Makefile
  10. LIBS = -levent -lm -L /usr/local/libevent/lib #修改这一行
  11. INCLUDE=-I /usr/local/libevent/include #添加这一行
  12. [root@memcached01 magent]# make
  13. gcc -Wall -g -O2 -I/usr/local/include -m64 -o magent magent.o ketama.o /usr/lib64/libevent.a /usr/lib64/libm.a
  14. [root@memcached01 magent]# ls
  15. ketama.c ketama.h ketama.o magent magent.c magent.o Makefile
  16. #为了方便使用
  17. [root@memcached01 ~]# cp /opt/magent/magent /usr/bin/

下面是编译时的一些常见错误及解决方法

  1. [root@memcached01 magent]# make
  2. gcc -Wall -g -O2 -I/usr/local/include -m64 -c -o magent.o magent.c
  3. magent.c:71:19: 致命错误:event.h:没有那个文件或目录
  4. #include <event.h>
  5. ^
  6. 编译中断。
  7. make: *** [magent.o] 错误 1
  8. #解决以上错误
  9. [root@memcached01 magent]# ln -s /usr/local/libevent/include/* /usr/local/include/
  10. [root@memcached01 magent]# make
  11. gcc -Wall -g -O2 -I/usr/local/include -m64 -c -o magent.o magent.c
  12. magent.c: 在函数‘writev_list’中:
  13. magent.c:729:17: 错误:‘SSIZE_MAX’未声明(在此函数内第一次使用)
  14. if (toSend > SSIZE_MAX ||
  15. ^
  16. magent.c:729:17: 附注:每个未声明的标识符在其出现的函数内只报告一次
  17. make: *** [magent.o] 错误 1
  18. #解决以上错误
  19. [root@memcached01 magent]# vim ketama.h #在最开头添加如下3行内容
  20. #ifndef SSIZE_MAX
  21. #define SSIZE_MAX 32767
  22. #endif
  23. [root@memcached01 magent]# make
  24. gcc -Wall -g -O2 -I/usr/local/include -m64 -c -o magent.o magent.c
  25. gcc -Wall -g -O2 -I/usr/local/include -m64 -c -o ketama.o ketama.c
  26. gcc -Wall -g -O2 -I/usr/local/include -m64 -o magent magent.o ketama.o /usr/lib64/libevent.a /usr/lib64/libm.a
  27. gcc: 错误:/usr/lib64/libevent.a:没有那个文件或目录
  28. gcc: 错误:/usr/lib64/libm.a:没有那个文件或目录
  29. make: *** [magent] 错误 1
  30. #解决以上错误
  31. [root@memcached01 magent]# ln -s /usr/local/libevent/lib/libevent.a /usr/lib64/libevent.a
  32. [root@memcached01 magent]# cp /usr/lib64/libm.so /usr/lib64/libm.a

4.4 magent的命令使用

  1. [root@memcached01 ~]# magent -h
  2. memcached agent v0.6 Build-Date: Feb 7 2020 17:38:57
  3. Usage:
  4. -h this message
  5. -u uid
  6. -g gid
  7. -p port, default is 11211. (0 to disable tcp support)
  8. -s ip:port, set memcached server ip and port #主memcached的IP和端口
  9. -b ip:port, set backup memcached server ip and port #从memcached的IP和端口
  10. -l ip, local bind ip address, default is 0.0.0.0
  11. -n number, set max connections, default is 4096
  12. -D don't go to background
  13. -k use ketama key allocation algorithm
  14. -f file, unix socket path to listen on. default is off
  15. -i number, set max keep alive connections for one memcached server, default is 20
  16. -v verbose

只在memcached01上启动magent

  1. #启动magent
  2. [root@memcached01 ~]# magent -u root -n 4096 -l 192.168.154.101 -p 12000 -s 192.168.154.101:11211 -b 192.168.154.102:11211
  3. #查看是否启动
  4. [root@memcached01 ~]# netstat -antp | grep magent
  5. tcp 0 0 192.168.154.101:12000 0.0.0.0:* LISTEN 18347/magent
  6. #防火墙放行端口12000
  7. [root@memcached01 ~]# firewall-cmd --add-port=12000/tcp --permanent
  8. [root@memcached01 ~]# firewall-cmd --reload

4.5 在客户端上进行测试

  1. [root@web-server ~]# telnet 192.168.154.101 12000
  2. Trying 192.168.154.101...
  3. Connected to 192.168.154.101.
  4. Escape character is '^]'.
  5. add password 0 0 6
  6. 123456
  7. STORED
  8. get password
  9. VALUE password 0 6
  10. 123456
  11. END
  12. quit
  13. Connection closed by foreign host.

然后在从memcached02上检查

  1. [root@memcached02 ~]# telnet 192.168.154.102 11211
  2. Trying 192.168.154.102...
  3. Connected to 192.168.154.102.
  4. Escape character is '^]'.
  5. get password
  6. VALUE password 0 6
  7. 123456 #已经可以得到刚才添加的值
  8. END
  9. quit
  10. Connection closed by foreign host.

4.6 停掉主节点的memcached服务,在client主机上测试

  1. [root@memcached01 ~]# systemctl stop memcached.service

在客户端上继续操作,可以操作,现在其实操作的是从节点的memcached服务

  1. [root@web-server ~]# telnet 192.168.154.101 12000
  2. Trying 192.168.154.101...
  3. Connected to 192.168.154.101.
  4. Escape character is '^]'.
  5. set username 0 0 10
  6. zhangsan02
  7. STORED
  8. gets username
  9. VALUE username 0 10 8
  10. zhangsan02
  11. END

4.7 在magent节点上安装keepalived软件,实现magent的高可用

  1. [root@memcached01 ~]# yum install keepalived -y

从magent不需要安装magent软件,只需把主节点生成的magent命令文件 复制到从magent的/usr/bin目录下即可

  1. [root@memcached01 ~]# scp /usr/bin/magent 192.168.154.102:/usr/bin/

4.8 在主magent上修改keepalived配置文件

  1. [root@memcached01 ~]# vim /etc/keepalived/keepalived.conf
  2. ! Configuration File for keepalived
  3. global_defs {
  4. notification_email {
  5. acassen@firewall.loc
  6. failover@firewall.loc
  7. sysadmin@firewall.loc
  8. }
  9. notification_email_from Alexandre.Cassen@firewall.loc
  10. smtp_server 192.168.200.1
  11. smtp_connect_timeout 30
  12. router_id MAGENT_HA
  13. ##删除以下4行内容
  14. vrrp_skip_check_adv_addr
  15. vrrp_strict
  16. vrrp_garp_interval 0
  17. vrrp_gna_interval 0
  18. }
  19. ##添加新脚本magent
  20. vrrp_script magent {
  21. script "/opt/shell/magent.sh"
  22. interval 2
  23. }
  24. ##修改vrrp_instance实例
  25. vrrp_instance VI_1 {
  26. state MASTER
  27. interface ens32
  28. virtual_router_id 51
  29. priority 100
  30. advert_int 1
  31. ##添加使用新脚本
  32. track_script {
  33. magent
  34. }
  35. authentication {
  36. auth_type PASS
  37. auth_pass 1111
  38. }
  39. ##定义一个虚拟IP
  40. virtual_ipaddress {
  41. 192.168.154.200
  42. }
  43. }
  44. ##下面多余的部分可以全部删除

4.9 在从magent上编辑keepalived配置文件

  1. #从主magent上把配置脚本复制到从magent上
  2. scp /etc/keepalived/keepalived.conf root@192.168.154.102:/etc/keepalived/keepalived.conf
  3. [root@memcached02 ~]# vim /etc/keepalived/keepalived.conf
  4. router_id MAGENT_HB #修改为MAGENT_HB
  5. state BACKUP #修改为BACKUP
  6. virtual_router_id 51 #ID必须相同
  7. priortity 90 #优先级比主magent低

4.10 在主magent上配置 magent脚本

  1. [root@memcached01 ~]# mkdir /opt/shell -p
  2. [root@memcached01 ~]# vim /opt/shell/magent.sh
  3. #!/bin/bash
  4. K=`ps -ef| grep keepalived | grep -v grep | wc -l`
  5. if [ $K -gt 0 ];then
  6. magent -u root -n 51200 -l 192.168.154.200 -p 12000 -s 192.168.154.101:11211 -b 192.168.154.102:11211
  7. else
  8. pkill -9 magent
  9. fi
  10. [root@memcached01 ~]# chmod a+x /opt/shell/magent.sh

4.11 在从magent上配置 magent脚本

  1. [root@memcached02 ~]# mkdir /opt/shell
  2. [root@memcached02 ~]# vim /opt/shell/magent.sh
  3. #!/bin/bash
  4. K=`ip addr | grep 192.168.154.200 | grep -v grep | wc -l`
  5. if [ $K -gt 0 ];then
  6. magent -u root -n 51200 -l 192.168.154.200 -p 12000 -s 192.168.154.101:11211 -b 192.168.154.102:11211
  7. else
  8. pkill -9 magent
  9. fi
  10. [root@memcached02 ~]# chmod a+x /opt/shell/magent.sh

4.12 在主从magent上均启动keepalived服务

  1. [root@memcached01 ~]# systemctl enable keepalived.service
  2. [root@memcached01 ~]# systemctl restart keepalived.service
  3. ##防火墙放行vrrp协议
  4. [root@memcached01 ~]# firewall-cmd --add-rich-rule='rule protocol value='vrrp' accept' --permanent
  5. success
  6. [root@memcached01 ~]# firewall-cmd --reload

然后发现主magent服务器上以及存在VIP:192.168.154.200,在客户端上访问VIP的12000端口

  1. [root@web-server ~]# telnet 192.168.154.200 12000
  2. Trying 192.168.154.200...
  3. Connected to 192.168.154.200.
  4. Escape character is '^]'.
  5. get username
  6. VALUE username 0 4
  7. mary
  8. END
  9. set username 0 0 8
  10. zhangsan
  11. STORED
  12. gets username
  13. VALUE username 0 8 2
  14. zhangsan
  15. END

停止主magent的keepalived服务,VIP飘逸到从magent,客户端依然可以访问memcached服务

  1. [root@memcached01 ~]# systemctl stop keepalived.service
  2. [root@web-server ~]# telnet 192.168.154.200 12000
  3. Trying 192.168.154.200...
  4. Connected to 192.168.154.200.
  5. Escape character is '^]'.
  6. get username
  7. VALUE username 0 8
  8. zhangsan
  9. END