小土刀

【通天塔之架与构】2 云平台架构沉思录

在云资源无比丰富的今天,公司核心新系统的设计开发,尤其是后台架构部分,如果不是仅仅为验证想法而做的 MVP,应该在一开始就考虑好如何利用云平台的特性,这样才能在业务增长时仍能游刃有余。


本文是我在设计新系统时的一些思考,剥离了具体的业务内容,但是仍会在业务层面关注各个系统组件的可用性和伸缩性,力求在设计中充分利用云计算的优势,同时为未来留有余地。简单来说,是结合企业的业务特点构建稳定可靠的分布式系统。

更新历史

  • 2016.07.31: 初稿完成
  • 2016.11.13: 合并到【架与构】系列中

系列文章

他山之石

当当架构部总监史海峰在其为架构师杂志写的开卷语中列举的架构师自我修养工整且字字在理,这里摘抄如下,也算是对自己的鼓励和激励:

  • 以工程思维全面理解业务需求
  • 基于模型和基础模式抽象简化
  • 提出恰当可行的整体解决方案
  • 在限定资源范围完成明确目标
  • 满足业务需求且保证系统质量
  • 在可预见的周期内具备扩展性
  • 并在系统生命周期内持续演进

架构能力的成长没有捷径,也不可能成为所谓的理论家,一定要在项目实践中不断思考积累经验,了解技术和业务发展趋势,用强大的自学能力补足各种短板,严格要求自己,用最大的动力朝抵抗力最大的路径走。当然,最重要的,有责任心,要对自己交付的每一行代码,每一张图片,每一句文字都负责。

一个访问人数众多的 7x24 的互联网服务对于架构师和开发者来说意味着以下三个方面:高吞吐高并发、低延迟、负载均衡。而如果我们想要面向全球用户提供服务,抛开繁文缛节的法律问题不说,如何跨越时空限制,也是难点之一。

《分布式系统本质论》中把架构问题做了一个很好的总结:

  • 互联网海量承载问题
    • 提高吞吐量:分层调用、异步并发
    • 降低延迟:缓存、NoSQL
  • 大量服务器管理问题
    • 故障恢复和可扩展性:分布式目录服务、消息队列服务、分布式事务系统
    • 运维便利性:自动部署工具、统一日志系统
  • 开发效率问题
    • 复杂的通信编程:微服务框架、异步编程工具
    • 大量功能模块需要分工:搭建 IaaS/PaaS/SaaS 云服务

接下来就对云平台架构设计中比较重要的几个关键技术点进行简单说明,同时也有自己的一些思考。

负载均衡

云平台对于用户来说是一个黑盒子,而负载均衡可以算得上是盒子内外的分界线,一个好的负载均衡机制,对于构建高性能高可用的网络架构非常重要。当然,一般来说凡事这类基础且需要一定技术水平才能做好的模块,目前的云平台都会提供,比方说 AWS ELB 就可以让开发人员告别繁琐的服务器配置,接入服务即可。不过即使如此,我们依旧需要了解其中的门道,这样即使需要自己搭建,也不至于手足无措。

负载均衡会和几乎所有的服务器主机连接,具体如何保证网络安全就是一个大问题,一个解决方案是利用 VPC。把主机和系统资源放置在 VPC 网络中并指定内网网段,这样经负载均衡隔离,主机间与资源间只通过内网地址进行通信,就不至于暴露在公网。

负载均衡一般来说有一台服务器负责扮演负载均衡器的角色,是一个单点问题,我们可以为其做冗余备份,一旦出现问题,立刻进行替换。具体涉及的技术一般是七层(Nginx, HAProxy)/四层(LVS)负载均衡,可以根据分发需求与性能需求进行选择;用来监控后端服务器可以使用简单的心跳检查。

从上面的介绍可以看出,负载均衡的原理并不复杂,但是具体落地有太多需要注意的细节,如果不是特别在意价格,建议直接使用 AWS ELB 或其他云服务。

缓存系统

一般来说,为了增加服务质量和速度,都会有一个完整的缓存系统,起到数据共享交换以及提高访问速度的作用。缓存的方式有很多种,我们的业务主要在用 Redis,速度很快,支持的数据结构也比较丰富。

因为涉及到共享数据,就不得不面对数据完整性与一致性的问题。目前在设计的系统的业务场景和滴滴出行的需求类似,即大部分请求是大部分请求是对数据进行修改,少部分请求对数据进行读取。比方说司机地理位置的即时变化是大量写入,而用户查看周围车辆的情况是少量读取。

比方说可以保存一个 Map 的缓存,其中的键值对是司机的编号和司机当前的信息,在并发读写时这个 Map 是临界资源,用户量一上来,就不得不在处理锁的问题上耗费大量的时间,因为一锁就只能锁整个 Map,出现的问题和内存系统中 false sharing 有些类似。优化方法有两种,一种是类似于 MySQL 分库,把一个 Map 拆成多个 Map;另一种是把 Map 转换为数组,这样就可以针对每个数组元素加锁。后一种的精细度非常高,只不过锁本身的开销就会太大需要权衡。

滴滴的解决办法很巧妙,直接去掉了锁,利用数据签名技术,在每次读取前都进行校验,一旦校验失败,则认为是 cache miss,这样就在性能成本较低的条件下保证了数据完整性。

对我自己的启示是一定要多研究业务场景相似时其他公司的云架构,进行一定简化之后可以有效保证之后升级的快捷。

业务层

业务层最关键的问题其实就是服务质量。在版本稳定之后,可以考虑制作镜像,需要时开启并挂载到负载均衡即可,具体判断是否需要也可以通过 AutoScaling 来自动化这个过程,唯一需要注意的就是需要在实际环境中测试并设置好触发条件,前期肯定要投入不少人力。

而如果是开发中,服务器环境和程序快速变化迭代的时候,最好依赖自动化部署工具,按照我一贯的思路,选择简单的,所以 Ansible 是不错的选择。

还有一些需要注意的地方:

  • 服务器连接无状态,因为负载均衡分发使得上下文不可靠,每次请求都需要带上所有信息,是在需要上下文,保存到缓存或数据库中
  • 根据业务需求拆分服务,降低耦合性,即使子服务故障也不会影响系统的可用性,进行扩容时也更加灵活
  • 不同子服务的调用异步化,利用消息队列来实现,好处在于消息队列可以把数据进行多向发送,满足业务需求的同时,还能导入数据分析平台,更加灵活

数据层

业务相关的场景大多都是对数据库的增删查改,如果单机数据库已经无法满足需求,可以利用缓存配合一致性哈希来处理热点数据的访问,或者增加节点。对于大的,用于后期分析或存档的文件,可以考虑直接通过消息队列保存到 AWS S3 中。

我个人是比较倾向于使用 NoSQL 数据库的,但是 NoSQL 没有所谓的万能钥匙,真正落地有太多可以优化的地方。主要有键值存储、文档数据库、列族存储、图数据库这么几类:列族重写入,文档更灵活,键值高性能,图处理关系。就目前的需求来说,图数据库配合文档数据库可能是比较好的选择,不过具体还需要进一步测试。

数据库的性能优化就是个大问题了,虽然 NoSQL 在一定程度上摆脱了传统迂腐的 SQL 设计,不过一个设计精良的存储方式,能减轻很大的性能压力。

后端框架

框架这个问题有太多可以说的,前段时间主要在做 Ruby on Rails 相关的工作,对于 Ruby 本身和 Rails 的设计思想有一定的了解。

简单来说就是 DRY 原则 - Don’t Repeat Yourself。代码中有重复和相似地方不可避免,把这部分抽取出来,只写必须要写的代码,也就是所谓的业务逻辑。

至于设计模式什么的,实话说我觉得有些过时,框架等于是在设计模式的基础上更上一层,极大提高了开发效率,但是却让开发者真正成为了搬砖的角色,个人觉得是非常痛苦的。

为什么痛苦?我觉得最不能忍受的就是『我』成为了『框架』的一部分,就好比『我』要用『框架』,最后却发现是『框架』在用『我』,类似的,和『我以为自己在玩游戏其实却是在被游戏玩』一个感觉。

但是,但是,这绝不是说真的就不需要框架。练剑的最高境界,是人剑合一,用框架也是如此,当我们了解了框架是怎么运作的,也就不再是框架的工人,而变成了框架本身。

所以可以这么看,框架的选择就是系统个性的选择,按照我自己的习惯,当然是要走极简主义的轻框架,灵活且易于自定义,适合小团队。

总结一下,具体选择什么框架,有以下这么几个考虑

  • 按照工作量和系统复杂度选择最合适的框架
  • 按照开发人员的风格选择最合适的框架
  • 按照业务需求和特点选择最合适的框架

看起来都很虚,要慢慢悟。

总结

不谋万世者,不足以谋一时。虽然架构演进是一个漫长而艰巨的过程,但是加入一定程度的面向未来设计,后面随着业务发展也会少走一点弯路。

希望自己在实践中也能牢记此律。

扩展阅读

您的支持是对我创作最大的鼓励!

热评文章