Skip to content

分布式系统挑战

About 2341 wordsAbout 8 min

架构新书

2026-06-14

当我们还是小白,学习编程第一课时候,总是会遇到一个叫Hello World的程序,无论使用何种语言实现,它容易编译,容易编译和且执行必定成功,让我们生心愉快,对未来信心满满。如下Java代码,方法main调用方法b的时候,我们毫不怀疑,肯定能调用到方法b,且只会调用一次。方法b也肯定会返回一个调用结果.

public static void main(String[] paras){
    b(1)
}

protected static boolean b(int data){
  System.out.println("hello "+data)
  return true
}

编程的第一课,虽然简单,但掩盖了走入职业生涯遇后到分布式系统带来的容易被忽略的俩个问题

  • 可观测性问题:b方法输出字符串到控制台我们能容易得看到。但实际分布式系统运行的可观测非常复杂,不仅仅是期望从控制台得到一个“Hello World日志”。程序员需要建立对系统可观测性新认知,企业需要建立一套可观测性系统对分布式系统进行观测,个人和企业都不应该期望仅仅查看控制台观日志能观测整个系统,关于“Hello World和可观测性”,将在下一节介绍
  • 分布式问题:b方法如果是改成分布式调用,会带来各种问题,影响系统的高可用,高性能等质量属性。如下代码把调用b方法改成分布式调用,这里假设Remote类是一个调用远程代理类,可能是一个EJB 客户端,Dubbo客户端,或者Feigh客户端。
@Autowired
Remote remote;
public static void main(String[] paras){
    remote.b(1)
}

在分布式场景下,main方法调用remote.b,会出现各种结果

  • remote.b(),成功返回返回结果,这是最理想结果。
  • remote.b(),网络故障,调用立刻失败
  • remote.b(),网络正常,远程服务B不存在,调用立刻失败
  • remote.b(),网络拥塞,调用超时,导致main方法阻塞
  • remote.b(),hello world成功输出到控制条,并返回结果,但这是调用超过设定时限,main方法不知道是否调用成功
  • remote.b(),黑客截获调用参数,修改参数重调用或者模拟一个返回结果。
  • main方法所在主机自身问题,如垃圾回收导致CPU繁忙,导致b方法调用看起来超时的幻觉
  • remote 所在主机自身问题,如海量访问导致CPU繁忙,导致b方法调用来超时
  • remote 系统正在重启,由于JVM预热初始化访问性能较慢,main尝试调用超时导致不断无谓的重试。

虚假的分布式是“像单体应用那样,纳秒级响应且毫无错误执行,并有确定返回的的调用”。真实的分布式调用,不会出现这种理想情况。下图的一个简单的分布式调用模型,会给我们带来哪些问题以及它是如何影响系统质量的?

下表总结了分布式调用,遇到不可靠的网络或者不可靠服务节点(软件架构师,总是悲观的认为基础设施或者其他系统不可靠)时候遇到的问题以及解决办法

真实的分布式原因客户端现象解决办法
网络不可靠有人拔掉网线;运营商故障;运营商误设黑名单,云厂商故障调用超时服务A超时检测,降级,以退避指数来重试节点等
网络高延迟运营商和云厂商故障延迟或者超时服务A超时检测,降级,以退避指数来重试节点等。
网络无穷延迟网络故障,节点假死一直未响应服务A超时检测,降级,以退避指数来重试节点等
带宽有限带宽太贵了延迟或者超时服务A性能优化,包括压缩,拆包发送,缓存,以及服务A实现降级处理。
网络不安全黑客网络数据被截取或者篡改通道加密SSL或者消息加密&数字签名
服务B节点不会一直存在节点故障,节点维护调用超时服务A重试其他节点,服务注册和发现
服务B节点不会快速响应节点负荷高调用超时,阻塞服务A快速失败,降级,重试其他节点,服务注册和发现
时序不保证异步处理先后发出的消息,可能最后才达到目的地。消息源头带有带序号,接收方检测序号。放弃晚到的消息
重复发送节点故障导致服务A重发消息服务B收到重复的消息消息源头带有带序号,接收方检测序号
服务A和B不可靠时钟不同机器时钟不一致通过时钟描述系统事件先后发生不一定准确按照时间自增时候考虑时钟回拨;逻辑时钟(logic clock)相关技术:如使用ID生成器(比如开源的id生成服务器,Mesh网络上的设备统一使用Mesh网关的顺序号)

上表可以看到,分布式调用产生的问题,以及相应的的解决办法,会对整个系统质量产生一定影响。提高其质量的战术方法将在第三章描述,这些战术的Java实现细节将在第三章后剩余部分描述。

无需真的拨掉网线验证分布式调用。可以通过iptable 模拟网络不可靠;通过openssl命令模拟主机CPU繁忙。使用dd命令模拟主机内存受限环境。向虚拟机植入agent模拟JVM各种故障等,这部分内容参考第三章中的《可用性捣乱》章节

上表分布式问题影响了系统的质量,同时导致了分布式系统产生了如下困难。

困难描述解决办法
传输对象成本序列化,反序列化性能对系统影响较大;网络传输耗时,内网也有10毫秒左右延迟。轻量级协议设计;高效序列化工具选择。TCP改成UDP。
分布式锁在分布式系统下对共享资源进行锁操作。不如服务B的多个实例可能同时操作某个共享资源借助数据库的行锁或者Zookeeper等实现分布锁功能。
部分失效相比于单体,要么成功,要么失败,分布式系统容易产生部分成功。比如服务A除了调用服务B外,还会调用服务C。当B调用成功后调用C出现上面分布式问题时候,服务A的调用属于部分成功。服务端支持幂等;分布式事务;事务最终一致方案;事后人工补救;一致性共识
一致性共识是一种在分布式系统中实现数据一致性的方法,它规定了在多个副本之间如何更新和读取数据,以确保数据的一致性。一致性共识通常采用一种称为“共识算法”的技术手段来实现。Paxos、Raft等算法,这些算法通过通信和协商过程,确保所有副本都达成一致的结果,以实现数据的一致性。业务系统不必实现这些算法,使用支持共识算法的中间件,比如Zookeeper。
故障检测是网络问题,还是节点问题,还是节点依赖的节点问题。 是节点所在机器问题,还是JVM问题,还是业务自身问题?可观测平台建设
可观测单体的可观测性技术,比如使用日志,无法满足分布式系统可观测性可观测平台建设,实现链路跟踪,操作系统和JVM,以及运行的系统的运行指标可查询。

分布式带来的系统问题,在系统建设初期,为了实现业务迭代可以暂时忽略。但随着系统用户增加,访问量增加,节点的增加以及频繁部署节点,会放大上述分布式带来的问题,导致开发人员和运维人员疲于解决。个人经验,大型互联网和物联网公司员工,即使有成熟的技术架构和可观测性平台,也至少40%的时间用来解决本章节列出的分布式问题带给业务系统带来的问题。本书后面内容深入阐述这些问题,以及解决办法(提供Java实现)

由于分布式理论博大精深,关于本章节内容,如果需要深入了解,可以参考

  1. Designing Data-Intensive Applications》第8章
  2. 本书《高可用系统4大原则之一:端到端原则》,《高可用系统4大原则之一:无状态服务vs有状态服务》
  3. 《分布式系统概念与设计》

知行合一