第三届字节跳动青训营——架构学习

一、架构基础

  • 架构定义:有关软件整体结构与组件的抽象描述,用于指导软件系统各个方面的设计
  • 常见软件架构
    • 单机:所有功能都实现在一个进程里,进程部署在单台机器上,运维时需要停服
      • C10K问题(Concurrent 10,000 Connection):服务器如何支持10K个并发连接,进行高性能网络编程。解决方式:采用IO复用模型epoll方法,在调用返回时,只给应用提供发生了状态变化的文件句柄,不需要轮询fd(文件描述符)
      • 单机架构瓶颈:
        • 需要大量进程 / 线程作为处理单元,需要占用大量内存空间
        • 进程 / 线程切换,系统调度代价高
      • 解决方案:采用协程(Routine),一个线程中,存在多个协程。协程实现如Go语言的轻量级线程Goroutine。协程由程序控制,在用户态中执行,在线程的基础上通过分时复用的方式运行多个协程
        • 协程适用场景:IO密集型任务,异步IO型任务(异步IO型任务包括:主动查询是否有数据;被动监听是否有数据状态)
    • 单体:分布式部署,进程部署在多台机器上,具备水平扩容能力(添加更多服务器),运维不需要停服
      • CAP理论:一个分布式系统最多只能同时满足一致性(Consistency)、可用性(Availability)、分区容错性(Partition tolerance)三项中的两项
        • 一致性:所有节点同时看到相同的数据
        • 可用性:读写总是成功的
        • 分区容错性:尽管任意消息存在丢失或部分系统存在故障,系统继续运行
    • 垂直应用:单体架构基础上,将进程按照应用垂直切分开的单体,在一定程度上减少了后端进程职责
    • 面向服务型架构(SOA,Service Oriented Architecture):将进程按照不同的功能单元进行抽象,拆分为服务。SOA为服务间通信提供标准,采用重量级协议,如SOAP及其他WS标准
    • 微服务(Micro-Service):SOA的去中心化的演进,每个服务都有自己的数据模型和数据库
      • 注意事项:
        • 数据一致性
        • 高可用
        • 治理、容灾
        • 解耦 vs. 过微
        • 权衡运维成本
  • 架构演进方式:业务需求量逐渐增大、系统逐渐复杂
    • 水平切分:分层 / 模块化
    • 垂直切分:分布式

二、企业级后端架构

  • 云计算:通过软件自动化管理,提供计算资源的服务网络,是现代互联网大规模数据分析和存储的基石
    • 虚拟化技术:对计算机资源的抽象,实现资源隔离与共享,资源异构性屏蔽,资源池化
      • 硬件层:VMware
      • 操作系统层:Docker
      • 网络层:Open V Switch(OVS),基于软件实现的开源虚拟交换机
    • 编排方案
      • VM:OpenStack(一款开源虚拟化平台) / VMWare Workstation
        • OpenStack各组件:
          • Horizon,Web前端服务
          • Nova,计算服务
          • Neutron,网络服务
          • Swift,对象存储服务
          • Cinder,块存储服务
          • Keystone,认证服务
          • Glance,镜像服务
          • Ceilometer,监控服务
          • Heat,集群服务
          • Trove,数据库服务
      • Container:Kubernetes、Docker Swarm
  • 云架构
    • IaaS:Infrastructure as a Service,基础设施即服务,对底层硬件资源池的抽象。消费者在其中的角色是开发者或系统管理员。如阿里云的ECS云主机、Amazon EC2(2006年推出)
    • PaaS:Platform as a Service,平台即服务,基于资源池抽象,云中有完整开发和部署环境。消费者在其中的角色是开发者。如Google App Engine、Windows Azure Platform
    • SaaS:Software as a Service,软件即服务,基于弹性资源平台,一切不需要本地化部署的云服务软件产品。消费者在其中的角色是终端用户。如OA、CRM、E-mail服务
    • FaaS:Function as a Service,函数即服务,更轻量级的函数服务。开发者只需要编写业务代码,无需关注服务器,并且代码的执行由事件触发。如LeetCode的Online Judge系统、AWS Lambda
  • 云原生(Cloud Native):构建和运行可弹性拓展的应用
    • 弹性资源
      • 弹性计算资源:虚拟化容器、快速扩缩容
        • 服务资源调度
          • 微服务
          • 大服务:调用量多
        • 计算资源调度
          • 在线计算:互联网后端服务,如热销榜单
          • 离线计算:大数据分析,MapR / Spark / Flink,如热销榜单更新
        • 消息队列
          • 在线队列:削峰、解耦
          • 离线队列:结合数据分析的一整套方案,如ELK
      • 弹性存储资源:将存储资源当成服务一样
        • 经典存储
          • 对象存储:视频、图片等
          • 大数据存储:应用日志、用户数据等
        • 关系型数据库
        • 元数据:
          • 服务发现:程序如何通过一个标志来获取服务列表,并且这个服务列表能够随着服务的状态而动态变更
        • NoSQL
          • KV存储:Redis
          • 文档存储:MongoDB
    • 微服务架构:提供业务功能单元解耦,提供统一的通信标准。微服务将单一应用程序划分成一组小的服务,每个服务域进行在独立的进程中。云原生场景下,微服务不必在业务逻辑中实现符合通信标准的交互逻辑,而是交给框架来做
      • 通信协议:轻量级的通信机制
        • HTTP(RESTful API)
        • RPC(Thrift,gRPC)
      • 微服务中间件RPC和HTTP:选用时从性能(RPC提供大量压缩方案)、服务治理、协议可解释性(HTTP的JSON格式)来考虑
      • 微服务的部署方式
        • 虚拟机
        • 容器
          • 解决了应用程序运行环境依赖
          • 实现计算资源、网络、存储的隔离
          • 提高部署密度和计算资源的利用率
        • 无服务器模式(Serverless):由开发者实现的服务端逻辑运行在无状态的计算容器中,由事件触发,完全被第三方管理
          • 无需管理基础设施
          • 事件驱动
          • 自动化构建部署
          • 无服务器计算 = FaaS + BaaS(Function as a Service + Backend as a Service)
    • DevOps:贯穿整个软件开发周期
      • 敏捷开发
      • CI / CD(Continuous Integration / Continuous Delivery,持续集成 / 持续交付):实现应用开发中高度持续自动化和持续监控
    • 服务网格(Service Mesh):微服务之间通信的中间层,一个高性能的四层网络代理,数据平面代理与业务进程采取进程间通信的模式,将流量层面的逻辑(包含治理)与业务进程解耦
      • 业务与治理解耦,生命周期易于管理
      • 异构系统的治理统一化
      • 具有复杂治理能力

三、后端架构的挑战

  • 基础设施层面
    • 存在问题:
      • 离线任务(主要为CPU密集型,非实时性)和在线任务(主要为IO密集型,潮汐性、实时性)的物理资源用量峰期不同。
      • 物理资源有限、带宽有限,资源利用率受限于部署服务
    • 解决思路:
      • 离在线资源并池:采用混合资源池,按时间段划分离线资源池和在线资源池,提高物理资源利用率
      • 同一个机器上的离在线隔离:使用容器对CPU核心做隔离,使用cgroup统一对离在线进程进行分组
      • 利用在线业务潮汐性自动扩缩容,扩缩容的依据指标:如CPU使用率的P50数值(中位数值)、内存利用率等
  • 用户层面
    • 存在问题:
      • 网络抖动导致运维成本提高
      • 异构环境中,不同实例间的算力差异(CPU型号差异、网络请求差异)
    • 目标:打平异构环境算力差异,为自动扩缩容提供正向输入
    • 解决思路:如何屏蔽异构环境的算力差异?CPU水位负载均衡
      • IaaS:提供资源探针(Probe)
      • 服务网格:动态负载均衡
  • 微服务层面
    • 存在问题1:微服务间的高通信成本
    • 目标:降低业务成本,提高服务可用性
    • 解决思路:微服务亲和性部署,满足亲和性条件的容器微服务中间件采用IPC(Inter-Process Communication)通信,不满足亲和性条件的容器通过服务网格RPC(Remote Procedure Call),从而减少网络通信。
      • 将满足亲和性条件(调用关系紧密、通信量大的服务)的容器部署到一台宿主机
      • 微服务中间件与服务网格通过共享内存通信
      • 服务网格控制面实施灵活、动态的流量调度
    • 存在问题2:流量治理问题
    • 目标:提高微服务调用容错性、提供容灾、DevOps提高开发效率
    • 解决思路:基于微服务中间件和服务网格的流量治理
      • 熔断、重试
      • 单元化
      • 复杂环境(功能、预览)的流量调度

四、后端架构实战

  • 如何做架构设计
    • 需求先行:问题定义
    • 业界调研:可参考的业界解决方案
    • 技术选型:内部 / 社区中的基础组件
    • 考虑异常情况:考虑某服务不行了怎么办
  • 基本概念
    • 负载均衡(Load Balancing):将工作负载分布到多个服务器来提高服务的性能和可靠性
    • 服务发现(Service Discovery):容器部署在集群时,服务地址(IP和端口)是集群动态分配的。服务发现解决了在微服务中如何精确的定位需要调用的服务IP及端口
    • 服务注册(Service Registry):服务提供者将自己的服务地址等信息登记到服务注册中心
    • 注册中心:服务提供者和服务消费者之间的桥梁,提供管理和查询服务注册信息的API。当服务提供者的实例发生变更时,服务注册表更新最新的状态列表,并通知服务消费者
      • 如Zookeeper、Netflix Eureka
    • 宿主机(Host):可以是物理机或虚拟机
    • 容器(Container):包含了完整的运行时环境,基于内核的轻量级虚拟化技术。容器允许同一物理或虚拟服务器上毫不冲突地运行多项工作负载
    • 时序数据(Times Series):与时间序列相关的数据,如哨兵监控的指标数据、业务数据(JVM GC、时延)
    • 一致性哈希(Consistent Hash):由于HTTP协议的无状态,服务器可能选择存储会话日志来记住用户,从而减少身份验证。因此,在添加或删除服务器时,需要尽可能减少对其他服务器的影响
      • 一致性哈希算法的步骤:
        • 将服务器节点哈希映射到[0, 2^32-1]的整数圆环上
        • 将请求哈希映射到圆环上
        • 从请求哈希映射的顺时针方向找到其右侧最近的服务器
      • 作用:增加 / 删除服务器时,最多只有一台服务器会受到服务器数量变化的影响
  • 问题:如何设计一个根据主机层面的资源信息,实时进行流量调度的系统,屏蔽不同宿主机异构环境的算力差异?
    • 输入
      • 服务网格数据面:支持带权重的负载均衡策略,服务发现,流量调度
      • 注册中心:存储所有容器的权重信息
      • 宿主机:提供容器的资源使用情况、物理资源信息(如CPU型号)
    • 关键点
      • 紧急回滚能力
      • 大规模场景中使用:主要考虑系统稳定性和计算瓶颈
      • 极端场景
  • 架构演进方向
    • 方案一:自适应静态权重。采集宿主机物理资源信息,调权代理调整容器注册的权重,缺少紧急回滚能力
    • 方案二:自适应动态权重。动态权重决策中心从注册中心获取和更新服务注册信息,从配置中心获取服务在注册中心的名字,通过计算赋予服务运行时自适应动态权重,具备紧急回滚能力
    • 方案三:在服务网格层面上报RPC指标(e.g. 实例:X:9090,延迟P50:10ms,延迟P99:100ms),防止权重调节过于偏颇。动态权重中心除了方案二的作用以外,还采集所有服务容器指标,使得极端场景处理成为可能,但时序数据库压力较大
    • 方案四:构建更复杂的动态权重决策中心
      • 微服务化进行拆分(如分为入口层、离线分析层、在线分析层等)
      • 引入消息队列削峰、解耦
      • 离在线链路切分
      • 梳理强弱依赖

 参考文献

[1]兰新宇. 架构初探-谁动了我的蛋糕. 参考链接