1. 什么是分布式系统
分散在各个机器上的组件共同协作组成一个完整的系统。(不严格的说法)
2. 我们为什么需要分布式系统
高性能:分布式系统出现的主要动机是解决单机系统算力不够和存不下的问题,比如高并发、大数据这样的场景。
高可用:分布式系统分散在各个机器上,通过网络协作,更容易出问题,进而产生了一些高可用和监控的解决方案。
可扩展:启用分布式系统通常用在业务增长较快的场景中,通常面对或将来面对扩容的问题,产生了可扩展的需求。
3. 微服务
微服务是分布式系统的一个解决方案,微服务系统是分布式系统,但不是银弹。
4. 搭建一个分布式系统的考量
- 我们确实需要一个分布式系统吗?
适合的才是最好的,我们可能需要快速开发原型,验证需求抢占市场,在对开发需求和任务认识模糊的情况下一开始拆分系统可能比后面拆分更有挑战性,单体依旧有生命力。
- 单体架构列出的一些问题是否已经严重影响了我们的业务?
- 企业新的业务系统是否要满足快速迭代、弹性等需求?
- 团队内是否有 DevOps 氛围?
- 企业内是否有足够的动力、技术储备、和时间去接触新的技术?
- 先拆什么?
先拆无状态的部分,而后是有状态的部分。比如基于反向代理 的网站、文档等,这类应用简单且容易实现,有了一定的经验之后,第二步就可以开发有状态类型的新应用,有状态服务的是数据管理。
拆数据库,常见的是,能不拆先不拆,如果数据库遇到瓶颈,先扩容,先垂直(加硬件)后水平(无状态+多节点);然后考虑分库分表,先垂直后水平。
拆之前,可以考虑使用缓存、消息队列等方式,缓解数据库压力。
可以考虑的两种场景:cpu长期高负荷、宕机等现象;链接数不够用的,数据库处理超时。
- 基础设施-缓存
将热点数据放在缓存中,将缓存放在数据库前面,一可以加快数据访问速度,支持高性能;二可以帮助数据库分担压力;三可以通过分布式缓存共享数据,通过共享数据实现系统状态同步。
访问速度:一般进程内缓存=100倍网络缓存(Redis);网络缓存(Redis)=10倍数据库。(来自网友)
使用技巧:
可以将一些基础的数据放到缓存中,一个用户1k数据,10万用户才10M。社区中大石头哥的XCode数据访问层用的很深入。
使用Redis这些基础设施实现CQRS。
分布式锁。
Actor可以承担分布式缓存的角色。
注意事项:中小规模并发可以不在意,如果并发量很高,特别是缓存承担的是分担数据库压力的角色时,需要注意缓存穿透,缓存击穿,缓存雪崩的问题;缓存与数据库总会存在一定的不同步,根据场景确认是否可以接受,特别是在缓存集群且有消息队列存在的场景。
- 基础设施-消息队列
一般消息队列承担三个角色,进程间异步通信、削峰、解耦(基于异步通信)。
系统消息流转的脉络,常常要考虑的是保证消息不丢失,同时注意幂等性问题,一般以集群的方式保证高可用。
- 基础设施-定时任务
因为系统是分布式的,可扩容的,如果压力大了一个Web服务扩容为了三份,里面承载了三个定时器,比如发邮件,同一个任务会执行三次;
如果在分布式系统中只部署一个定时任务,需要考虑异常或宕机的场景,即要保证定时任务的高可用;
如果定时任务是多对多的场景,就是要求扩容的系统分别执行,可能要考虑时钟同步和定时任务时间粒度的问题。
- 基础设施-日志
分布式系统中常常考虑的是集中式日志,如果分布式系统只有一两个节点,出了问题可以单独登录系统查看各自的日志记录,如果是十几个,几十个,成千上万个微服务,这种手动登录查看解决的方式就出现弊端了。
目前常用的方式是分布式系统中将引发的异常集中发送到消息队列,落库到ElasticSearch集中管理。
- 基础设施-链路追踪
分布式系统中有许多的服务,如果A->B->C->D,如果有这样的调用链,如果出问题,响应慢或者出异常,需要知道在哪个环节出现了问题,此时需要链路追踪。
最实用的方式是:避免这样的调用出现,如果出现这样的调用,特别是交叉调用,设计可能出了问题。
常见的方式是使用开源的APM组件、借助K8S、在请求链路中传递一个公用的GUID。
注意事项:APM的引入会降低程序的性能,普遍情况可以承受这样的性能损失,可以关闭一些APM功能,特定的场景需要的时候再打开。
- 基础设施-单点登录
当一个Web通过负载均衡变成多个时,常见的Cookie-Session机制会失效,第一次访问时需要登录,访问了A机器,Session在A机器,第二次来访问了B机器,Session不在B上,但明明已经登录过了。
常见的方式一是用分布式缓存模拟Session,存储信息,达到集群内信息共享同步的目的。二是将用户登录信息存在客户端,如Token,用户每次来,不管访问了哪台机子,都带着该Token。
- 基础设施-进程间通信
系统部署在多台物理机、由不同进程协作时,需要进程间通信,常见的方式有同步和异步两种,同步常常是http或rpc,异步常常是走MQ。
http一般用于系统外通信,rpc用于系统内部。rpc除了常见的gRPC,actor在某种角度可以承担这样的角色。
注意事项:虽然MQ是异步的,但它的性能常常比较高,与缓存搭配,可以模拟同步效果,但这种搭配有点actor的味道了。
- 基础设施-分布式文件系统
中小型常见的方式是使用MongoDB的GridFS。单机系统中常用的思路是记录文件路径,文件夹下存文件,当单机存不下的时候,也可以通过路由表记录文件存储位置,或者路由算法实现,这种方式要考虑路由算法扩容问题。
- 基础设施-熔断降级
服务熔断和服务降级就可以视为解决服务雪崩的手段之一。
服务熔断:当下游的服务因为某种原因突然变得不可用或响应过慢,上游服务为了保证自己整体服务的可用性,不再继续调用目标服务,直接返回,快速释放资源。服务降级有很多种降级方式,如开关降级、限流降级、熔断降级。服务熔断属于降级方式的一种。
- 基础设施-分布式事务
一般使用常用的是最终一致性与补偿机制。主要是性能问题。 (不展开了)
- 基础设施-高并发
常见的方式扩展机器、加缓存、排队、分布式锁、减共享资源的粒度;actor模型。
- 基础设施-分布式锁
5. 技术之外
- 测试与集成测试成本
- 团队文化
- 项目进度
写到这引出的问题好像许多。其实这些问题可能我们不会一下子都遇到,大家往往都是小团队。
聊聊C#体系怎么落地思路
1. 社区中忽视的系统或组件
- Newlifex
- BeetleX
- Ray
2. 演进开发
不一下子达成这些目标,需要一个过程。特别是第一版,需要的是系统基础良好、功能实现、可以演进。
3. 落地思路
- 单体开发,但是保留分布式或微服务的形式
- 不管微服务,MiniService或常规分布式系统
- 借助微服务组件
- 借助容器技术
- 借助云基础设施
小团队,不上云,会很依赖运维团队,或Devops