前言
之前这篇文章PHP与Memcached实战说的是php怎么连memcached,文中主要写的是php连接memcached的api。
Nginx连接Memcached
架构图
首先请求Nginx,Nginx去连接Memcached,从中读取数据,如果不存在,就使用PHP连接MySQL,在数据库中读取数据,然后将数据在复制到Memcached一份。
Memcached key
memcached是k/v存储,nginx请求memecached时用什么做key?一般用 uri
和 arg
做key, 如 /user.html?id=1
,这里我只使用uri做key
单个memcached服务器配置
server{
listen 80;
server_name test.com;
location / {
set $memcached_key $uri;#设置mc的key
memcached_pass 127.0.0.1:11211;#设置mc的端口
error_page 404 /callback.php;#错误提示功能
}
#nginx连接php的配置
location ~ \.php$ {
root html;
fastcgi_pass 127.0.0.1:9000;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $DOCUMENT_ROOT$fastcgi_script_name;
include fastcgi_params;
}
}
首先设置memcached存储的key为uri,然后设置mc服务器的端口,还设置了一个404返回的界面,当没有请求到页面时,我们就调用这个callback.php
文件,在callback.php文件中我们要连接数据库,然后查找到数据,将数据复制到memcached一份。
callback.php文件:
<?php
//获取请求的地址
$uri = $_SERVER['REQUEST_URI'];
//获取uid
$uid = substr($uri,5,strpos($uri,'.')-5);
//连数据库
$dsn = 'mysql:host=localhost;dbname=test';
$pdo = new PDO($dsn,'root','1234');
$sql = 'select * from user where id = '.$uid;
$st = $pdo->prepare($sql);
$st->execute();
$user = $st->fetchAll(PDO::FETCH_ASSOC);
if(!empty($user)){
echo 'from mysql';
print_r($user);
//连接mc
$mem = new Memcache();
$mem->connect('127.0.0.1',11211);
$mem->add($uri,$user,false,300);
$mem->close();
}else{
echo 'no user';
}
当我们这样请求的时候:
test.com/user1.html
test.com/user22.html
test.com/user100.html
如果memcached中有数据,就不走callback文件,如果没有数据,我们就走callback文件,然后截取到对应的user
的id
,拿着id去查询数据库,然后将查询到的数据复制到memcached一份。
memcached集群
#memcached服务器池
upstream memcached_pool{
consistent_hash $request_uri; #一致性hash算法
server 127.0.0.1:11211;
server 127.0.0.1:11212;
server 127.0.0.1:11213;
}
server{
listen 80;
server_name test.com;
location / {
set $memcached_key $uri;
memcached_pass memcached_pool;
error_page 404 /callback.php;
}
location ~ \.php$ {
root html;
fastcgi_pass 127.0.0.1:9000;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $DOCUMENT_ROOT$fastcgi_script_name;
include fastcgi_params;
}
}
这里我们不仅做了集群,还设置了Nginx连接memcached的算法为一致性哈希算法,至于什么是一致性哈希算法,可以去看我的文章:一致性哈希算法,为什么要设置一致性哈希算法。这样可以保证uri相同的情况下,我们发出的请求连接的memcached都是一台服务器,如果不写一致性哈希算法,那么Nginx将默认使用轮询算法,这样就不科学了。假设我们已经将内容写入了第一台服务器,但是第二次请求竟然落到了第二台服务器上,这样的话,又要写一遍数据。好不科学。
callback.php文件
<?php
//获取请求的地址
$uri = $_SERVER['REQUEST_URI'];
//获取uid
$uid = substr($uri,5,strpos($uri,'.')-5);
//连数据库
$dsn = 'mysql:host=localhost;dbname=test';
$pdo = new PDO($dsn,'root','1234');
$sql = 'select * from user where id = '.$uid;
$st = $pdo->prepare($sql);
$st->execute();
$user = $st->fetchAll(PDO::FETCH_ASSOC);
if(!empty($user)){
echo 'from mysql';
print_r($user);
//连接mc
$mem = new Memcache();
$mem->addServer('127.0.0.1',11211);
$mem->addServer('127.0.0.1',11212);
$mem->addServer('127.0.0.1',11213);
$mem->add($uri,$user,false,300);
$mem->close();
}else{
echo 'no user';
}
php连接memcached使用一致性hash算法
在php.ini中添加这样的一段:
memcache.hash_strategy=consistent
有的同学可能会问,为什么php也要使用一致性hash算法?假设nginx使用了一致性hash算法,用户请求的是:/user1.html
页面,假设落到的是1号服务器,没有读到数据,去请求php,php去连接memcached,结果连得是第2台服务器,然后将数据写入了第2台服务器,我们发出的请求明明落在1号服务器,但是却写到了第2台服务器,这样就造成了数据的不一致。所以,我们要让Nginx和php都采用一致性hash算法。
nginx编译一致性hash扩展
1、下载ngx_http_consistent_hash扩展模块
cd /usr/local/src
wget https://github.com/replay/ngx_http_consistent_hash/archive/master.zip
2、将文件解压后复制到下/usr/local/src下
3、编译
./configure --prefix=/usr/local/nginx --add-module=/usr/local/src/ngx_http_consistent_hash-master/
make && make install
结论
使用集群的时候,只要我们使用将nginx和php连接memcached的连接算法改为一致性hash算法,就可以实现读取和写入memcached的时候都是一台服务器了。