pm961 mysql_搭建Memcache服务详解

博文大纲:

一、Memcache简介

二、Memcache工作流程

三、Memcache调度算法

四、Memcache实现原理

五、安装Memcache

(1)安装nginx服务器

(2)安装PHP服务器

(3)安装MySQL数据库

(4)测试PHP与Nginx、MySQL的连通性

(5)安装Memcache服务器

(6)PHP服务器安装Memcache客户端

(7)使用 memcache 实现 session 共享

(8)测试Memcache缓存数据库

一、Memcache简介

Memcache是一套自由、开源、高性能、分布式的高速缓存系统。由于Memcache通过在内存中缓存数据和对象来减少读取数据库的次数。目前被许多网站使用以提升网站的访问速度,尤其对于一些大型的、需要频繁访问数据库的网站访问速度提升效果十分显著。

Memcache是一个存储键值对的HashMap,在内存中对任意的数据都可以使用key-value的方式存储,数据库可以来自数据库调用或API调用。Memcache设计理念就是小而强大,她简单的设计促进了快速部署、易于开发并解决大规模的数据缓存的许多难题,而其所开放的API使得Memcache能用于Java、C/C++/C#、Perl、Python等大部分流行的程序语言。

二、Memcache工作流程

注意Memcache虽然被称为“分布式缓存”,但是Memcache本身完全不具备分布式的功能,Memcache集群之间不会相互通信,所谓的“分布式”,完全依赖于客户端程序来实现,如图:

66810c2863e25af539fb4700ee3ff2f4.png

Memcahe工作流程:

(1)应用程序输入需要写入缓存的数据;

(2)API将Key输入路由算法模块,理由算法根据Key和Memcache集群服务器列表得到服务器的编号;

(3)由服务器编号得到Memcache及其IP地址和端口号;

(4)API调用通信模块和指定编号的服务器通信,讲数据写入该服务器,完成一次分布式缓存的写操作;

不管是读取缓存还是写入缓存,只要使用相同的路由算法和服务器列表、应用程序查询的是相同的key,Memcache客户端总是访问相同的客户端去读取数据,只要服务器缓存中还有该数据的缓存,就能保证缓存命中。

这种Memcache集群的方式也是从分区容错性的方面考虑的,假设Node2宕机了,那么Node2上存储的数据都不可用了,此时由于集群中Node0和Node2还存在,下一次请求获取Node2的缓存数据,肯定是没有命中的。这时首先会从数据库中获取到缓存的数据,通过路由算法根据将缓存的数据存储在Node0或Node1上,这种集群的做法很好,但是缺点是成本太大。

三、Memcache调度算法

Memcache共有两种路由调度算法,分别是:余数Hash与一致性Hash。

(1)余数Hash

简单的路由算法可以使用余数Hash:用服务器数目和缓存数据Key的hash值相除,余数为服务器列表中的服务器编号。由于HashCode随机性比较强,所以使用余数Hash路由算法就可以保证缓存数据在整个Memcache服务器集群中有比较均衡的分布。

如果不考虑服务器集群的伸缩性,那么余数Hash算法几乎可以满足绝大数的缓存路由器需求你,但是当分布式缓存集群需要扩容时,就会出现很大的问题。

比如Memcache服务器集群由3台变成4台,假设有HashCode为0~19的20个数据,如图:

3台Memcache服务器环境:

62bffea239d9b05b1b81e320fa0c64b1.png

现在扩容到4台Memcache服务器:

404324780b0342c19770e140045d2f84.png

仅仅是扩展到4台,原本缓存的数据就只可以命中6次,那么如果扩容到20台一行,只有前三个缓存中对应的数据可以命中。通过以上举例就可以说明:

使用余数Hash的路由算法,在扩容的时候会造成大量的数据无法正确命中缓存。

在网站业务中,大部分的业务数据操作请求都会通过缓存来获取的,只有少量的数据操作才会访问数据库,因此数据库的负载能力是在有缓存为前提的情况下设计的。当大部分缓存的数据因为Memcache服务器的扩容而不能正确读取时,这些数据访问的压力就落到了数据库的身上,这将大大超过数据库的负载能力,严重的情况还会造成数据库宕机。

对于使用余数Hash算法的Memcache集群环境,需要扩容时,解决方案:

在网站访问量低谷,通常是深夜,技术团队加班、扩容、重启服务器;

通过模拟请求的方式逐渐预热缓存,使缓存服务器中的数据重新分布;

(2)一致性Hash

一致性Hash算法通过一个叫做一致性Hash环的数据结构实现key到缓存服务器的Hash映射,简单来说,一致性Hash将整个Hash空间组织成一个虚拟的圆环,假设某空间哈希函数H的值空间是0~2^32-1,那么整个Hash空间如图:

f7f66e2127aac9799cfae8d3fac93560.png

将各个服务器使用H进行一个Hash计算,具体可以使用服务器的IP地址或主机名作为关键字,这样每台服务器都能确定其在上面的Hash环上的位置了,并且是按照顺时针排序。

假设三台Memcache经计算后位置如下:

e7d7c59da96c76fd4280e5679ddbcb22.png

接下来使用相同的算法计算出数据的哈希值,并由此确定数据在此哈希环上的位置,比如有四个数据,经过哈希计算后位置如下:

c6c64b5a0d42f26df024764778f5d6a4.png

根据一致性Hash算法,按照顺时针找最近服务节点方法,这样得到的Hash环调度方法,有很高的容错性和可扩展性。

假设server03宕机,就会出现以下情况:

58857b3bb2603104caaea1687652b84b.png

可以但看到此时C、B会收到影响,将B、C节点重定向到server01上。

在一致性Hash算法中,如果一台服务器不可用时,则受到影响的仅仅是此服务器按照顺时针方向的第一台服务器,其他服务器并不会受到影响。

假设在原本的环境中,再添加一台服务器server04,就会出现以下情况:

dbbcdec349f2e342bcdbc8765a4e32f3.png

此时,A、D、C都不会受到影响,只有B需要重定向到新的Server04。

在一致性Hash算法中,如果增加一台服务器,则受影响的数据仅仅是新服务器按照顺时针方向的第一台服务器,其他并不会受到影响。

综上所述,一致性Hash算法的优缺点:

优点:对于节点的增减都只需重定位环空间的一部分数据,具有较好的容错行和可扩展性;

缺点:在服务节点太少是,容器因为节点分布均匀而造成数据倾斜问题。

当然对于其缺点,我们可以通过增加虚拟节点的方式来解决。

总之就是:集群中的缓存服务器节点越多,增加、减少节点的影响越小;随着集群规模的增大,继续命中原有缓存数据的概率会越来越大,虽然仍有小部分数据缓存服务器中不能被读取,但是比例相对来说,较小。即使访问数据库,也不会对数据库产生致命的负载压力。

四、Memcache实现原理

Memcache的特点:

访问数据库的速度比传统的关系型数据库要快(因为Memcache是存放在内存中的,而传统的关系型数据库是存放在磁盘中);

Memcache的数据存放在内存中,就意味着只要Memcache重启,数据便会丢失。

由于现在大部分都是64位操作系统,这里就不介绍内存对32位系统的影响了;

Memcache的原理:最重要的就是内存如何分配,Memcache采用的内存分配方式是固定空间分配的,如图:

422f812e2b317f97f97284077250963a.png

图中主要设计到了stab_class、slab、page、chunk四个概念,四者的关系:

(1)Memcache将内存空间分为一组slab;

(2)每个slab下又有若干个page,每个page默认是1M,如果一个slab占用100M内存的话,那么这个slab下应该有100个page;

(3)每个page里面包含一组chunk,chunk是真正存放数据的地方,同一个slab里面的chunk的大小是固定的;

(4)有相同大小chunk的slab被组织在一起,称为slab_class;

Memcache内存分配的方式称为allocator(分配运算),slab的数量是有限的,几个、十几个或者几十个,这个和启动参数的配置相关;

Memcache中的value存放的地方是由value的大小决定的,value总是会存放到与chunk大小最接近的一个slab中。

如果这个slab中没有chunk可以分配了怎么办?如果Memcache启动没有追加“-M”,那么Memcache会把这个slab中最近最少使用的chunk中的数据清理掉,然后放上最新的数据。

1.Memcache的工作流程

如图:

37e88c3fe261e1267c22d4b9b417a352.png

1、检查客户端的请求数据是否在 memcached 中,如果有,直接把请求数据返回,不再对数据库进行任何操作,路径操作为①②③⑦;

2、如果请求的数据不在 memcached 中,就去查数据库,把从数据库中获取的数据返回给客户端,同时把数据缓存一份到 memcached 中(memcached 客户端不负责,需要程序明确实现),路径操作为①②④⑤⑦⑥;

3、每次更新数据库的同时更新 memcached 中的数据,保证一致性;

4、当分配给 memcached 内存空间用完之后,会使用 LRU(Least Recently Used,最近最少使用)策略加上到期失效策略,失效数据首先被替换,然后再替换掉最近未使用的数据;

2.Memcached的特征

协议简单:

基于文本行的协议,直接通过 telnet 在 memcached 服务器上可进行存取数据操作;

基于 libevent 事件处理;

所有数据都保存在内存中,存取数据比硬盘快,当内存满后,通过 LRU 算法自动删除不使用的缓存,但没有考虑数据的容灾问题,重启服务,所有数据会丢失;

分布式:

各个 memcached 服务器之间互不通信,各自独立存取数据,不共享任何信息。服务器并不具有分布式功能,分布式部署取决于 memcache 客户端。

五、安装Memcache

Memcache 的安装分为两个过程:memcache 服务器端的安装和 memcached 客户端的安装。所谓服务器端的安装就是在服务器(一般都是 linux 系统)上安装 Memcache 实现数据的存储。

所谓客户端的安装就是指 php(或者其他程序,Memcache 还有其他不错的 api 接口提供)去使用服务器端的 Memcache 提供的函数,需要 php 添加扩展。

由此可以看出Memcache的搭建需要借助于LAMP或LNMP,本篇博文采用LNMP结构。

安装环境:

3033c86398c3b0f2df12ce8df3053244.png

(1)安装nginx服务器

关于安装Nginx的详细介绍可以参考Nginx深度优化(二),那么这里就不多做解释了!

[root@Nginx ~]# useradd -M -s /sbin/nologin nginx

[root@Nginx ~]# yum -y install openssl-devel

[root@Nginx ~]# tar zxf pcre-8.39.tar.gz -C /usr/src

[root@Nginx ~]# tar zxf zlib-1.2.8.tar.gz -C /usr/src

[root@Nginx ~]# tar zxf nginx-1.14.0.tar.gz -C /usr/src

[root@Nginx ~]# cd /usr/src/nginx-1.14.0/

[root@Nginx nginx-1.14.0]# ./configure --prefix=/usr/local/nginx \

--user=nginx --group=nginx --with-http_dav_module \

--with-http_stub_status_module --with-http_addition_module \

--with-http_sub_module --with-http_flv_module --with-http_mp4_module \

--with-pcre=/usr/src/pcre-8.39 --with-zlib=/usr/src/zlib-1.2.8 \

--with-http_ssl_module --with-http_gzip_static_module && make && make install

[root@Nginx ~]# ln -s /usr/local/nginx/sbin/nginx /usr/local/sbin

[root@Nginx ~]# nginx

[root@Nginx ~]# netstat -anpt | grep 80

tcp 0 0 0.0.0.0:80 0.0.0.0:* LISTEN 8460/nginx: master

(2)安装PHP服务器

关于PHP的安装详细介绍可以参考部署LAMP动静分离以及部署Discuz论坛这里就不多做解释了!

[root@PHP ~]# yum -y install openssl-devel libxml2-devel bzip2-devel libcurl-devel

[root@PHP ~]# tar zxf libmcrypt-2.5.7.tar.gz -C /usr/src

[root@PHP ~]# cd /usr/src/libmcrypt-2.5.7/

[root@PHP libmcrypt-2.5.7]# ./configure --prefix=/usr/local/libmcrypt && make && make install

[root@PHP ~]# tar zxf php-5.6.27.tar.gz -C /usr/src

[root@PHP ~]# cd /usr/src/php-5.6.27/

[root@PHP php-5.6.27]# ./configure --prefix=/usr/local/php \

--with-mysql=mysqlnd --with-pdo-mysql=mysqlnd --with-mysqli=mysqlnd \

--with-openssl --enable-fpm --enable-sockets --enable-sysvshm \

--enable-mbstring --with-freetype-dir --with-jpeg-dir --with-png-dir \

--with-libxml-dir=/usr --enable-xml --with-mhash --with-zlib \

--with-mcrypt=/usr/local/libmcrypt --with-config-file-path=/etc \

--with-config-file-scan-dir=/etc/php.d --with-bz2 \

--enable-maintainer-zts && make && make install

[root@PHP ~]# cp /usr/src/php-5.6.27/php.ini-production /etc/php.ini

[root@PHP ~]# cp /usr/src/php-5.6.27/sapi/fpm/init.d.php-fpm /etc/init.d/php-fpm

[root@PHP ~]# chmod +x /etc/init.d/php-fpm

[root@PHP ~]# chkconfig --add php-fpm

[root@PHP ~]# cd /usr/local/php/etc/

[root@PHP etc]# cp php-fpm.conf.default php-fpm.conf

[root@PHP etc]# sed -i 's#;pid = run/php-fpm.pid#pid = run/php-fpm.pid#g' php-fpm.conf

[root@PHP etc]# sed -i 's/listen = 127.0.0.1:9000/listen = 0.0.0.0:9000/g' php-fpm.conf

[root@PHP etc]# sed -i 's/pm.max_children = 5/pm.max_children = 50/g' php-fpm.conf

[root@PHP etc]# sed -i 's/pm.start_servers = 2/pm.start_servers = 5/g' php-fpm.conf

[root@PHP etc]# sed -i 's/pm.min_spare_servers = 1/pm.min_spare_servers = 5/g' php-fpm.conf

[root@PHP etc]# sed -i 's/pm.max_spare_servers = 3/pm.max_spare_servers = 35/g' php-fpm.conf

[root@PHP ~]# systemctl start php-fpm

[root@PHP ~]# netstat -anpt | grep 9000

tcp 0 0 0.0.0.0:9000 0.0.0.0:* LISTEN 118146/php-fpm: mas

(3)安装MySQL数据库

[root@Mysql ~]# ls

anaconda-ks.cfg initial-setup-ks.cfg mysql-5.7.22-linux-glibc2.12-x86_64.tar.gz mysql.sh

[root@Mysql ~]# sh mysql.sh

Starting MySQL.. SUCCESS!

[root@Mysql ~]# mysql -u root -p123

mysql> create database testdb1;

mysql> use testdb1;

mysql> grant all on *.* to lzj@'192.168.1.%' identified by '123456';

mysql> create table test1(id int not null auto_increment,name varchar(20) default null,primary key (id)) engine=innodb auto_increment=1 default charset=utf8;

mysql> insert into test1(name) values ('tom1'),('tom2'),('tom3'),('tom4'),('tom5');

mysql> select * from test1;

+----+------+

| id | name |

+----+------+

| 1 | tom1 |

| 2 | tom2 |

| 3 | tom3 |

| 4 | tom4 |

| 5 | tom5 |

+----+------+

//创建数据库,及添加数据并创建授权用户

(4)测试PHP与Nginx、MySQL的连通性

Nginx服务器的操作:

[root@Nginx ~]# vim /usr/local/nginx/conf/nginx.conf

location / {

root html;

index index.php index.html index.htm;

}

location ~ \.php$ {

root /var/www/html;

fastcgi_pass 192.168.1.6:9000;

fastcgi_index index.php;

fastcgi_param SCRIPT_FILENAME /scripts$fastcgi_script_name;

include fastcgi.conf;

}

[root@Nginx ~]# nginx -t

nginx: the configuration file /usr/local/nginx/conf/nginx.conf syntax is ok

nginx: configuration file /usr/local/nginx/conf/nginx.conf test is successful

[root@Nginx ~]# nginx -s reload

PHP服务器的操作:

[root@PHP ~]# mkdir -p /var/www/html

[root@PHP ~]# vim /var/www/html/test.php

phpinfo();

?>

[root@PHP ~]# vim /var/www/html/test1.php

$link=mysqli_connect('192.168.1.8','lzj','123456');

if($link) echo "恭喜你,数据库连接成功!!!"; else echo "connect shibai";

mysqli_close($link);

?>

客户端访问测试:

b856c5649cf5b27faf267a7af5960765.png

ce46c0d7455dde708fc1f05705e1736f.png

客户端访问测试,确保没有问题!

(5)安装Memcache服务器

[root@Memcache ~]# tar zxf libevent-2.0.22-stable.tar.gz -C /usr/src

[root@Memcache ~]# cd /usr/src/libevent-2.0.22-stable/

[root@Memcache libevent-2.0.22-stable]# ./configure && make && make install

//安装Memcache依赖软件包

[root@Memcache ~]# tar zxf memcached-1.4.33.tar.gz -C /usr/src

[root@Memcache ~]# cd /usr/src/memcached-1.4.33/

[root@Memcache memcached-1.4.33]# ./configure --prefix=/usr/local/memcached \

--with-libevent=/usr/local/ && make && make install

[root@Memcache ~]# ln -s /usr/local/memcached/bin/memcached /usr/local/bin

[root@Memcache ~]# memcached -d -m 2048 -l 192.168.1.7 -p 11211 -c 10240 -P /usr/local/memcached/memcached.pid -u root

[root@Memcache ~]# netstat -anpt | grep 11211

tcp 0 0 192.168.1.7:11211 0.0.0.0:* LISTEN 10886/memcached

启动Memcache常用参数说明如下:

-d:启动一个守护进程;

-m:分配给 Memcache 使用的内存数量,单位是 MB,默认 64MB;

-l:监听的 IP 地址;(默认:INADDR_ANY,所有地址)

-p:设置 Memcache 的 TCP 监听的端口,最好是 1024 以上的端口;

-u:运行 Memcache 的用户,如果当前为 root 的话,需要使用此参数指定用户;

-c:最大运行的并发连接数,默认是 1024;

-P:设置保存 Memcache 的 pid 文件路径;

-M:内存耗尽时返回错误,而不是删除项;

-f:块大小增长因子,默认是 1.25;

-n:最小分配空间,key+value+flags 默认是 48;

-h:显示帮助;

(6)PHP服务器安装Memcache客户端

[root@PHP ~]# scp 192.168.1.7:/root/memcache-3.0.8.tgz .

[root@PHP ~]# tar zxf memcache-3.0.8.tgz -C /usr/src

[root@PHP ~]# cd /usr/src/memcache-3.0.8/

[root@PHP memcache-3.0.8]# /usr/local/php/bin/phpize

//生成configure文件

//若在执行上述命令时报错,则需要执行“yun -y install autoconf "安装提示的autoconf包

[root@PHP memcache-3.0.8]# ./configure --enable-memcache \

--with-php-config=/usr/local/php/bin/php-config && make && make install

//执行后会显示memcache.so存放的路径

[root@PHP ~]# echo "extension = /usr/local/php/lib/php/extensions/no-debug-zts-20131226/memcache.so" >> /etc/php.ini

//在PHP主配置文件中填写memcache.so模块存放的路径

[root@PHP ~]# systemctl restart php-fpm

[root@PHP ~]# vim /var/www/html/test2.php

$memcache = new Memcache;

$memcache->connect('192.168.1.7', 11211) or die ("Could not connect");

$version = $memcache->getVersion();

echo "Server's version: ".$version."
";

$tmp_object = new stdClass;

$tmp_object->str_attr = 'test';

$tmp_object->int_attr = 123;

$memcache->set('key', $tmp_object, false, 600) or die ("Failed to save data at the server");

echo "Store data in the cache (data will expire in 600 seconds)
";

$get_result = $memcache->get('key');

echo "Data from the cache:
";

var_dump($get_result);

?>

//此测试脚本是显示memcached的版本

//并且向里面插入了一个缓存时间为600秒的键值对“test=123”,其ID为“key”

客户段访问如下:

24aaf64f90b47b58b18afe42e55ebf64.png

在PHP服务器上安装telnet工具测试

[root@PHP ~]# yum -y install telnet

[root@PHP ~]# telnet 192.168.1.7 11211 //登陆到memcached的11211端口

Trying 192.168.1.7...

Connected to 192.168.1.7.

Escape character is '^]'.

get key //查询ID为“key”的键值对,可以看到我们测试脚本写入的“test=123”

VALUE key 1 66

O:8:"stdClass":2:{s:8:"str_attr";s:4:"test";s:8:"int_attr";i:123;}

END

//在进行上面的get验证时,需要将test2.php文件中插入的键值对的保存时间值改大一些

//或者重新访问一下,以免缓存失效,查询不到

quit //退出当前环境

Connection closed by foreign host.

[root@PHP ~]#

(7)使用 memcache 实现 session 共享

在PHP服务器上进行以下操作:

[root@PHP ~]# vim /etc/php.ini

//编写PHP主配置文件,并在末尾添加以下内容

session.save_handler = memcache

session.save_path = "tcp://192.168.1.7:11211?persistent=1&weight=1&timeout=1&retry_interval=15"

[root@PHP ~]# systemctl restart php-fpm

[root@PHP ~]# vim /var/www/html/test3.php

session_start();

if (!isset($_SESSION['session_time']))

{

$_SESSION['session_time'] = time();

}

echo "session_time:".$_SESSION['session_time']."
";

echo "now_time:".time()."
";

echo "session_id:".session_id()."
";

?>

PHP配置文件中写入的内容解释如下:

session.save_handler:设置 session 的储存方式为 memcache ;

默认以文件方式存取 session数据;

session.save_path: 设置 session 储存的位置;

使用多个 memcached server 时用逗号”,”隔开,可以带额外的参数”persistent”、”weight”、”timeout”、”retry_interval”等等;

类似这样的:"tcp://host:port?persistent=1&weight=2,tcp://host2:port2";

客户端访问如下:

0ee75af43cbbba30d7cbd1cc92ad16d4.png

5166d4acfafa858e5e98829e6768317a.png

67d4f9036da732861c4d702e0996682b.png

[root@PHP ~]# telnet 192.168.1.7 11211

Trying 192.168.1.7...

Connected to 192.168.1.7.

Escape character is '^]'.

get a8aujbnie16p29rj4pf9ltfjp3 //使用网页出现的server_id号,来获取其对应的值

VALUE a8aujbnie16p29rj4pf9ltfjp3 0 26

session_time|i:1576329213;

END

quit

Connection closed by foreign host.

[root@PHP ~]#

(8)测试Memcache缓存数据库

由于步骤3创建数据库时,就已经在数据库中写入了测试所用的值,接下来就是创建测试脚本就可以了!

PHP服务器的操作:

[root@PHP ~]# vim /var/www/html/test4.php

$memcachehost = '192.168.1.7'; //指定Memcache服务器地址

$memcacheport = 11211; //指定其开放的端口号

$memcachelife = 60;

$memcache = new Memcache;

$memcache->connect($memcachehost,$memcacheport) or die ("Could not connect");

$query="select * from test1 limit 10";

$key=md5($query);

if(!$memcache->get($key))

{

$conn=mysql_connect("192.168.1.8","lzj","123456"); //指定数据库服务器的IP地址、用户及密码

mysql_select_db(testdb1);

$result=mysql_query($query);

while ($row=mysql_fetch_assoc($result))

{

$arr[]=$row;

}

$f = 'mysql';

$memcache->add($key,serialize($arr),0,30);

$data = $arr ;

}

else{

$f = 'memcache';

$data_mem=$memcache->get($key);

$data = unserialize($data_mem);

}

echo $f;

echo "
";

echo "$key";

echo "
";

//print_r($data);

foreach($data as $a)

{

echo "number is $a[id]";

echo "
";

echo "name is $a[name]"; //突出显示信息的字体颜色

echo "
";

}

?>

//经常需要修改的地方已经标注了!而且这个测试脚本在Memcache软件中也有

客户端进行测试:

第一次进行访问

c6879bf12e7f955c17fb803a3e08a21b.png

第二次进行访问(刷新之后)

ea2e55f4f3c84529cf917cc1fd871127.png

在缓存过期之前,也可使用get来获取缓存所对应的值:

[root@PHP ~]# telnet 192.168.1.7 11211

Trying 192.168.1.7...

Connected to 192.168.1.7.

Escape character is '^]'.

get d8c961e9895ba4b463841924dbcefc2b

VALUE d8c961e9895ba4b463841924dbcefc2b 0 251

a:5:{i:0;a:2:{s:2:"id";s:1:"1";s:4:"name";s:4:"tom1";}i:1;a:2:{s:2:"id";s:1:"2";s:4:"name";s:4:"tom2";}i:2;a:2:{s:2:"id";s:1:"3";s:4:"name";s:4:"tom3";}i:3;a:2:{s:2:"id";s:1:"4";s:4:"name";s:4:"tom4";}i:4;a:2:{s:2:"id";s:1:"5";s:4:"name";s:4:"tom5";}}

END

—————————本文到此结束,感谢观看——————————

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值