使用 Nginx 构建一个“高”可用的 PHP 集群

  跳过没必要的介绍,直接进入主题。目前建立一个高可用集群的方案不少,可以使用硬件或软件 LVS 类构建,现在我说的方案是只用 Nginx 来进行构建。

  这个集群的架构如下图:

  上面我们共部署了5个节点,每个节点上配有 Nginx + PHP。这个架构的重点就在于,Nginx 不只是与本机的 PHP 通信,整个集群应该把 Nginx 部分抽象到面向业务的第一层,而 PHP 则在第二层。每层都为多节点均衡架构。

  其中 Nginx 层面使用 DNS 均衡实现,DNS 负载均衡是一个很传统的方案,在单个域名下绑定多个 IP 进行轮循,可有效的把业务请求分发到多个节点上,但某节点故障时则需要有相应的解析处理,把故障的节点从 DNS 记录中删除。目前推荐使用 DNSPOD 的解析服务,可支持 API 操作。这样我们就可以自己建立一个服务器状态管理的程序,自动切换 DNS 解析。(注意:域名解析的切换需要 5~10 分钟,当然这是由域名解析记录的 TTL 值决定,为避免大量的 DNS 解析影响请求打开速度,建议 10 分钟或以上为佳)

  第二层 PHP 则由 Nginx 使用 upstream 实现均衡,Nginx 本身的 upstream 就已支持节点健康维护的功能,可以放心的交给 Nginx 来做。而如果 PHP 业务层带缓存功能,则要考虑使用一致性哈希模块来实现 upstream 的均衡策略,否则节点故障对整个 PHP 集群的缓存造成大幅度的震荡。根据我们测试的数据,在普通哈希策略下,一个节点故障会导致 90% 的缓存失效,而使用一致性哈希则可降低到 50% 。并且我们的 Nginx 一致性哈希模块,还可以把故障节点的请求分发到邻近的节点,可以再提高部分缓存命中率,使得整体提升到 70% 的样子。

  这样一个架构方案给我们实现了一个“高”可用的 PHP 集群,并且没有单点故障的隐患存在。DNS 解析服务是多节点,Nginx 层是多节点,PHP 层更是多节点的模式。如使用 LVS 方案,LVS 服务本身也要做一套热备,才能避免单点问题,且增加了架构复杂性。

  应该选择那套架构方案还由业务决定,这里我只是提供一个新思路罢了 ;)

  Nginx 一致性哈希模块:ngx_consistent_hash-1.0.tar

             ngx_consistent_hash-1.1.tar.gz (Fix nginx reload bug)

  使用方法:

upstream backend {
server 192.168.1.101 weight=1;
server 192.168.1.102 weight=2;
server 192.168.1.103 weight=3;
server 192.168.1.104 weight=4;
server 192.168.1.105 weight=5;
consistent_hash $host$request_uri 2;
}

  consistent_hash 支持2个参数,第一个参数为哈希字符串,第二个参数为备份节点数量。当某节点故障时,将把该节点的请求分发到2个备份节点上。当然你可以设置1或更高,建议2为佳 :)

  模块对 nginx 原 upstream 模块的 weight 节点权重功能进行了替换,weight 的功能是配置节点在集群中的位置顺序。(做一致性哈希,这是必须的)

  • BigPigeon

    我一直很好奇现在常规的集群中的session是如何同步的?我记得有次听eBay的一个engineer说他们刚开始是用组播技术,现在是用的中央数据库。

    • oneoo

      目前分布式缓存技术都很成熟,把session存入缓存集群就可以解决同步的问题 :)

  • http://www.weibo.com/tigerking6 jameswang6

    这个方案还是值得借鉴。
    会话复制技术和会话持久化(到shared file或database)都是很常见的技术。会话复制技术出现得更早,涉及到的技术组件相对少一些。如果采用会话持久化技术,还需要增加对持久层的管理。

  • ianzhang

    有一个问题,如果是nginx已经经过负载均衡,php进程再进行负载均衡还有必要么?

    • oneoo

      没必要~

  • ianzhang

    又来请教问题,nginx内部执行是非阻塞的,但是执行php本身就是阻塞的,比如memcache,mysql的访问,这样应该是达不到nginx的最高性能,有什么办法解决么?

    • oneoo

      PHP 不支持线程或协程,这是语言局限。考虑 Lua / Python / node.js 吧~

  • ianzhang

    就算用了不阻塞语言,也有问题的,就是你还是需要去连接db,memcache,远程调用。感觉后面的逻辑有这些操作的,都不适合放到nginx上面,不然其实不会有很高的性能,使用场景也不对了

  • xsstestxss