前言

之前这篇文章PHP与Memcached实战说的是php怎么连memcached,文中主要写的是php连接memcached的api。

Nginx连接Memcached

架构图

Nginx连接Memcached_php

首先请求Nginx,Nginx去连接Memcached,从中读取数据,如果不存在,就使用PHP连接MySQL,在数据库中读取数据,然后将数据在复制到Memcached一份。

Memcached key

memcached是k/v存储,nginx请求memecached时用什么做key?一般用 uriarg 做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文件,然后截取到对应的userid,拿着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的时候都是一台服务器了。

网络上志同道合,我们一起学习网络安全,一起进步,QQ群:694839022