此系统主要分为几个低偶合的层次组装而成,具有多IDC分布的特性。从底往上依次为DB层,MC池子层,Nginx+PHP Fast-CGI层,LVS层,而后经过DNS接入用户。每一层都具有良好的扩展性以及灾备能力。php
DNS:html
从大的结构上来讲,此系统分布在4个主要IDC,网通电信各2。 机器数量按照网通:电信 1:2的比例配置,同一运营商下的两IDC机器数量等同。这样在宕掉一个IDC的状况下,能够经过切换DNS,临时访问相近的IDC,达到IDC间灾备。mysql
LVS层:nginx
每一个IDC的接入层经过LVS做四层负载均衡。IDC内部任何接入机器宕掉,能够经过failover机制在一分钟内自动摘除。程序员
Nginx+PHP Fast-CGI层:算法
经过fpm管理PHP Fast-CGI进程,Nginx通unix域协议与fpm通讯。转发 *.php的请求以及响应。使用APC做为OP代码加速器,加速php响应。PHP直接与MC池子或Mysql层进行交互。sql
MC池子层:shell
每一个IDC内部的多个MC机是做为一个总体来管理的,也就是说,假如你有100个数据,4个MC机,那么每一个MC上只会存25个数据,而不会每一个都存100个。好处是,得到了至关于之前4倍的内存池, 增大了缓存命中率。更额外的好处是,减小了,本IDC内部各MC间数据同步的时间开销,使得本IDC任何一服务器更新的数据,同IDC内其余服务器当即可见。也使得IDC之间MC的同步更为节约,每一个IDC一个池子,每一个池子只须要写一份数据。编程
MC池子增减机器的问题:若是是传统的hash算法根据key值,将数据平均的hash到4台机器上,那么按照新的hash规则,增长一台机器后,将会有近80%的缓存失效,形成大量数据迁移。因此咱们改用Consistent Hashing,首先求出memcached服务器(节点)的哈希值,并将其配置到0~2的32次方的圆上。而后用一样的方法求出存储数据的键的哈希值,并映射到圆上。而后从数据映射到的位置开始顺时针查找,将数据保存到找到的第一个服务器上。若是超过2的32次方仍然找不到服务器,就会保存到第一台memcached服务器上。它能最大限度地抑制键的从新分布。并且,有的实现还采用了虚拟节点的思想,使得分布更加均匀。由服务器台数(m)和增长的服务器台数(n)计算增长服务器后的命中率计算公式以下:(1 - n/(n+m) ) * 100
宕机重启后初始化的问题:若是初始化的数据只从一台DB上得到,那么在高峰期间必将压垮DB,因此,咱们须要将这个压力,分散到本IDC的多台DB上。细节会在基础类库中进行讲述。
异地MC池子的数据同步:已经封装在基础类库中,更新MC时指定特定的参数,就会在其余异地MC池子上也进行更新。因此咱们若是在北京添了一个数据,那么在深圳也会马上看到。
DB层:
主从结构集群,主库可切换,从库互为备份。从DB宕掉一台,能够自动跳过,不影响服务,目前经过基础类库实现,之后会采用内部DNS。全部从库直接和主库相连,结构简单,方便管理。不过写的数据量相对比较小的,并且咱们容许DB延迟(由MC来保证明时性,或者异地原本就能够接受短暂的延迟),这里将不是问题。
MC和DB写队列:
在专线中断时不影响写操做(读都是本地的,固然也不受影响),并且在更新数据时可以快速返回(由于不须要远程通讯)。
具体来讲就是将MC的异地写,以及DB的写操做封装成直接追加写本地队列文件。每一个机器上有个守护进程,每0.1秒检查一次,有则rename,而后开始将队列数据逐条往深圳数据中心post,同时每成功一条记录当前offset,以便意外宕掉后接着上次的后面处理。(优化点的能够改成批量确认,冒进点的,可使用内存盘优化写速度)。若是线路不通,或者数据中心写失败,post程序将sleep and retry,线路恢复则继续,不会丢数据。这里有个细节须要注意,就是往数据中心同步的时候,尽可能发往同一台机器,也即绑定,以保证序列的顺序,这个保证能够经过对post守护进程的pid取mod来实现,若是绑定的机器出了故障,则应该选定另一台机器绑定,一旦检测到之前的机器好了,则应该切回来,以保证应有的负载均衡。另外,最外层的接入端也一样须要注意这个问题,咱们对lvs作了会话保持,以确保同一用户在至关长的一段时间内,会访问到同一台机器。对于仅仅使用DNS轮询,又没有使用长链接的服务,若是在短期内作数据更新,确定会出现乱序的问题。除非你各服务器时间都很精准,并且,每一个记录写的时候打上精确到ms的时间戳。
而后,深圳数据中心接收到post的数据后,根据消息类型按必定规则的命名分别存放,这样即便增长一种新的消息,也能够方便的使用队列。好比收到MC的消息后,要写三份,分别发往三个IDC,各自不受影响。DB,能够根据不一样的port实例子来建立队列,省得有一个port宕了,影响其余。具体实现细节能够本身调整,一样得有个程序按期(好比每0.1秒,0.01秒也无所谓,对cpu的消耗很是小)检查队列文件存在就rename,而后往master里写。
以上则能保证,DB的master宕掉,或者专线的中断状况下,用户服务基本不受影响,只是异地的同步会延迟一些。额外收获是未来作DB升级调整的时候,能够用队列分流,分别作不一样的处理。
四,环境配置以及底层基础类库
PHP网络版环境:每IDC之间差别化的Nginx环境变量配置,使得相同的应用程序在不一样的IDC运行时,使用各自IDC内部的MC以及DB等资源。
PHP
PHP
好比/path/php/bin/php test.php
或在php程序第一行加上
#!/path/php/bin/php
而后赋予php脚本可执行权限,使做为shell程序运行。
因为 php 做为 shell 运行时,没法继承 nginx 配置的环境变量。因此它必须依赖一个独立的配置文件。
底层基础类库:
底层基础类库,起到粘合剂的做用,将环境配置,服务器资源等所有结合起来,使得这些资源以及配置信息对上层开发人员透明,无须考虑。总的来讲有如下一些功能。
1,
[DB_stock]
host = m3306_sz_gtimg_cn
类库目前除支持模拟DNS外,还直接兼容真实域名以及IP地址,方便未来进行数据迁移。之因此没有直接使用 DNS是也有历史缘由的。
2,
在没有内部DNS的状况下,将DB读写分离,账户选择,链接的创建(包括 什么时候候真正创建链接,创建长链接,仍是短链接,链接的绑定,以及生命周期),负载均衡以及failvoer等封装成对用户透明的以下简单用法:
//指定以读(r) 或 写(w) 的方式打开一个库。不指定的状况下,默认是”r”方式打开。
$db_r =new MYSQL(“testdb”,“r”);
<?
require_once (dirname(__FILE__).'/../mysql.php');
$db_w =new MYSQL("test","w");
$arr = array(
if( $db_w->insert("test",$arr) )
{
}
else
{
}
echo "read ............<br>";
$db_r =new MYSQL("test","r");
$sql = "select * from test";
if( $result = $db_r->query($sql))
{
}
else
{
}
3,
简化MC池子使用方法,支持异地MC数据同步。
//if rset=1,the mc data will sync to other idcs, default 0.
function set($key, $value, $flag, $expire, $r_set=0)
<?php
require_once (dirname(__FILE__).'/../memcache.php');
$mc = new MC("test");
for($i=0;$i<2;$i++)
{
}
for($i=0;$i<2;$i++)
{
}
注意:我在引用头文件时候,都会使用相似require_once (dirname(__FILE__).'/../mysql.php'); 的方法。我暂且管它叫动态绝对路径。它的好处是,
跟相对路径比:当一个头文件被多层引用时,目录结构又不一致,不会找不到。
也不会去搜索全部可能的目录,执行多余的fstat以及open操做。
跟绝对路径相比:若是我将整个项目,包括头文件所有平移到别的目录,不须要挨个修改文件。
固然,你也能够采用另一个方法,将本项目相关的配置信息绝对路径放在一个统一的文件里,而后经过任何一种方法引用那个文件。