架构的终极目标是降本增效。是各方面折中的结果。
本文以互联网软件架构的演进之路为主线,结合案例分析每个阶段架构的适用场景、技术栈和优缺点。
0、架构的本质
架构是对业务场景抽象后,对各方面因素折中后的结果,这些因素包括:
- 业务复杂度
- 数据规模大小
- 团队技术栈/研发能力
- 时间成本
- 运维成本
只有对这些因素都妥善折中后,才能达到降本增效的目的。
场景驱动架构不断演进,脱离场景谈架构都是耍流氓。
1、架构演进概述
从单体架构演进到微服务架构的精髓用一个字概括:拆。
2、单体架构设计与实践
客户端APP发送请求到单体服务,服务端接收到请求后从DB读取数据,并进行业务逻辑处理,最后对返回结果进行封装,返回结果给APP。
- 案例 (如下比较简单的案例可以用单体来实现)
如用户个人主页,获取用户个人详细信息等简单场景。 - 实践
MVC框架套用在单体上:
--M:数据库读取数据
--V:封装结果返回
--C:业务逻辑处理
2.1 单体架构优点
只有一个进程,简单。
注意单体架构实际也必须扩展,即使只有一个QPS,生产环境下也不能只部署一个单体实例。
上图中,生产环境中Nginx一般也至少两台,前面还有一组主备的LVS,LVS的VIP直接和app交互。
上面的单体WAR必须是无状态的,才是可扩展的,因为这样它的冗余备份才是完全对等的。
2.2 单体架构适用场景:
- 业务场景简单、功能不负责、人员少
如O2O - 创业公司初期
- 性能要求极其苛刻
性能指吞吐量和RTT(响应延迟)
金融案例:
股票、虚拟币等高频交易的背景下,量化交易软件要及时毫秒级捕捉价格变化。所以service和DB最好在一台机器上(单体),请求端和服务端也尽量在同一机房。
思考下公司哪些业务适用于单体架构。
单体架构缺点
单体架构最大的一个痛点就是耦合度太高,业务壮大后都在一个单体内肯定不行。
拆分
由此,单体架构需要破局演进。很自然想到,把上图单体服务原本的一个进程安装业务种类垂直拆成三个服务进程。当然可以按功能水平拆分,并且数据库也需要拆。
总结:
1、 数据库存储量大 、请求量大,拆分主要两种方式:
- 垂直拆分(分库)
- 水平拆分(分表)
如uid取模
2、 架构拆分
其实架构的拆分也和DB的拆分的思路是类似的:
- 垂直分拆(业务维度)
- 水平分拆(功能维度)
3、面向服务架构设计与实践
SOA是由单体架构的垂直方向拆分演变而来。
但是生产中的SOA架构和上图这样的理想模型是有一点差别的。
以58为例:
每个单体有自己的DB,服务间通信有soap(也可以json)。
3.1 SOA架构缺点
- 仅垂直方向拆分
- 每个服务还是单体服务
- 有一些公共服务并没有抽象出来
- 服务间通信消耗较大
思考:公司里哪些业务适用于SOA/ESB
4、水平分层架构设计与实践
按请求功能进行聚类。
水平风向拆分,如电商案例(按请求链路拆分)
APP到GW 传输部分用的http协议,数据部分用json。
网关到业务逻辑层一般用TCP(也可以用http2.0,支持长连接,机房内部可以用),数据部分用pb/msgpack,可以跨语言的。
数据访问层到DB是SQL2003.
业务逻辑层和数据访问层可以考虑合并成一个进程,从而降低分层后交互的延迟。又能节省成本。
接下来深入介绍每一层的功能。
网关层功能
纯数据,前后端分离。
- 功能一: 请求鉴权
发布商品,登录鉴权。看此用户是否带着session,以及session是否合法。 - 功能二: 数据完整性检查
数据包定长header + 变长body。
主要是检查header各个字段有没有填写。
header: uid + cmd + session id + body length
变长body:{k1:v1, k2:v2}(网关是不关心这部分,因为属于业务语义部分。)
- 功能三: 协议转换
JSON -> HashMap(String,Object) -> pb(proto buffer)
在golang中可以用grpc来解决? - 功能四: 路由转发
根据CMD转发到不同业务逻辑层 - 功能五:服务治理
限流、降级、熔断
网关层框架对比
业务逻辑层功能
- 功能一: 业务逻辑判断
- 案例一: 微信黑明单检查
- 案例二: 微信好友删除
业务编排就是在这层做。
数据访问层功能
- 功能一: CRUD
业务增删改查接口(批量) - 功能二: ORM
MyBatis3 - 功能三:Sharding(分库分表)(可选)
Sharding-JDBC (sharding sphere)
这部分最难,最好不分库分表。所以mysql-> mongo - >TiDB - 功能四: 屏蔽底层存储差异性
MySQL/MongoDB, Redis, TiDB
4.1 水平分层架构服务和协议
思考如何把同步变异步。加个MQ就可以。
所以上面如果想异步,MQ要加在GW与logic层之间。
请求异步
- 读请求:不可以异步(脏读)
- 写请求:
- AP(社交场景)可以:如发朋友圈失败,gw可以把数据发给app缓存,但重试三次后都不能持久化,则发给用户提示发送失败。
- CP(发红包场景)不可以:
4.2 水平分层架构缺点
-
粒度太粗
如业务逻辑层会部署多个实例,但是公司所有的服务逻辑都在一起,粒度太粗。
4.3 水平层次划分案例
后来路由层干掉了,存到Redis
百度空间案例:
5、微服务架构与实践
既做水平拆分,又做垂直拆分就是微服务。比SOA粒度更小,且去掉了ESB。
没必要按DDD来做,落地难度太大。
微服务中垂直拆分不容易。比如,用户功能中有用户注册、用户登录、用户查询。读(用户登录)会影响写(用户注册),因此要考虑读写分离,所以这样就是按API的粒度来拆分,其实是否要拆主要就看读写之间影响是不是很大。另如下单和查询。
再如user,user主库用于写,从库用于读。从库没有数据,再去主库查。但是insert可以,update不行,解决方案是主从强同步,主进行切片。根据uid就可以知道落到哪个库。或者采用集群的方式。
网关层相当于SaaS层。
一般没必要按API的粒度去拆分。
以上不同层其实可以用不同语言来写。
微服务问题1:服务设计和服务治理强耦合。
微服务问题2: 基础组件升级困难
微服务问题3:多语言之间通信问题
解决方案:微服务2.0,即service mesh。就是把服务设计(业务)和服务治理(通信)解耦。
把通信组件独立为一个进程,叫sidecar,和应用程序在同一台机器。
6. Service Mesh 架构设计与实践
- 最早由开发Linkerd的Buoyant公司提出,并在内部使用。
- 2016年9月第一次公开使用
- 2017年初,Service Mesh进入国内技术社区视野
服务网格化的目的:把和业务无关的服务治理功能剥离出来。
linkerd后来被istio被替代了。
Service Mesh 优势
- SM独立进程,独立升级
- 业务团队可以更专注于业务逻辑本身
- 一套基础设施支持多语言开发
- 业务团队和基础设施团队物理解耦
- 服务治理和服务本身的物理剥离
重点注意,数据访问流向。
上图数据访问层访问DB画的不对,应直接访问DB/Cache。
7. 大中台架构设计与实践
同层调用不允许,可以直接跨层向下调用,为了避免循环调用。
业务数据和中台数据如何存储
上图是mysql要这么做,如果是MongoDB不需要扩展表,只要一张表,因为不需要schema。
8. 云原生架构设计与实践
重点是部署。
- 云原生
最初由Pivotal公司2013年提出,该公司先后开源了云原生的java开发框架Spring Boot和Spring Cloud。
2015年Google成立了CNCF(cloud native computing foundation),使得云原生受到了越来越多的关注。而云原生真正在生产中普及,应该说归功于docker和k8s的发展和成熟。 - 云原生架构
指专门为云平台部署和运行而设计的架构 - 云原生架构本质
- 按需使用和弹性伸缩
- 无状态化设计(stateless)
- 自动化部署和管理
- CICD
9. 架构演进
公共逻辑层即技术中台。
架构设计思考:
思考:
- Dubbo和grpc是一个层面的吗?
- 同步和阻塞IO有啥区别,异步和非阻塞IO
- OLAP和OLTP有何区别。前者搞数仓。
- 如何理解回调是进程内的小异步。
- schema什么意思?行扩展。 shema不固定只能用列扩展?
- MongoDB这种free shema怎么存都行。但是mysql建议other字段存pb/json。