你是做分布式系统的吗?微服务、WebAPI、SOA、Web服务器、应用程序服务器、数据库服务器、缓存服务器、负载平衡器——如果这些描述了系统设计中的组件,那么答案是肯定的。分布式系统由许多协调以实现共同目标的计算机组成。20多年前,PeterDeutsch和JamesGosling定义了分布式计算的8个谬误。这些是许多开发人员对分布式系统所做的错误假设。从长远来看,这些通常会被证明是错误的,从而导致难以修复的错误。八个谬论是:网络是可靠的。延迟为零。带宽是无限的。网络很安全。拓扑不会改变。有管理员。运费为零。网络是同构的。让我们看看每个谬误,讨论问题和可能的解决方案。1、网络可靠性问题会导致网络调用失败。今天的大多数系统都调用其他系统。您是否正在与第三方系统(支付网关、会计系统、CRM)集成?你在做网络服务电话吗?如果调用失败会怎样?如果您正在查询数据,您可以进行简单的重试。但是如果你发送命令会发生什么?让我们举一个简单的例子:varcreditCardProcessor=newCreditCardPaymentService();creditCardProcessor.Charge(chargeRequest);如果我们收到HTTP超时异常会怎样?如果服务器没有处理请求,那么我们可以重试。但是,如果它确实处??理了请求,我们需要确保不会向客户重复收费。您可以通过使您的服务器幂等来实现这一点。这意味着,如果您使用相同的收费请求拨打10次电话,客户只会被收费一次。如果你没有正确处理这些错误,你的系统就是未定义的。处理所有这些情况可能非常复杂。解决方案那么在网络上调用失败怎么办呢?好吧,我们可以自动重试。排队系统非常擅长这一点。他们通常使用一种称为存储转发的模式。他们在将消息转发给收件人之前将其存储在本地。如果收件人离线,排队系统将重试发送消息。MSMQ是此类排队系统的一个示例。但这种变化将对您的系统设计产生重大影响。您正在从请求/响应模型转变为触发后忘记。由于您不再等待响应,因此您需要更改系统中的用户行程。您不能只用队列发送替换每个Web服务调用。结论您可能会争辩说网络现在更可靠了——事实确实如此。但是事情发生了。硬件和软件可能会出现故障-电源、路由器、失败的更新或补丁、微弱的无线信号、网络拥塞、啮齿动物或鲨鱼。是的,鲨鱼:在发生一系列鲨鱼咬伤事件后,谷歌正在使用Kevlar加强其海底数据线。还有人的因素。可以启动DDOS攻击以及破坏物理设备。这是否意味着您需要删除当前的技术堆栈并使用消息系统?并不真地!您需要权衡失败的风险与您需要进行的投资。您可以通过投资基础架构和软件来最大程度地减少失败的可能性。在许多情况下,失败是一种选择。但是在设计分布式系统时,确实需要考虑故障。2.延迟为零问题通过互联网拨打电话不是即时的。内存调用和互联网调用之间存在七个数量级的差异。您的应用程序应该是网络感知的。这意味着您应该清楚地将本地调用与远程调用区分开来。让我们看一个我在代码库中看到的例子:varviewModel=newViewModel();var文档=新文档集合();foreach(vardocumentindocuments){varsnapshot=document.GetSnapshot();视图模型。Add(snapshot);}无需进一步检查,这看起来不错。但是,有两个远程调用。第2行调用以获取文档摘要列表。在第5行,还有另一个调用检索有关每个文档的更多信息。这是一个经典的Selectn+1问题。考虑到网络延迟,您应该在一次调用中返回所有必需的数据。一般的建议是本地调用可以细粒度,但远程调用应该更粗粒度。这就是为什么分布式对象和网络透明的想法已经死了。但是即使每个人都同意分布式对象不是一个好主意,有些人仍然认为延迟加载总是一个好主意:varemployee=EmployeeRepository.GetBy(someCriteria)foreach(varpeerinmanager.Employees;){//dosomething}你不希望属性getter进行网络调用。然而,每一个。在上面的代码中调用实际上可以触发对数据库的访问。该解决方案带回您可能需要的所有数据如果您进行远程调用,请确保恢复您可能需要的所有数据。网络交流不宜唠叨。将DataCloser移至客户端的另一种可能解决方案是将数据移至更靠近客户端的位置。如果您使用云,请根据客户的位置仔细选择可用区。缓存还可以帮助最小化网络调用的数量。对于静态内容,内容分发网络(CDN)是另一个不错的选择。反转数据流移除远程调用的另一种选择是反转数据流。我们可以使用Pub/Sub并在本地存储数据,而不是查询其他服务。这样,我们就可以在需要时获取数据。诚然,这会带来一些复杂性,但它可以成为工具箱中的一个很好的工具。结论虽然在LAN上延迟可能不是问题,但是当您移动到??WAN或Internet时,您会注意到延迟。这就是为什么将网络调用与内存中的调用明确分开很重要。在采用微服务架构模式时,您应该牢记这一点。您不应该只用远程调用替换本地调用。这可以将您的系统变成一个分布式的大泥球。3.带宽无限制。带宽有限。带宽是网络在一段时间内发送数据的能力。到目前为止,我还没有发现它是一个问题,但我明白为什么在某些情况下它可能是一个问题。虽然带宽随着时间的推移而提高,但我们发送的数据量也在增加。与通过网络传递简单DTO的应用程序相比,视频流或VoIP需要更多的带宽。带宽对于移动应用程序来说更为重要,因此开发人员在设计后端API时需要考虑它。错误地使用ORM也会造成伤害。我见过开发人员在查询中过早调用.ToList()的示例,从而将整个表加载到内存中。解决方案域驱动设计模式那么我们如何确保不会引入太多数据呢?领域驱动设计模式可以提供帮助:首先,您不应该争取单一的企业范围的领域模型。您应该将您的域划分为有界上下文。为避免在有界上下文中使用大型复杂对象图,您可以使用聚合模式。聚合确保一致性并定义事务边界。命令和查询职责隔离我们有时会加载复杂的对象图,因为我们需要在屏幕上显示它的一部分。如果我们在很多地方这样做,我们最终会得到一个大而复杂的模型,该模型对于写入和读取都不是最优的。另一种方法是使用命令和查询责任分离-CQRS。这意味着将域模型分为两部分:写入模式将确保真实数据保持不变和一致。由于写模型不关心视图问题,它可以保持小而集中。读取模型针对视图问题进行了优化,因此我们可以获取特定视图(例如,我们应用程序的屏幕)所需的所有数据。结论第二个谬误(延迟不为0)和第三个谬误(带宽无限大)之间有一段距离,您应该传输更多数据以最小化网络往返。您应该传输较少的数据以尽量减少带宽使用。您需要平衡这两种力量并找到正确的数据量以通过网络发送。虽然您可能不会经常遇到带宽限制,但重要的是要考虑传输的数据。更少的数据更容易理解。更少的数据意味着更少的耦合。因此,只传输您可能需要的数据。4.网络是安全问题网络不安全。这是一个比其他假设获得更多媒体报道的假设。您的系统的安全取决于最薄弱的环节。坏消息是分布式系统中有很多链接。您正在使用HTTPS,除非与不支持它的第三方遗留系统通信。您正在查看自己的代码,寻找安全问题,但您使用的是具有潜在风险的开源库。OpenSSL漏洞允许人们窃取受SSL/TLS保护的数据。ApacheStruts中的错误可能允许攻击者在服务器上执行代码。即使您要防御所有这些,仍然存在人为因素。恶意的DBA可能会放错数据库备份。今天的攻击者拥有强大的计算能力和耐心。所以问题不是他们是否会攻击你的系统,而是什么时候。深度解决方案防御您应该使用分层方法来保护您的系统。您需要在网络、基础架构和应用程序级别进行不同的安全检查。安全心态在设计您的系统时要考虑到安全性。在过去5年中,前10大漏洞列表没有太大变化。您应该遵循安全软件设计的最佳实践,并检查您的代码是否存在常见的安全漏洞。您应该定期搜索第三方库以查找新漏洞。常见漏洞和风险的列表可以提供帮助。威胁建模威胁建模是一种识别系统中可能存在的安全威胁的系统方法。首先识别系统中的所有资产(数据库中的用户数据、文件等)以及如何访问它们。之后,您可以识别可能的攻击并开始执行它们。我建议阅读AdvancedAPISecurity的第2章,以便更好地概述威胁建模。结论唯一安全的系统是断电、未连接到任何网络(最好是在有形模块中)的系统。这是一个多么有用的系统!事实是,安全既困难又昂贵。分布式系统中有许多组件和链接,每一个组件和链接都是恶意用户的可能目标。组织需要在攻击的风险和可能性与实施预防机制的成本之间取得平衡。攻击者手上有很大的耐心和计算能力。我们可以通过使用威胁建模来防止某些类型的攻击,但我们不能保证100%的安全。因此,最好让企业明确这一点,共同决定在安全方面投资多少,并制定安全漏洞何时会发生的计划。5.拓扑没有变化。网络拓扑不断变化。网络拓扑总是在变化。有时它会因意想不到的原因而改变——当您的应用程序服务器出现故障并需要更换时。很多时候这是故意的——在新服务器上添加新进程。随着云和容器的激增,这一点在今天更加明显。弹性扩展——根据工作负载添加或删除服务器的能力——需要一定程度的网络灵活性。解决方案摘要网络的物理结构您需要做的第一件事是抽象网络的物理结构。有几种方法可以做到这一点:停止对IP进行硬编码——您应该更喜欢使用主机名。通过使用URI,我们依靠DNS将主机名解析为IP。当DNS不够用时(例如,当您需要映射IP和端口时),使用发现服务。服务总线框架还可以提供位置透明性。价值,不重要通过将您的服务器视为无关紧要的而不是必不可少的,您可以确保没有服务器是不可替代的。这点智慧可以帮助您树立正确的心态:任何服务器都可能发生故障(并因此改变拓扑结构),因此您应该尽可能地使其自动化。测试最后一条建议是测试你的假设。停止服务或关闭服务器,看看你的系统是否还在运行。Netflix的ChaosMonkey等工具通过随机关闭生产中的虚拟机或容器来实现这一点。通过造成痛苦,您更有动力构建一个更具弹性的系统来处理拓扑变化。结论十年前,大多数拓扑并不经常变化。但是当它发生时,它可能会在生产中发生并引入一些停机时间。今天,随着云和容器的激增,很难忽视这种谬误。你需要准备好失败和测试。不要等待它在生产中发生!6.有一个知道一切都不存在的管理员问题。好吧,这看起来很明显。当然,没有人知道一切。这是个问题吗?只要应用程序运行顺利,它就不会。但是当出现问题时,您需要修复它。因为很多人接触了该应用程序,所以可能没有知道如何解决问题的人。有很多事情都可能出错。一个例子是配置。今天的应用程序将配置存储在多个存储中:配置文件、环境变量、数据库、命令行参数。没有人知道每个可能的配置值的影响是什么。另一件可能出错的事情是系统升级。分布式应用程序有许多活动部件,您需要确保它们同步。例如,您需要确保当前版本的代码适用于当前版本的数据库。如今,人们关注DevOps和持续交付。但支持零停机部署并不容易。但是,至少这些事情都在你的控制之下。许多应用程序与第三方系统交互。这意味着如果他们失败了,您将无能为力。所以即使你的系统有管理员,你仍然无法控制第三方系统。解决方案每个人都对发布过程负责。这意味着从一开始就让运维人员或系统管理员参与进来。理想情况下,他们将成为团队的一员。尽早让您的系统管理员了解您的进度可以帮助您发现限制因素。例如,生产环境可能具有与开发环境不同的配置、安全限制、防火墙规则或可用端口。日志记录和监控系统管理员应该拥有正确的工具来报告错误和管理问题。您应该从一开始就考虑监控。分布式系统应该有一个集中的日志。访问十个不同服务器上的日志来调查问题是不可接受的方法。解耦在系统升级期间,您应该力争将停机时间降至最低。这意味着您应该能够独立升级系统的不同部分。通过使组件向后兼容,您可以在不同时间更新服务器和客户端。通过在组件之间放置队列,可以暂时解耦它们。这意味着,例如,即使后端关闭,Web服务器仍然可以接受请求。隔离第三方依赖项您应该以不同于您拥有的组件的方式对待您无法控制的系统。这意味着让您的系统对第三方故障更具弹性。您可以通过引入抽象层来减少外部依赖的影响。这意味着当第三方系统出现故障时,您将没有多少地方可以查找错误。结论要解决这个谬误,您需要使系统易于管理。DevOps、日志记录和监控可以提供帮助。您还需要考虑系统的升级过程。如果升级需要数小时的停机时间,您就无法部署每个冲刺。没有一个管理员,所以每个人都应该对发布过程负责。7、运输成本为零。运输成本不是零。这个谬误与第二个谬误有关,即零延迟。通过网络传输内容需要时间和资源成本。如果第二个谬误讨论的是时间方面,那么谬误#7则涉及资源消耗。这种谬误有两个明显的方面:网络基础设施的成本网络基础设施是有成本的。服务器、SAN、网络交换机、负载平衡器和负责这些设备的人员——所有这些都需要花钱。如果您的系统部署在本地,则您需要预先支付此价格。如果您使用的是云,您只需为使用的部分付费,但您仍然需要付费。序列化/反序列化的成本这个谬误的第二个方面是在传输层和应用层之间传输数据的成本。序列化和反序列化会消耗CPU时间,因此要花钱。如果您的应用程序是本地的,如果您不主动监控资源消耗,则可以隐藏此成本。但是,如果您的应用程序部署在云端,那么这笔费用可能会非常可观,因为您需要按使用量付费。解决方案对于基础架构的成本,您无能为力。您只能确保尽可能高效地使用它。SOAP或XML比JSON更昂贵。JSON比Google的ProtocolBuffers这样的二进制协议更昂贵。根据系统的类型,这可能或多或少重要。例如,对于与视频流或VoIP相关的应用,传输成本更为重要。结论您应该了解运输成本以及您的应用程序进行了多少序列化和反序列化。这并不意味着您应该优化,除非您需要它。您应该对资源消耗进行基准测试和监控,并确定运输成本是否适合您。8.网络是一个同质的问题网络不是同质的。同构网络是使用相似配置和相同通信协议的计算机网络。拥有一台类似配置的计算机是一项艰巨的任务。例如,您几乎无法控制哪些移动设备可以连接到您的应用程序。这就是重点放在标准协议上的原因。解决方案您应该选择一种标准格式以避免供应商锁定。这可能意味着XML、JSON或ProtocolBuffers。有很多选项可供选择。结论您需要确保系统的组件可以相互通信。使用专有协议会损害应用程序的互操作性。设计分布式系统很难这些谬论是在20多年前发表的。但他们今天仍然存在,有些比其他人更多。我想现在很多开发人员都知道它们,但我们编写的代码并没有显示出来。我们必须接受这样一个事实,即网络不可靠、不安全并且要花钱。带宽有限。网络拓扑会发生变化。它的组件配置不同。意识到这些限制将有助于我们设计更好的分布式系统。
