高性能应用构建模式解析

原文: http://java.sys-con.com/node/2116436

原作者的帅照

虽然自己开发的一直都是号称"高性能, 高可用, 高并发"的"三高"应用. 但是一直没有对如何实现这种"三高"应用没有进行深入的思考, 直到最近看到这篇文章, 突然有一种醍醐灌顶的感觉. 所以简单的将其转换成中文, 以方便以后工作中的不时之需.

当谈到一个互联网系统的高性能时, 不外乎以下几点:
  • 以迅雷不及掩耳盗铃之势打开一个网页(低延时)
  • 满足用户访问量的持续增长(可伸缩性)
  • 提供7x24服务, 永不当机(高可用)

下面针对实现上面的这些目标的一些常见的手法进行说明.

一. 延时
应用分层: 这个是系统出现延时的最主要的部分. 一个系统的数据一般会经过web server->application server->database三个系统. 而数据的序列化和反序列化又是整个过程中最耗时的部分. 有时候通过将web server和application server合二为一, 甚至将其揉到同一JVM进程, 然后在代码中通过逻辑分层(web层 + application层)来避免物理上的分层从而达到降低延迟. 比如使用Spring容器来帮助我们实现系统分层. 而一旦采用SOA架构(web service+jms), 网络传输和数据的序列化不可避免的增加了系统延时. 这里可以借助一些其他的东东来降低系统的延时, 比如IBM Datapower XML Accelerator和Solace Message Router (感觉有软文的成分^_^)

让数据靠近应用: 为了让数据更靠近应用, 我们将尽可能的降低对数据库的访问, 而最常用的杀手锏就是对数据进行缓存. 一般我们会在web层和应用层使用类似memcached/ehCache这样的缓存服务器. 在web层我们一般会缓存一些静态的内容, 比如HTML, 图片, javascript, css文件. 而应用层通常缓存非事务数据. 如果是互联网应用, 那么通常会采用CDN来加速静态内容的访问.

降低磁盘I/O: 磁盘IO也是系统性能杀手, 而常用的做法就是将数据保存在内存中, 比如使用内存数据库, 相关产品略去500字, 避免广告嫌疑.

任务并行化:将请求切分层小的任务并行处理, 然后再对处理结果进行合并后返回. 而对请求的处理进行分割就是所谓的Map Reduce, 这个一般通过开源软件Hadoop, CouchDB等即可实现. 也可以从语言层面来实现并行处理, 比如Scala, ERLang, Ada等. 对于Java来说, 可以使用Akka库(基于Actor模型)或者ExectorService并行包来实现.

硬件/网络配置:
     优化硬件--因为应用是跑在硬件上的, 所以对硬件的优化升级也是降低延时的一种手段, 比如采用10G/20G的网络带宽, SSD硬盘, 避免使用虚拟化技术(译注:比如我们正在推广直接在linux实体机部署应用而取代原有的虚拟机中部署)
     传输机制调整--有时候处于安全的考虑, 我们会采用SSL来进行网络通讯, 这个也会导致一定的网络延时.

最后, 通过从以上这些方面(比如缓存问题, 算法问题, 数据问题, 检查方式不对等)查找系统延时的瓶颈, 从而实现对系统的调优.

二. 系统可伸缩性

可伸缩性意味着系统能淡定的承受数据和访问量在一定范围内的暴增而不会挂掉, 另外还有最重要的一点就是以降低系统性能来实现的可伸缩性都不能称为真正的可伸缩性. 通过下面几个方面可以实现系统的可伸缩性.

系统应用无状态: 应用的状态应该集中进行存取(通常就是数据库系统), 而应用本身必须是无状态的. 因此在本地文件系统中将不会存储数据或状态. 应用无状态带来的好处就是可以通过增加任意数量的应用实例来应对访问量的增长. 不过这种做法带来的另外一个问题就是存储状态的数据库会逐渐成为整个系统的瓶颈. 随着数据的增加, 数据库的性能也会开始下降. 这时就需要分散数据库中易变的数据. 为了处理这种场景, 数据分片(data sharding)开始登场了. 另一个办法就是数据库只负责写入, 而部分或全部的读操作尽可能转移到NoSQL存储中.

负载均衡:随着系统之间的调用增加, 系统的请求压力需要通过增加应用实例来化解. 负载均衡将保证系统的压力能均匀分布, 而不会超出现有的系统负载, 使得整个系统的压力能实现自动调整. 对于数据库来说, 也可以通过采用Master-Master或者Master-Slave(读写分离)的结构来实现负载均衡. 但是如果数据增长到PB级别, 就需要采用带数据复制(data replication)的数据分片(data sharding)结构. 另外基于内存的数据表格(in-memory data grid)架构也可以解决数据的可伸缩问题.

系统容错能力: 为了保证一个大型应用集群始终处于可运行状态, 最重要的工作就是要避免人工介入. 比如当系统压力达到既定阈值时, 监控系统能自动增加应用实例, 负载均衡能重新分配调用请求, 保持整个系统压力实现新的平衡. 对于数据库来说, 如果对数据重新进行分片, 应用系统能能重新定位到新的IP并建立连接. 一旦应用系统无法识别对应的资源, 应用能自动切换到其他可用资源. 为了实现分区容错, 整个系统还需要借助一个协调各个系统之间依赖关系的元数据配置中心.

三. 系统可用性
系统的可用性可以认为是可伸缩性的产物, 下面列举的一些因素将影响到系统的可用性.

系统冗余: 系统必须做到某些部分的损坏当机(硬件或者软件)能有一定的备份及时进行补充. 这种冗余备份可以针对系统的任何层面. 比如软件, 硬件, 电源甚至是整个数据中心(即使整个数据中心瘫痪, 系统依然可用). 在大多数情况, 成本投入的大小会决定冗余备份的级别和当机时间.

系统可监控/可检测: 具备一套完善的监控系统对了解整个系统的运行状况非常关键. 如果没有监控, 那么系统的处理余量我们将无法评估. 当我们对系统进行长期监控之后, 我们就可以发现系统的执行流程以及瓶颈, 从而对系统进行调优. 一旦系统有了监控, 系统的自动扩展, 对线上系统的检查将变得更容易. 这里可以借鉴Netflix的做法, 他们借助Chaos Monkey(https://github.com/simonmunro/ChaosMonkey 一套通过在线上模拟各种故障来测试系统可靠性的框架)来检测系统的稳定性.

系统可配置: 要保证系统的持续可用还需要实现在运行过程中能对各种参数进行调整. 比如, 系统提供了新的接口, 那么能通过配置开关来打开新接口或者保证与老接口的兼容性. 当要上线一个新功能, 但是希望该功能不是一次性全部打开, 而是根据条件逐步打开的时候, 这个功能将变得非常重要.

综合上面提到的方面将从各个层面来帮助我们构建一套高可用性的系统.

猜你喜欢

转载自macrochen.iteye.com/blog/1366137