【大型网站技术架构笔记】(三)高性能与高可用架构
性能测试指标
1.响应时间。
2.并发数。如果暂时没有对应的准确监控,针对不同业务模型,可以有不一样的并发数的预估。我们的系统进行峰值并发数预估的话,有一种比较粗略的计算方式,即全天请求平均每秒并发数 * 3。但也需要case by case。
3.吞吐量。比较常见的有QPS(每秒查询数)、HPS(每秒http请求数)以及TPS(每秒处理事务数)。
4.性能计数器。包括系统负载、线程数、cpu、内存使用情况等。可以用top、free、cat /proc/cpuinfo等命令来查看。系统负载的定义为当前被CPU执行的线程数/等待被CPU执行的总线程数。当其值与逻辑cpu个数相同时是最佳状态,其代表所有的资源都被最大限度地被利用。但也有人认为当负载为0.7倍逻辑CPU数时最佳。
1)系统负载、任务、cpu、内存使用情况:
$ top top - 22:50:09 up 1093 days, 6:14, 1 user, load average: 18.18, 14.55, 10.18 Tasks: 275 total, 12 running, 261 sleeping, 0 stopped, 2 zombie Cpu(s): 1.9% us, 8.5% sy, 42.1% ni, 47.3% id, 0.0% wa, 0.0% hi, 0.1% si Mem: 65878264k total, 65837688k used, 40576k free, 130476k buffers Swap: 1020088k total, 635080k used, 385008k free, 40273792k cached
2)内存使用:
$ free Mem: 65878264 65757048 121216 0 135408 39984028 -/+ buffers/cache: 25637612 40240652 Swap: 1020088 635080 385008
3)逻辑CPU处理器个数:
$ cat /proc/cpuinfo | grep "processor" | wc -l 12
需要格外注意的是。需以processor个数为准。cpu cores可能出现偏差。要想获取准确的服务器CPU信息,请根据model_name到网上去搜。
web前端性能优化
浏览器访问优化
- 减少http请求。避免建立太多通讯链路。将js、css、图片文件尽可能合并。避免太多请求。同时,对于系统的后端请求也尽可能进行合理的设计,来避免出现太多交互。
- 使用浏览器的缓存。http头设置Cache-Control和Expires.js文件名比如可以带时间戳。一旦有更新则更新时间戳,否则缓存;同时尽量避免同一时间更新大量静态资源。
- 对静态资源进行压缩。
- css放置在页面最上方,js放下最下面。以提前进行css渲染。同时避免js带来的页面阻塞。但需要case by case。比如页面dom节点需要依赖js生成,则可视情况改变文件位置。
- 减少cookie传输。同时让静态资源有独立域名,发送静态资源请求时候不发送cookie。以此减少传输代价。cookie可以通过document.cookie获取。
CDN加速
缓存图片、文件、CSS以及script脚本。但是pc上的CDN加速效果要好于移动端。经过调研发现,last-mile的延迟越高,CDN的相对有效性越差。
反向代理
可以提供七层负载均衡(http请求进行均衡策略),并且可以提供静态资源的缓存,请求转发,防止网络攻击等。比较流行的有nginx。
应用服务器性能优化
分布式缓存
网站性能优化的第一定律:优先考虑使用缓存优化性能。
- 一般来说,存入cache的数据的读写比在2:1以上;且应该是热点数据。
- 需要考虑如果采用缓存则可能带来的数据短期内的不一致,或者如果实时更新缓存可能带来的性能和资源开销。
- 需要考虑cache一旦失效,大量请求直接命中DB可能带来的服务性能雪崩。所以可以对cache采用集群化部署,以此避免丢失过多数据造成服务压力陡增。
- 对于热点数据考虑进行缓存的预热加载。比如高峰期来临前,先将热点数据提前存入缓存。以此提高高峰期的服务性能。
- 为了避免恶意攻击,一直query不存在的数据,导致cache无法命中而频繁访问DB,可以将不存在的数据也进行缓存并定期清理。同时有机制对恶意请求进行识别和封禁。
- 分布式缓存应该去中心化并集中管理。通过不同实例间的互不通信和同构来保证可扩展性,并降低系统复杂度。
异步化
任何可以晚点做的事情都应该晚点再做。
通过分布式消息队列来实现削峰的目的。通过业务配合技术来解决问题。比如12306的排队。
集群
采用集群也是服务虚拟化的一个体现。用以避免单点问题,同时提供更加高可用,高性能的服务。
代码优化
- 多线程中,如果是密集型计算,线程数不宜超过CPU核数。如果是IO处理,则线程数=[任务执行时间/(任务执行时间-IO等待时间)] * CPU核数。除此之外,我们应该将对象设计成无状态对象,多采用局部对象,适当将锁细化。
- 进行资源复用。比如采用单例模式,比如采用连接池。
- 合理设置JVM参数,以最大程度避免不合理的full gc。
存储性能优化
关系型数据库的索引采用B+树进行实现。而很多的nosql数据库则采用了LSM树进行存储。LSM在内存中保留最新增删改查的数据,直到内存无法放下,则与磁盘的下一级LSM树进行merge。所以对于写操作较多,而读操作更多的是查询最近写入数据的场景,其性能远高于b+树;采用HDFS结合map reduce进行海量数据存储和分析。其能自动进行并发访问和冗余备份,具有很高的可靠性。其等于是实现了RAID的功能。
高可用的应用
- 将服务设置为无状态性,并通过负载均衡来对无状态的服务进行失效转移。
- session进行统一集群化管理。
- 通过CAS进行集群中的单点登录控制。
高可用的服务
- 分级管理。区分高优先级服务和低优先级服务,以配合可能到来的服务降级。
- 超时设置。对于超时处理,应该有对应的重试或者快速失败等策略来应对。
- 异步调用。
- 服务降级。高峰期间如果适当采取随机拒绝或者关闭服务的方式来达到将用优先的资源服务最优的请求。
- 幂等设计。重复的操作不会带来数据状态的变化即称幂等性。除了一些核心写库、交易等操作,其余尽量设计为幂等操作,以便定制负载均衡和失败策略。
高可用的数据
- 采用数据备份和失效转移。数据备份分为冷备和热备。失效转移要求在数据失效的情况下迅速切换数据。
- 利用缓存。缓存已经越发成为大型系统中不可分割的数据层组成部分。
- 利用分布式存储系统,比如NFS,比如Hadoop进行数据存储。
- 因为采用了集群、分布式化的服务,所以数据存储过程中的CAP(一致性、可用性、伸缩性)可能要进行一定程度的博弈来进行最后的权衡。有时候,为了A和P可能要牺牲数据的强一致性。这都是需要结合业务进行综合考虑的。
服务于高可用的监控
对用户行为以及服务器性能进行监控。并根据监控引申出系统报警、失效转移、自动降级等保证高可用的操作。
总结
贯穿这两章,其实也贯穿了整本书,本书的作者希望能够传达这样一个观点:技术是为业务服务的,怎么样在当前的业务领域里面最简单但最高效地实现一个高性能、高可用的服务是我们必须关注的,而这一切的立足点,则是业务。