今天给大家分享今日头条架构演进, 前面几位讲师讲了很多具体的干货, 我的分享偏重基础设施及架构思路的介绍, 我们想法是通过提供更好的基础设施, 帮助架构做更好的迭代.
从架构的角度, 技术团队应对的压力最主要来自三方面:
服务稳定性. 接口的稳定性, 让服务更可靠;
迭代速度. 迭代速度对于大公司来讲相对没那么重要, 规模比较大, 生存压力相对小一点, 但相对中型小型公司来讲, 迭代速度是必须要保证的, 时间窗也是一个决定能否成功的重要因素;
服务质量. 主要关注用户满意度, 它也是一个特别重要的 topic.
今日头条发展特别快, 只有 4 年的历史, 从人员数量和规模增长来看非常快, 在稳定性可用性方面压力比较大, 一方面需要快速把业务实现, 但在另外一方面, 类似这些高可用的问题会经常骚扰工程师: 上线就挂, 运营活动量大服务崩溃, 单机性能顶不住, 一个小服务上线把核心服务搞挂了...... 类似这些问题, 技术团队需要如何更好的去应对?
先补充下我对架构演进的理解, 在不同阶段的公司都会面临各种压力. 小公司压力可能是业务没起来, QPS 很低, 要做优化也没有环境及条件; 当公司大了, 服务器可能已经不是问题, 但你要不断考虑调优及应对访问压力, 改善基础设施以提供更稳定的开发环境. 所以说架构演进是持续一个过程, 没有终点.
为什么今日头条有这么大的压力? 今日头条增长速度是比较快的, 从上图可以看出, 现在公司已有 4 年, 2014 到 2016 年每年都是 DAU 翻番. 这对业务挑战是非常大, 规模上来以后, 我们原有的架构难以做到线性扩展, 部分能线性扩展的服务, 问题也比较多, 业务增长太快, 后端压力比较大.
头条架构发展简史: 三个历史阶段
今日头条的架构是怎么发展过来的?
从来没有一个完美的架构能够一直支撑下去, 架构是动态系统, 实时变化的, 因为量变而发生质的变化, 不同的阶段需要不同的架构.
什么时候需要做架构上的改造呢? 当突然发现系统问题越来越多, 经常出现事故或者报警特别多, 沟通的效率降低等问题, 很有可能你的架构出现问题了.
软件架构有一个问题, 它改动的周期相对比较长. 架构的模式思路定下来, 随着业务的增长, 包袱越来越大. 做过基础设施的人都有这样的体验: 有一个好的想法很容易, 但做一个好用软件就有很多的困难. 技术改造是漫长的, 以年为单位. 所以这个时候只能让架构迭代更快一点. 最后, 不要企图做特别完美的架构, 我们只要保持敏捷演进就好了.
架构不可避免会劣化.
头条第一阶段: 三层结构
今日头条刚开始做的时候, 就是一个简单 web 应用, 搭个数据库, 把业务实现就行了. 头条最开始的优势是推荐引擎, 还有另外一套数据挖掘和离线计算. 在线的服务在前端相对来讲模式还是比较清晰的, 三层就搞定了. 业务刚开始起来的时候, 没什么问题, 访问增大水平扩展一下就可以解决.
头条第二阶段: 拆分
跟大部分公司的架构演进历史非常相似, 当上个版本遇到一些性能问题后, 最简单就做一些拆分. 优化的过程中, 那一块太重了就从代码上进行拆分. 上图中, A,B 和 C 是不同的业务, 刚开始代码是一起的, 演进的过程中, 迭代一年或者两年的产品, 异构去拆其实挺痛苦.
前面时代的架构, 基本上没考虑太多的人员或者规模上的发展, 刚开始也没有专门的人做架构优化, 很多人都扑在业务上, 把功能点加上. 比如推荐的效果不好, 就加强推荐, 每块都没有专门的的人去考虑整体架构去怎么组织规划.
到了去年, 每个季度做的预算, 到第二个月机器都用完了. 高峰的时候有 60% 到 70% 的压力, 这里涉及有两个问题: 第一个问题, 有些地方是性能衰减的问题; 另外一个, 业务压力太大.
架构团队需要想办法变得更快, 即使出现访问问题, 压力大, 机器不够, 也要让我们的服务有所保证. 业务一直在快速前进, 包袱是比较沉重, 改造的成本比较高. 基于这些问题, 谈下我们下一阶段的思路, 做微服务.
头条第三阶段: 微服务
目前我们的思路是通过微服务方式做新架构. 通过拆分成子系统, 大的应用拆成小应用, 抽象通用层做代码复用.
系统的分层比较典型. 我们重点在基础设施, 希望通过基础设施提高快速迭代, 容灾和一系列的工作, 希望各个业务团队能更快做业务上的迭代以及架构上的调整.
微服务架构
微服务我们认为最关键的三点
解耦, 一个服务会依赖另外一个服务, 模块或子服务的概念.
轻量, 减轻维护人员的成本.
易管理.
现实中微服务的关键是自治. 虽然微服务是自治自包含, 但也需要有一个层级. 比如你提供的服务是外面公司提供的, 微博提供的服务, 你不能够要求微博为你得服务去做更改. 微服务要有界限, 在公司层面, 不能让它过于独立, 过于独立会增加沟通上的成本. 基础设施和规范最好能复用.
现实中的微服务是什么样的?
架构必须要落地成具象的东西. 微服务有一个开发框架, 做业务的同学根本不需要关心容灾, 也不需要重复做这样一套东西, 这个东西怎么部署, 他们也不用关心;
需要有一个流程规范性去约束. 有规范就可以做全局优化;
微服务的表现形式是提供一个平台或者一些工具.
头条服务化的现在及未来
最后再给大家介绍前面服务化的思路在今日头条是怎么执行的? 怎么给各个业务团队开发者提供服务?
头条的主要服务化思路如下:
立规范. 规范怎么做? 部署 RPC, 一个服务调另外一个服务是怎么做的? 创新我觉得没有问题, 但你得考虑给其他人带来成本, 这个规范还是需要有的, 这样可以做全局控制. 服务化的稳定和统一, 你要考虑它带来真正的优势, 性能高是一个点, 但是本地优先会好一些;
打基础. 有了规范以后, 开始真正落地的服务. 比如说基础库, 把 Ngnix,Redis,MySQL 这些库封装起来, 统一起来做一些事情. 开发框架, 你不用关注数据去优化;
渐进. 先拆离再迭代, 把服务优化进来;
一切都是服务, 第四点是和其他公司或团队稍不一样的地方, 我们的想法是一切都是服务, 每个节点都是抽象归属于某一个具体的服务. 存储的确是一个服务, 但它不只是提供 API 或者提供功能的东西, 还需要包括服务质量, 需要别人用起来是比较简单的;
平台化. 最后的落地是平台化的东西. 我们框架是怎么设计, 和服务怎么结合?
首要规范: 一切都是服务
资源是有限的: 按需申请, 需申请和授权;
简单的使用方式: 开发者只需要关注业务;
有唯一定位的方式: 用全局资源定位;
最后, 每个服务都有拥有者 (owner), 偏工程架构方面的东西, 我的规范必须可执行的.
我们的规范
必须要有全局的中心, 服务统一注册到 consul 中;
服务有唯一的标示, 命名范:{产品线}.{子系统}.{模块} P.S.M, 公司有很多部门, 我们不希望部门之间沟通起来有差异, 所以需要有全局规划去追溯它;
业务服务使用 Thrift 描述接口, 必须传递标准参数. 如果用弱的描述数据, 没有强约束, 在客户端的数据可能会出现类型错误;
RPC 使用统一收敛的库;
Nginx,Redis,MC,MySQL,etc 都是服务
服务注册
我们服务统一使用 loader 或 wrapper 脚本启动, 具体启动由业务决定.
服务启动会有一个名字, 把 app 注册到服务里面, 看起来有一些约束, 数据库 MySQL 可以启动吗? Redis 可以吗?
启动的时候, 服务方式不用去管, 就用同一个框架, 一个新的规范, 容易把已有的服务迁移上来, 但这不是个特别强的规范, 考虑迁移成本. 轻规范, 易迁移.
服务中心
服务中心有服务信息, 会同时带上是什么样的服务, 其他人比较简单的调这个服务就 OK 了. 这个服务到底提供什么样的服务质量, 拥有者可以管理这个信息. Redis 去服务, 负载均衡, 服务一个项目, 把服务接上去.
服务关系与授权
服务之间有个关键的概念: 服务授权. 一般我们起一个服务, 通过 IP 就可以连上它了. 数据库有用户名认证, 也可以对 IP 授权. 不过内网很多服务限制比较少, 不是所有服务都有授权认证. 我们希望把服务之间的关联关系, 全局拓扑关系记录下来并且可执行.
一个服务提供接口, 我们可以由 owner 来做授权, 其他服务授权后才可以访问它.
描述信息: 这个服务是什么样子? 最大的 QPS 是多少? 通过描述信息发现问题, 用户信息服务托不住了, 就拒了, 把资源分到其它服务上面, 就可以做更多的东西. 还有机房信息可以放在这里面.
服务授权认证思路:
基于服务标示, 重要服务增加更多认证方式;
协同认证, 客户端自身协助认证.
举一个 Thrift 的例子. 这两边有两个虚线, 服务中心水平扩展能力很强, 向它要基本授权的信息, 我可不可以调这个服务? 默认是可以, 就是一个 Thrift 包, 我知道你是谁, 自己做策略, 服务包带过来. 请求带上来, 分析调用是不是有问题, 这也是规范的一部分. 开发的同学是不用关心框架这边如何做的.
另外一种向服务中心调用服务, 把你拒了. QPS 压力大, 已经支持不了你. 一个好处是可以避免浪费资源; 另外, 虚拟化 Docker 的环节. 以前的思路按 IP 授权, 每一个 IP 做控制, 提供类似于匿名服务, 根据节点所属 IP 去做. 现在用 Docker 拿一个标识不太好做, 在网络层也不太好做, 在内网环境下有一定的可信, 我自觉告诉你, 我是谁, 然后调用.
MySQL 目前正在做的一个方案见下图, 不像 Redis 要求带上你是谁, 调用 MySQL 需要把调用方是谁带上来. 一个重要数据库, 肯定做安全授权, 我刚只是说常规情况下. 这几种方式叠加起来做, 把原信息带过来, Redis 带过来, 做加权校验.
Redis 在协议层做不了, 而 MySQL 在调用中增加上述信息不会影响语义. 我们服务器提供 HTTP 接口就可以在 HTTP 头提供这个信息做授权认证.
有授权关系, 所有的服务构成完整服务的拓扑关系. 一个服务预先授权才能调它. 如果有线上真实的拓扑关系, 就可以做报警优化. Redis 报警了, MySQL 报警了, 有这样的拓扑, 会提升问题追查的速度.
我们有了这样的拓扑信息, 知道服务的全局元信息, 我们就可以更好的做服务变更的影响评估和报警等等的优化.
RPC 开发框架
我们自己开发了一个 RPC 框架. 开发框架会帮助我们开发代码, 这个事情很多人都在做. 它的主要特性包括:
快速开发: 代码生成;
服务发现: 理解服务化;
可观测性 (Observability):logid, pprof, admin 端口;
容灾降级: 业务降级开关;
过载保护: 断路器, 频率控制;
多语言支持: Python/Go
比如可观测性是说所有的服务都能暴露内部的状态, 这有非常好的优势, 服务上来以后, 默认剖析内部的端口或者服务端口, 服务上线与平台. 根据拓扑关系自动分析出来服务状态, 甚至做性能剖析, 开发者可以不用关心这些事情, 天然获得这些能力.
还有容灾降级, 还有过载保护, 我们还有一个平台管理关联关系和降级, 你可以更多关注业务.
下面是大概模块的示意图, 通过模块化的方式, 而不是嵌入到框架里面, 使我们的维护成本更低.
前面服务是自治的体现, 跟 Docker 比较像, 我们也会做容器化的开发. 只是把服务跑在容器内还远远不够, 把服务化体系打通, 我们思路实现开放, 实现我们 "有态度" 的私有云, 把基础设施这一块让我们平台做, 业务部门只关心业务.
我们目前到这个环节, 做一个服务的重构, 我们的私有云构建. 前面的框架,
不断去迭代.
最后我们和虚拟化 PaaS 平台怎么规划?
我们通过三层实现, 通过 PaaS 平台统一管理. 提供通用 SaaS 服务, 同时提供通用的 App 执行引擎. 最底层是 IaaS 层.
IaaS 管理所有的机器, 把公有云整合起来, 头条有一些热点事件会全国推广推送, 对网络带宽比较高, 我们借助公有云, 需要哪一种类型计算资源, 统一抽象起来. 基础设施结合服务化的思路, 比如日志, 监控等等功能, 业务不需要关注细节就可以享受到基础设施提供的能力.
Q&A
Q: 刚才讲单体服务拆分成微服务成本是不是有所增加, 你怎么考虑?
夏绪宏: 我在过去建一个数据库, 直接跑起来. 以前把整个库升级, 现在只需要升级一小部分, 业务比较简单规模比较小的时候单体服务确实成本低. 当你的业务增大机器增多, 单体服务会成为瓶颈, 但是微服务如果是标准化的, 可以用自动化的工具, 平台去管理, 不能靠人去管理, 因此成本反而是降低的.
Q: 把服务跑在容器里面, 用了 consul, 自身的容器和 IP 等自身的信息注册到 consul, 更新您授权的 ACL?
夏绪宏: 这的确是一个思路, 我们用了 consul 是去中心化, 不过也是增加了一层. 如果你需要控制微服务的访问与安全, 容器节点还要分级别, 比如我会分到小集群, 物理层是隔离, 以这种方式实现安全的. 仅是 consul 是不够的.
Q:RPC 服务发现是什么? RPC 是自己实现的?
夏绪宏: 服务发现是 consul;RPC 是自己在 Thrift 基础上实现, 服务调用上也实现了熔断机制.
Q: 选型的时候为什么不用开源, 我们是整个平台的架构准备做微服务的改造, 想选一下服务的架构, 您这块是自己做的, 我们在开源和自己做之间在选. 能举一个例子?
夏绪宏: 看场景, 你们现在什么都没有可以考虑各种开源方案, 我们也有一些自己特殊场景, 开源的东西跟内部服务做整合, 需要考虑一些整合成本和我们自己维护的成本. 很多时候开源项目会出于普适性, 会考虑的特性比较多一些, 代码也会相对复杂, 有些功能我们用不到, 我们要去做改造, 整体上不复杂;
授权标准也是自己做的, 基于服务标识, 服务器里面, 并没有考虑场景互联.
Q: 我们现在如果做微服务平台改造, 业务系统开发模式是不是有比较大的改变? 我们平台从开发模式到设计都会有所改变, 你们改造过了, 你们有哪些经验?
夏绪宏: 我们改造到现在也没改造完, 这个改造挺困难的. 你先定好一个大方向, 因为很多东西是涉及到沟通的问题, 推动的问题, 你先需要沟通好的方向, 达成一致, 到怎么改造, 机动性少一点, 或者你把大部分的功能实现好, 只需要做小小的迁移, 减少迁移的成本.
来源: https://yq.aliyun.com/articles/626433