分布式学习(一)

分布式学习

什么是分布式

分布式是这样定义的:

分布式系统是这样一种系统:位于联网计算机上的硬件或软件组件,仅通过消息传递进行通信并协调行动。

分布式,集群和微服务的区别:

分布式是一种组织形式,集群是物理形态,微服务是一种架构风格,一般来说微服务一定会解决分布式事务问题,而分布式不一定是微服务架构

分布式对比集群:

分布式:

1.一个业务分拆为多个子业务,部署在不同的服务器上
2.将不同的业务分布在不同的地方
3.分布式中的每一个节点,都可以作集群
4.从狭义角度上讲和集群差不多,但分布式的组织松散
5.分布式的每一个节点都完成不同的业务,一个节点垮了,这个业务就不可用了
6.以缩短单个任务的执行时间来提升效率

集群:

1.同一个业务,部署在多台服务器上
2.分担并发量,提升计算机的运算速度
3.几台服务器集中在一起,实现同一业务
4.集群并不一定就是分布式的
5.有一个组织性,一台服务器垮了,其他的服务器可以顶上来
6.通过单位时间内执行的任务数来提升效率
7.不同服务器部署同一套服务对外访问,一般配置Nginx的负载容器实现服务的负载均衡,静态资源缓存,Session共享
8.区别集群的方式是根据部署多台服务器业务是否相同

注:集群模式需要做好session共享,确保在不同服务器切换的过程中不会因为没有获取到session而中止退出服务。

分布式对比微服务:

分布式:

1.分布式是系统的部署方式
2.分布式系统中可横向扩展服务器,降低业务的耦合度
3.将不同业务部署在多台服务器或虚拟机上,通过RPC或Restful进行数据传输
4.分布式重在资源共享与加快计算机计算速度

微服务:

1.微服务是系统架构的设计方式
2.当业务板块A出了问题,不影响业务板块B正常工资(因为分解成了两个独立的服务,但服务A和服务B可以部署在同一台服务器上)
3.微服务的核心要素是服务划分的"微小"
4.纵向扩展单个业务,实现微服务
5.微服务重在解耦和,使每个模块都独立
6.微服务一定会去解决分布式事务问题

注意:

 分布式未必是微服务,比如将一个单体应用划分成三块部署,这符合分布式;但这三块依旧很大,不符合微服务。但分布式最后都会向微服务演进。

**问:** 分布式是否属于微服务?
答案是肯定的。微服务的意思也就是将模块拆分成一个独立的服务单元通过接口来实现数据的交互。

**问:** 什么是微服务架构
微服务的设计是为了不因为某个模块的升级和BUG影响现有的系统业务。微服务与分布式的细微差别是,微服务的应用不一定是分散在多个服务器上,他也可以是同一个服务器。

参考: 深入理解集群、分布式、微服务的概念、关系和区别

分布式解决了什么问题

当业务足够简单,访问量比较低的时候,使用单体应用是很合适的,管理后台,数据库,业务系统都部署在同一台服务器上易于开发,部署和排查问题

但当压力开始增加的时候,服务器会出现卡顿,延迟激增等问题,在最开始压力增加的时候,可以通过加机器的方式来解决,但这样一旦某个服务挂了,就会影响一整个业务,这在庞大的用户基数下是不可接受的.

在业务中,采用分布式系统的动机大多是:

性能撑不住:单机处理不了那么多请求,需要多台机器分担计算。

数据放不下:单机存储、索引、备份和恢复成本太高,需要分片或副本。

故障扛不住:单点故障影响太大,需要冗余、故障转移和降级。

分布式系统不断将业务进行拆分,降低耦合,从而提高稳定性

以电商平台为例:

现在有一个电商系统,订单,支付,用户,商品,库存都写在一个Spring Boot应用里,数据放在同一个MySQL中, 最开始没问题,但当访问量上来之后,压力会首先落在这几个地方:商品详情页查询量高,订单创建写入最高,库存扣减并发冲突多,支付链路不能随便失败.如果继续把所有业务塞在同一个应用里,任何一个模块变慢,都可能拖住整个系统.

想要解决问题,第一个方案就是横向扩容,上Nginx做负载均衡,加更多的服务器

但这样是不行的,终归是要对业务进行拆分

  • 商品服务负责商品信息和价格展示
  • 订单服务负责订单创建,订单状态流转
  • 库存服务负责库存扣减,库存回滚和库存流水
  • 支付服务负责对接第三方支付渠道
  • 消息队列负责把支付成果,库存变更,物流通知等事物异步传出去

拆分后的好处很直接.商品服务访问量大,可以单独扩容;支付服务对稳定性要求高,可以单独做限流,重试和熔断;库存服务并发冲突多,可以围绕库存扣减设计专门的数据结构和锁策略.

分布式系统解决了很多问题,但也引入了新的问题:

分布式系统会将原来单体应用中出现的问题放大: 订单服务调用库存服务扣库存,如果请求超时了,订单服务到底该不该重试?上一次扣库存是没发出去,还是已经扣成功但响应丢了?如果库存扣成功了,订单创建失败了,库存怎么补?支付成果消息重复投递.订单状态会不会被重复更新?

这些问题在单体应用中也会出现,分布式系统进一步放大了这些问题,需要重新设计不同业务之间的协作

分布式系统复杂度的来源有哪些

多节点协作造成的延迟和卡顿

由于分布式系统每个请求要有多个节点来完成,下单请求可能经过网关,订单服务,库存服务,支付服务,消息队列和数据库.

当链路拉长后,延迟和故障都会被放大.如某个节点线程池打满,数据库慢查询,网络抖动,都会造成用户"下单一直转圈圈"

节点并发执行,没有瞬时全局视图造成的矛盾

由于节点并发运行,每个节点只能直接看到自己的本地状态,以及已经收到的信息,不能在某个瞬间获取整个系统的真实状态.

所以可能出现一个节点已经拿到了最新配置,另一个节点还停留在旧版本;一个节点认为Leader还活着,另一个节点因为超时已经开始选举了.

网络通信造成的复杂度

节点之间是通过网络进行数据交换的.网络和本地内存不是一类东西,它不保证请求一定到达,也不保证响应按预期时间返回.

一次远程调用可能出现这些情况:

  • 请求尚未离开客户端 (丢包)
  • 请求已经到达服务端,但尚未执行完成; (卡顿)
  • 服务端已经执行成果,但响应没有到达客户端; (超时)
  • 客户端已经超时,服务端仍在继续执行; (超时后执行)
  • 服务端执行失败,但错误响应也没有成功返回. (失败无响应)

要解决这些问题就要提前准备好相应的应对手段: 超时, 重试, 幂等, 熔断, 降级

1. 超时

给一次请求设定一个最大的等待时间。一旦超过这个时间还没收到响应,就直接放弃等待,视为失败。

    作用:防止调用方被慢服务无限阻塞,避免线程/连接等资源耗尽。

    例子:设置 HTTP 请求的超时为 3 秒,3 秒没返回就报“请求超时”。

2. 重试

调用失败(如超时、网络闪断)后,重新发起相同的请求。

    作用:应对瞬时故障,提高成功率。

    关键前提:被调用的服务必须支持幂等,否则重试可能导致重复扣款、重复下单。

    常见策略:不能无脑重试,通常配合“指数退避”(如间隔 1s、2s、4s 重试),并限制最大重试次数。

3. 幂等

同一个操作,无论执行一次还是多次,产生的效果相同,不会因为重复执行而产生额外副作用。

    作用:这是安全实现重试和消息可靠投递的基础。

    实现方式:

        唯一键:如订单号作为数据库唯一索引,重复插入会报错,从而防止重复创建。

        Token 机制:先拿操作凭证,再带凭证提交,用后即焚。

    例子:支付接口收到完全相同的两笔扣款请求,只有第一次成功,第二次直接返回成功但不再次扣款。

4. 熔断

当某个服务连续失败达到预设阈值(如连续 5 次超时),就暂时“跳闸”,不再向该服务发真实请求,而是直接快速失败。

    作用:保护调用方资源,也给故障服务恢复的时间。避免级联故障(雪崩)。

    断路器状态:

        关闭:正常调用。

        打开:直接拒绝请求,通常配合降级。

        半开:过段时间后,尝试放行少量请求,若成功则关闭断路器,若失败则继续打开。

    比喻:像电闸,电路短路过载就自动断开,避免起火。

5. 降级

当系统压力过大或某个依赖服务不可用时,暂时关闭非核心功能,优先保证核心流程能基本运行。

    作用:丢卒保车,有损服务好过完全不可用。

    触发场景:熔断打开、高峰期资源紧张、依赖方故障。

    常见做法:

        返回默认值(如推荐列表不加载,显示固定广告)。

        返回缓存/托底数据。

        自动回复提示,如“当前人数过多,请稍后再试”。

它们如何协同工作?(一个请求的完整过程)

    调用服务,设置超时时间。

    超时或网络故障后,触发重试。

    因为被调接口支持幂等,重试不会产生重复数据。

    若多次重试后依然持续失败,熔断器跳闸,暂时不再发出真实请求。

    熔断期间,调用方执行降级逻辑,返回一个备选方案,保证核心体验不中断。

局部故障

单机系统里,进程挂了,问题边界相对清晰.分布式系统里经常是半边好, 半边坏: 一部分节点正常, 一部分节点异常; 一部分请求成功,一部分请求失败;A服务访问不了B服务, 但C服务还能访问B服务.

一个节点没响应,也不一定就是宕机了. 网络抖动, GC暂停,线程池打满,磁盘 I/O 卡住,都可能让它短时间"像死了一样".系统如果只靠"有没有响应"判断故障,很容易误判.

数据复制和数据分片

数据规模和可用性上来后,系统很容易走到分片和复制.

分片是把不同数据分散到不同节点,常见规则包括用户ID, 订单ID, 地域, 哈希值.分片之后,单个节点压力小了,跨分片查询,跨分片事物,分片扩容会变麻烦.

复制是把同一份数据保存多份,比如MySQL 主从复制, Redis 主从复制, Kafka 分区副本, ZooKeeper 多节点副本.有了副本,节点故障时更容易继续服务,读请求也可能分摊到多个副本上.代价是副本同步有延迟: 主节点写成功后,从节点可能还没追上;用户刚写完数据,下一次读请求如果落到旧副本,就可能读到旧值.

没有完美同步的全局时钟

每台机器都有自己的物理时钟,但时钟会有偏差和漂移。NTP、GPS 这类时间同步机制可以缩小误差,不能保证所有节点在任意时刻都有完全一致的时间视图。

分布式系统很少只靠墙上时钟判断事件先后。表达因果关系时,可以使用 Lamport Clock、Vector Clock 等逻辑时钟;做复制、选举和状态变更时,也常用 term、epoch、版本号或单调递增序列。

物理时间依然有用,日志、超时、租约、缓存过期都离不开它。只是依赖物理时间时,要知道自己能接受多大的时钟误差和漂移。逻辑时钟解决事件顺序问题,不能直接替代“锁多久后过期”这类物理时间需求。

常见的分布式系统有哪些?

分布式系统不是某一种中间件,而是一类系统形态。常见类型有这些。

类型解决的问题常见例子
分布式协调系统选主、配置管理、服务发现、分布式锁ZooKeeper、etcd、Consul
分布式数据库数据分片、副本复制、水平扩展TiDB、CockroachDB、Cassandra、HBase
分布式缓存多节点缓存、热点数据加速、缓存容量扩展Redis Cluster、Memcached 集群
分布式消息队列异步解耦、削峰填谷、事件驱动Kafka、RocketMQ、Pulsar
分布式文件/对象存储大文件存储、多副本、高吞吐读写HDFS、Ceph、MinIO
RPC 框架接口定义、序列化、跨服务请求响应gRPC、Apache Thrift
服务治理体系注册发现、负载均衡、流量管理、熔断、配置Dubbo、Spring Cloud

这些系统解决的问题不同,但经常一起出现在一个业务架构里。一个订单系统可能用 Redis 做缓存,用 RocketMQ 传递订单事件,用 ZooKeeper 或 Nacos 做注册发现,用 MySQL 分库分表存订单,再用链路追踪系统排查一次请求经过了哪些服务。

学分布式系统时,不要只盯着某个中间件背参数。要问它放在系统里解决了什么问题,又把哪些复杂度留给了业务方。

分布式系统可能在哪些部分出现问题

分布式的优势是解耦,缺点也因为组织松散导致错误排查变得困难,任何一个环节出了问题都可能会造成问题

比如订单服务调用库存服务扣库存,客户端设置了2秒超时,2秒后订单服务没收到响应,它有几种选择:

  • 直接认为扣库存失败,取消订单;
  • 重新调用一次库存服务;
  • 查询库存扣减流水,确认上一次请求是否成功
  • 先把订单置为处理中,后续通过消息或定时任务补偿

要解决这些问题就要预先在设计时做好准备

首先是保证幂等,只要存在超时和重试,同一个业务请求就可能被处理多次.服务端必须能识别"这是同一次业务操作",不能因为客户端重试就重复扣款,重复扣库存,重复发券.对有副作用的远程写操作,要设计业务幂等号,结果查询,有限重试,以及指数退避和随机抖动,避免下游故障时被重试流量继续压垮.

同时要保证数据一致性.单体应用里,一个数据库事务可以同时更新订单表和库存表;拆成订单服务和库存服务之后,订单库和库存库不在同一个事务里.想让他们要么都成功,要么都失败,就需要分布式事务,事务消息,TCC,Saga,本地消息表等方案.

很多跨服务业务会接受短时间状态不一致,再用事务消息、重试、补偿和对账,让订单、库存、支付等状态最终满足业务约束。工程里也常把这种方案叫“最终一致性”。它和副本一致性模型里的 eventual consistency 不是同一个语境:后者强调不再发生新写入时,多个数据副本最终收敛;前者更偏向跨服务业务流程的异步协调。

排查问题也会变慢。一次请求经过 6 个服务,任何一个服务日志不规范、链路追踪缺失、错误码设计混乱,定位都会很费劲。生产环境里缺少观测能力时,很难判断请求卡在哪个节点,错误最早从哪里冒出来。

参考: 分布式系统详解:核心概念、架构演进、典型特征与学习路线

Licensed under CC BY-NC-SA 4.0
Build by Oight
使用 Hugo 构建
主题 StackJimmy 设计