分布式开发思路

一、伸缩性扩展性问题
        根据不同系统层次,实现横向扩展设计,可简单地通过增加主机数或处理进程数来扩展;
        在前端浏览器层面,可以通过动态调用不同资源实现一定程度分流,如JavaScript、Flash(ActionScript)中加载应用服务器列表,并根据均衡策略选择其中一台服务器进行请求;
        在域名解析层面,可以考虑动态DNS解析服务,为跨机房跨区域服务系统部署提供IP地址的映射达到分流目的;
        在可使用负载均衡器(本着开源精神只谈软件)提供扩展支持,常见的是网络层的lvs、HAProxy(也可以工作在应用层)等开源产品;
        而在应用层上也可以使用nginx负载均衡扩展机制和upstream策略访问多个php应用服务;
        而在php应用服务层面,由php-fpm来管理进程,我们可以随时根据需要,通过php-fpm配置来增减php进程数实现伸缩性。

        另外,基于任务层面的调度分发机制下还可以使用消息队列、分布式调度中间件(如:gearman)等实现的消息缓冲,由work端运行的php程序主动pull消息做处理,
        通过增减work数量来实现任务的处理能力。


二、高性能问题
        缓存思路:WEB系统中高性能的一个重要原则是将资源推送到离用户最近的地方,这就是缓存原则,
        主要的缓存方式有如下几个层次:浏览器端的资源和数据缓存、CDN缓存、服务端反向代理、应用层内容及数据缓存、存储服务内部缓存、系统及IO缓存、硬件缓存,
        充分利用好每一层缓存机制,可以大幅降低服务器并发读请求流量,极高地提升了请求性能以及响应能力。

        批处理:PHP中可以运用共享内存,例如大量插入数据库的操作,我们可以先将SQL语句存于共享内存中(可持久),
        等若干时间或一定数据量后整合一次性写入数据库,以此来提升处理性能。

        高并发:高并发几乎是所有大型系统的问题起因,特别是并发写请求,很多写请求都会从用户端起走完系统的每个环节直到磁盘。而写请求的过程通常还会伴随每个系统环节的一致性、事务、锁等问题,这意味着有些时候的写请求无法并行完成,并在同一时间只能处理一个相同类型写请求,现实中例子如抢购火车票时的余量、线上秒杀的商品库存,这种情况下一定都会在读写两方面的请求上有着很高的数据一致性要求,在实现业务上,由于PHP没有语言级的共享内存支持,所以我们可以使用类似Redis(集群)这样的高性能NoSQL做数据境像,将数据更新的过程大大简化,以直接读写Redis方式,并异步更新数据到数据库,以支持实时的高并发读写。
        另外写请求过后通常还会伴随着每个缓存环节的失效更新等额外负担问题,而且写操作对于整个系统的可用性要求也是非常高的,所以写请求的成本很昂贵。

        响应性能和连接数:
        响应能力和用户体验息息相关,WEB应用在一定条件下连接数和响应能力上却是存在冲突的,
        如:为了提高前端整体响应,可以考虑facebook等使用的BigPipe这样的技术;
        常见的可以考虑异步加载页面、拆分资源等,如把一张大的图片切分成若干小图片(小图片多少合适也是有讲究的,通常是一个页的整数倍,根据不同环境假设4.5K的图片,那么在网络传输以及磁盘IO性能上可能就和4K完全不一样了),由于是分发且并行请求,理论情况下显示多个小图片要比一张大图片来得快,但是对于服务端而言,并发请求数也增加了, 对于一个繁忙的服务器,处理瞬间大量网络连接请求的成本有时会很高,容易造成WEB Server响应缓慢,甚至超时,反而降低请求响应。

        所以很多时候要根据业务类型权衡响应能力和连接数,对响应有高要求的就需要拆分资源,而对服务端处理并发连接要优化的,就需要整合资源(将JS、CSS等多个文件合成一个)。
        一些浏览器对于单个域名请求设置了最大连接数,使用多域名可以增加并发请求数;
        配置keepalive timeout,根据连续请求数情况,以及用户持续提交请求的间隔,设置合理数值,设置不当的长连接反而会额外增加服务器负载,影响性能;
        将静态资源分离到不同域名,避免动态请求上的cookie加到静态资源上;

        数据压缩:数据压缩更像一个时间换空间的作法,通过消耗cpu运算性能降低数据发送流量,额外的好处是还能减少系统数据在内存中拷贝的字节数,对于cpu不是瓶颈的场合建议使用。
        异步:异步处理是提高耗时服务处理的一个重要思路,使用消息队列、分布式调度中间件实现异步处理,
                由于PHP没有语言级的异步机制以及共享内存支持,故在使用第三方服务时要消耗更多的响应时间在网络通信环节。

        分布式系统通常使用client-server-worker模型,性能和响应主要消耗在网络通信以及消息的持久化上,
        1、网络通信高性能,对于fast-cgi下的php尽量使用长连接
        2、消息的持久化,通常使用写磁盘binlog方式实现,因此开销不小,另外可以考虑第三方的共享内存集群来持久化
        3、php语言本身运行时都要初始化进程和引擎来执行代码,性能影响很大,永久进程的php daemon运行方式可以复用进程,提升性能

三、容灾及高可用性
        负载均衡+keepalived:由于负载均衡器自身存在的单点问题,使用keepalived可以使得服务在意外停止的情况下重启或切换到其它健康的服务,以保证负载均衡正常工作。
        可靠性:如动态DNS在一定情况下可能出现负载不均衡的情况;基于不同网络协议层分发请求时,有着不同的特点,如基于4层的负载均衡,无法判断业务类型,由于不同业务的请求消耗的资源很不一样,有可能造成局部的节点压力过大的情况,而基于7层的负载均衡就能很好地解决这个问题,如Nginx等;其次均衡算法本身存在的问题,如使用随机算法时,有可能把多个请求分发到同一节点,而使用轮洵算法,对节点一视同仁,可能处理能力弱的节点就无法满足服务要求,造成负载过高;另外消息传递模式的问题,多数底层负载均衡使用的是push方式,均衡器无法得知节点的处理情况,有可能把消息阻塞在处理节点,造成负载过高,而使用pull方式的负载均衡,节点可以灵活地根据自身处理能力进行拉取,避免了消息阻塞。
        容错:负载均衡机制可以在分布式工作节点故障无响应后自动移除,保证可靠性和隔离性,不影响其它正常节点工作。
        异地容灾:对于重要系统要考虑异地容灭,分布式系统可支持远程连接和处理,可根据本地和远程服务的优先级均衡分发请求。
        平滑处理能力、流控:分布式平台可以对突发流量直到平滑处理的能力,通常使用消息列队方法进行排队,避免处理时造成系统过载、系统资源耗尽引发更严重的问题。在队列层面,当消息滞留过多时增加流控,对消息的提交根据策略予以拒绝。
        余量设计:  通常在物理环境做主机备份,在故障时可切换;   在系统层面做进程管理,可随时增减进程数。
        另外daemon方式的php在运行一些时间后可能会出现内存泄露等问题,设置一定策略重启机制如处理N个任务后、处理N个小时后,重启进程方式可最大程度避免问题发生。

四、可维护性
        易于安装和部署; 管理和监控工具;可配置;
        自动管理,可自动面对不同访问压力做出扩展和收缩。
        可恢复:对任意服务层的主机做备份机以便于随时切换,对于系统层服务,如php程序等,很多时候运行于daemon方式,可做到进程的平滑重启,以及代码版本的无逢更新。
        根据需要灵活地定制功能,而不轻易被系统所限制,如增加一些脚本解析支持。
        分布式平台对于非开发人员的访问接入是否足够简单,比如一些通用的访问协议telnet、http等方式都能给第三的调试和测试带来及大方便。
        调试工具是开发阶段重要的组成部分,可根据API描述简单地提交数据,并给出结果以及运行时间等参数数据。


五、安全性
        对内消息传递时,保证通信仅限于局域网内,在服务程序、系统或防火墙层面限制对公网开放端口。
        对外、跨机房远程通信时,保证消息的加密签名有效性和可靠性,加强认证和访问控制,可添加IP限制、时间有效期限制等机制增加异地通信的安全性;
        在程序层面要加强数据的边界校验,防止程序运行时的各种数据溢出造成安全性问题,如:在php中可适时增加编码校验、非法字符过滤、数据大小限制、类型和格式校验等。



六、一致性
        分布式资源访问竞争:分为平等组和等级组, 平级组实现较为复杂,为网状结构,需要基于消息的投票算法(如Paxos),优点是可用性较高,缺点是性能相对较差;等级组的实现较为简单,主要基于一个协调服务,如PHP中可以使用共享内存实现一个分布式锁,共享内存本身也是一个协调者,缺点也很明显,因为共享内存本身的可用性也要保障。
        强一致性,分布式系统的跨主机事务常见的有2PC(两阶段提交)方式,对系统的可用性影响非常大,任何节点的故障都会造成事务失败,同样也会极大影响整体性能,事务要向每个节点进行提交确认以及资源锁定。
        弱一致性是一种有效权衡的解决方案,如MySQL中的Master&Slave结构,以及php中使用的缓存或境像,缓存总是和原数据存在一定时间的不一致性,但最终总是会更新到较新的数据,所谓最终一致性,这经常会导致一些问题,所以我们经常会通过一些机制强制实现缓存和原数据的一致性,包括手工清缓存。最后碰到更新一致性的问题时还可以引入version机制等来解决。

你可能感兴趣的:(php分布式开发)