图片中的人物是Jeff Dean ,google分布式系统的灵魂式人物,具体的内容大家可以google一下,他的很多分布式系统设计思想都在影响着技术趋势,而他在google具体工作实现,更是引领着未来。
现在网上有很多类似文章对网站架构的设计原则把握,因此此篇文章只描述LNMP(LVS NGINX MYSQL PHP)架构的个人看法,不涉及通用做法,比如说动静分离、读写分离、cache为王、CDN等等,也不涉及大型分布式系统的做法,什么柔性可用,什么过载保护,什么动态运营之类。仅仅说点其他个人经验认识:
1) 架构简化原则。一定要保证架构的简化,在不引入多余节点的情况,尽量不要引入多余节点,保证架构的极度简化。如果把一个架构拓扑图拆解成一个点和线来看的话,点的选择我们是需要按照标准来选型,比如java容器,有jetty,resin,tomcat,那我们只能选择一种。所谓线的定义是客户端如何实现到后端服务的调用,有些方案使用名字服务使用DNS,有些使用LVS和haproxy。其实更好的做法是:对于小访问量的系统,建议在client api中把连接管理、状态管理、容灾容错管理实现。对于大访问量系统,前端对后端服务的寻址使用统一名字服务来实现,通行做法使用zookeeper,具有很好的分布式特性,并实现高度可用性(5个节点)。
2) 架构分离原则。这个原则貌似和第一条有些冲突,其实不然。当业务越来越复杂,承载的功能越来越多,必须要分离出不同的服务功能。分离的原则:一定是以节点可扩展为第一原则,其次参考业务逻辑分离的必要性。在很多早期架构设计中,服务节点一般不具备扩展能力,这一点非常致命,常见的是文件服务,通行的做法是把文件上传在webserver上,此时webserver就有很重的逻辑状态;在演进一点就是使用NFS的方案,此时我们依然发现nfs还无法扩展,单机的瓶颈依然存在;在往后演进就是FastDFS/MFS等分布式文件系统;再大规模的文件服务,我们就需要考虑GFS(google分布式文件系统)/TFS(淘宝开源方案)方案了;另外开始业务上线的时候,业务功能单一,此时完全可以把所有的服务柔和在一起,随着业务量的增大,不断去分离出一些功能,组成统一的被调用服务。
之前我们的架构分离只有三层,接入层、逻辑层(基于统一的server框架开发)和数据存储层(NOSQL的接口都是一致的,另外有些mysql)
3) 避免过度设计原则。一般的规则是设计10倍于当前业务容量的架构,不要设计100倍于当前容量的架构。100倍的架构,都意味着需要重构了。过度设计也会让我们在轻量级系统上投入很多不必要的其他成本。比如说业务容量很小的时候,单实例mysql已经足够支撑业务容量,而此时非要引入分库分表的算法,大可不必。
4) 客户端智能原则。所谓的客户端智能就是让客户端来决定返回后端的哪个服务节点,如果该某个服务节点健康不达标,则直接剔除该服务。就拿LVS来说,我们知道它有很好的四层容错性,但没法做到彻底应用协议层容错(可以写少量的脚本插件来实现),如果把它作为一个后端服务的容错和容灾节点,肯定是不合适的。通行的做法,客户端收集后端服务的访问错误和延时情况,使用一个具体的算法来完成对后端服务节点的评价(可以独立成一个本地agent),确定下次访问该如何使用哪个节点。
5) 无状态化原则。所谓无状态,就是任何一个服务节点down掉,都不影响服务。从用户访问进入到我们的服务节点,就需要时刻牢记这一准则,LVS我们可以不采用主从,采用LVS OSPF方案,彻底解决主从的问题;nginx、webserver都不要保留业务的状态信息,彻底的去状态(常见状态数据如用户头像、用户的session数据等等);在数据存储层,NOSQL有很好的分布式方案(参见bigtable和GFS),而我们经常使用的mysql则还没有对应的集群方案(mysql cluster不成熟),但是我们可以通过分库分表sharding的方案,再结合mysql的双主、主从等数据保护措施来完成mysql的状态保护。
6) 透明化服务原则。Zbus就是一个很好的例子,服务的位置无关性,内部访问统一控制等等。在很多系统中,无法做到这一点,这也是开发运维仅仅耦合子在一起的一个重要原因。当服务具备透明化的能力之后,一些变更就完全可以让运维来控制,大大提高运维的故障处理和应急能力。可以顺带思考一下,有一天当不同的系统需要不同的ZBUS服务集群的时候,我们如何更好的管理这些集群?服务如何与这些集群透明对应?
7) 端到端监控原则。该写日志就写日志,特别是重要的错误日志,这个在初期故障定位的时候非常有帮助,另外运维需要做好从基础设施层、到应用服务层的完全监控,更高级的做法是结合用户访问流完成端到端的监控。这个难度很大,不关是在这个监控设计本身,还有监控带来的海量数据分析,更是头疼,一般通行做法选择的是抽样采集。
8) 场景决定存储原则。存储的选型一定要从场景出发。以下几个维度是在存储选型的时候,必须时刻要问自己的问题:高访问量和低访问量?单用户数据是大还是小?热点是集中还是分散?整体数据量是大还是小?是否有事务?存储本身的集群化能力?等等。能用redis搞定的,就不要用memcache,能用mysql搞定的就不要用memcache。我们看过存储的接口就知道,他们的API绝对是不一样的,如果引入过多的存储一则增加开发难度,更难的是这些存储之间的数据同步,另外要彻底掌握一个存储难度非常大。之前有很多例子cassandra和mongodb控制不了切回mysql的情况,最大的禁忌是按照网上的文章来选择存储。
关于NOSQL,在我们目前的阶段来说,根本不需要考虑,甚至我们的业务在增加10倍。
建议存储方案:mysql redis或者mysql handlesocket,都有数据持久化的能力 cache的能力。
9) 数据隔离原则。对核心数据的保护尤其重要,它是数据安全的重要部分,运维策略就有很多种,数据分级、数据专区保护、数据审计、定时密码更换机制、数据操作可视化等等。然而从开发的角度来说,需要有个重要的保护措施----提供统一的数据访问保护。我的建议在数据库之前分离出一个数据访问层(DAL),这一层可以完成数据库的路由访问控制、分库分表访问控制、甚至过载控制。这样的分离能够进一步提高数据的安全性,用接口的形式对外提供服务,可以有效避免数据访问信息的泄露。
10) Zookeeper的灵活使用。这是一个让人非常兴奋的组件,很少有组件从底层算法、到设计实现、到核心应用场景覆盖都如此的特别,因此要把它单独拿出来说一下。这个是google chubby的开源实现,有着广泛的使用场景,它能做运维的配置管理,它能业务的配置管理,它能做名字服务,它能做产号程序,它可以做分布式消息队列,关键是它能跨IDC,还能保证一致性。在一个分布式系统中,它已经变得不可或缺。
11) 控制一切的原则。大家一定和你的系统建立了感情,然而这种感情不能给予充分的自由,而是控制。从浏览器、DNS、LVS、webserver、逻辑层、数据存储层。能自己开发的就自己开发,不能自己开发的,一定要深入了解它的特性,以便更好的控制它。就前端浏览器来说,我们需要知道IE的整体分布情况,特别是IE6(影响webserver的压缩算法配置);不同的IE版本并发连接数是多少?IE内核和其他的浏览器内核又有什么不同;chrome又有着什么新特性。对待DNS也是如此,随着系统规模的越来越大,必须走到自建DNS的层面上,其他架构部分都是如此……。我相信中心未来一定也朝着这个方向发展。
12) 中心化和去中心化的思考。Google是中心化设计的杰出代表(GFS/BigTable都是如此),而Amazon代表着去中心化(DYNAMO一致性HASH架构模型)。而我有着不同的认识,在不同的场景下,可以采用不同的架构策略,在前端服务架构中,完全可以去中心化,而在数据存储层个人还是建议中心化的设计,中心化的设计可以减少内部的消息通信量,可以集中控制后端的存储节点的状态,可以统一路由控制策略。
13) 架构是演进出来的,不是设计出来的。它是也业务驱动架构的翻版,随着业务量和业务模型的变化,架构也在同步思考变化。
不总结了,只有待补充!!!