边缘控制平面Ambassador全解读

Ambassador是由Datawire开源的一个API网关项目,主要在Kubernetes的容器编排框架中使用。Ambassador本质上是一个通过配置边缘/API来管理Envoy数据面板的控制面板。而Envoy则是一个基于第7层协议的网络代理和通信总线,它是一个由Lyft开源的云原生服务,主要用于处理入口边缘以及内部服务之间的网络通信。今天Envoy正迅速成为现代网络事实上的代理,几乎所有的公共云供应商,以及eBay、Pinterest和Groupon等大型互联网公司都提供对这种代理服务的支持。

本文首先介绍了Ambassador的由来,然后详细介绍了它的发展过程,并讨论了开发人员在构建这个控制面板时遇到的技术挑战和经验教训,该面板用于在Kubernetes集群里管理微服务应用的入口流量(入口边缘)。

本文要点:

  • Ambassador是由Datawire开源的一个API网关,主要在Kubernetes容器编排框架中使用。

  • Ambassador本质上是一个通过配置边缘/API来管理Envoy代理数据面板的控制面板。

  • Envoy是一个处理边缘入口和内部服务之间网络通信的L7代理和通信总线。

  • 本文介绍了Ambassador的由来,然后详细介绍了它的发展过程,并讨论了开发人员在构建这个控制面板时遇到的技术挑战和经验教训,该面板用于管理部署到Kubernetes集群中的基于微服务的应用程序中的入口流量。

  • 将Ambassador迁移到Envoyv2配置和ADS(聚合发现服务) API是一个漫长而艰难的过程,需要大量的架构和设计讨论,以及大量的编码,但是社区的早期反馈是积极的。

Ambassador是由Datawire开源的一个API网关项目,主要在Kubernetes的容器编排框架中使用。Ambassador本质上是一个通过配置边缘/API来管理Envoy数据面板的控制面板。而Envoy则是一个基于第7层协议的网络代理和通信总线,它是一个由Lyft开源的云原生服务,主要用于处理入口边缘以及内部服务之间的网络通信。今天Envoy正迅速成为现代网络事实上的代理,几乎所有的公有云供应商,以及eBay、Pinterest和Groupon等大型互联网公司都提供对这种代理服务的支持。

本文首先介绍了Ambassador的由来,然后详细介绍了它的发展过程,并讨论了开发人员在构建这个控制面板时遇到的技术挑战和经验教训,该面板主要用于在Kubernetes集群里管理微服务应用的入口流量。

云原生新兴组合: Kubernetes与Envoy

像DevOps和微服务一样,云原生也开始变得耳熟能详,并在整个IT行业中不断获得关注。据Gartner报道,2018年全球公共云服务收入预计在1750亿美元左右,而明年仍将有超过15%的增长。目前的公有云市场完全由几家大公司垄断,而这几家公司也几乎把持了所有的关键技术,这让开源即服务的商业前景变得很不明朗。在这样的背景下,Linux基金会于2015年成立了CNCF(云原生计算基金会),旨在讨论和托管全栈云原生环境中的开源组件。

CNCF吸取了Open Stack社区的经验教训,在早期项目发展中相对保守和严格,它甚至为项目提供了发展概要,并要求项目有实际的应用案例,或者项目本身是基于已流行项目(如Kubernetes)的扩展。Google提供的Kubernetes容器编排框架,以及Lyft提供的边缘路由器Envoy代理是CNCF早期的两个主要项目。虽然平台即服务(Platform-as-a-Service,PaaS)架构复杂且各不相同, 但Kubernetes和Envoy一直是其核心组成。

许多PaaS供应商,甚至最终用户的技术团队,都将针对网络第7层元数据的编排容器和通信路由视为云原生系统的数据面板,比如HTTP URI、Headers以及MongoDB协议元数据。因此,更多的初创公司则把重心放在如何创建一个有效的\u0026quot;控制面板\u0026quot;上,例如帮助终端用户实现与数据面板的交互,配置数据面板的自定义参数,观察数据面板中的监测或日志等。

Kubernetes控制面板一般都采用了类似REST的API(又称\u0026quot;Kubernetes API\u0026quot;),像‘kubectl‘这样的CLI工具都是对此类API的二次封装。首版Envoy的控制面板主要使用了JSON的配置方式,还有一些可以选择性更新的松耦合API。随后这些API又变成了Envoy v2 API的一部分,新版Envoy同时提供了一系列基于gRPC的协议缓冲类型API。最初Kubernetes的kubectl工具并没有提供一种类似Envoy的实现,这给后来使用Kubernetes的团队制造了不少麻烦。当然很多关于实现Envoy V2控制面板的初创公司也因此出现。

服务网格 VS API网关

谈到网络中的控制面板,自然就少不了新兴的服务网格技术。诸如lstio、Linkerd和Conser connect之类的服务网格,主要在微服务系统中管理服务之间的通信,也就是所谓的“东-西”方向通信。lstio本身是一个非常有效的控制面板,它通过Envoy代理来管理网格之间的数据面板(L7网络流量);Rust语言编写的Linkerd实现了自己的代理;而Conser Connect则使用了自定义代理,最近也提供了对Envoy代理的支持。

\"\"

Istio架构,上半部分为Envoy代理的数据面板,下半部分为控制面板(图片来自Istio文档)

服务网格需要事先假定用户对网格通信的双方具有高度的所有权和可控制度。例如,同一个公司的不同工程部门的两个服务,或者是一个可信任网络边界内的第三方应用程序服务(可能跨越多个数据中心或虚拟专有云)。在这里,运维团队通常会设置一些符合常理的通信缺省值,而服务团队则在此基础上配置各自服务间的路由。在这种情况下,你不可能完全信任每个服务,因此要实施如速率限制和断路等保护,以此来调查和改变所检测到的不良行为,但是服务网格根本不可能实现这类保护,因为边缘或入口(“南-北”)流量来自外界网络,超出了其控制范畴。

\"\"

集群入口流量通常来自不可控的外部网络

任何来自我们信任的网络之外的通信都可能来自一个心怀不轨的的第三方,他的动机可能就是网络罪犯或破坏移动应用程序中的客户端库,因此我们必须对边缘/API网关部署适当的防御措施。在这里,运维团队将指定一些系统默认值,并根据外部事件实时调整这些值。你可能希望能够实现速率限制、全局配置和指定API负载(例如后端服务或数据集过载),以及DDoS保护实现等(基于时间或区域的保护)。另外,服务开发团队的一些任务也需要访问为新API配置路由的边缘,通过流量跟踪或金丝雀发布来测试和发布新服务。

顺便说一句,为了进一步讨论API网关的角色,Christian Posta最近发表了一篇有趣的博文“API网关正在经历身份危机”。本文作者也写过一些文章,介绍了API网关在云/容器迁移或数字转换过程中的角色,以及如何实现将API网关与现代持续交付模型的集成。

虽然乍一看,服务网格和边缘/API网关非常相似,但他们还是存在着一些细微的差异,而这些差异也影响了边缘控制面板的设计。

边缘控制面板设计

控制面板很大程度上由它所要控制的范围以及所使用的人群决定。我的同事Rafael Schloming之前在旧金山QCon会议上谈到过这个问题,他在会上讨论了针对集中化或去中心化的控制实现,以及服务目前处于的开发/运营周期(原型、关键任务等)是如何影响控制面板实施的。

如上文所述,以边缘代理的控制面板为例,集中化运营或SRE(网站可靠性工程师)团队可能希望为所有的入口流量指定合理的全局默认值和安全措施。然而在第一线工作的产品开发团队则希望能够进行更细化的控制,并且希望能够在本地更改全局设置。

Ambassador社区一开始就将开发人员以及应用工程师作为主要目标人群。因此他们主要关注的是基于去中心化配置的控制面板。Ambassador一开始就按照Kubernetes标准设计,因此我们最好也能够参照Kubernetes的服务规范来配置边缘,例如使用YAML文件,通过kubectl加载。

Ambassador的配置方式有Kubernetes Ingress对象、自定义的Kubernetes注解以及自定义资源定义(CRDs)。我们最终选择了Kubernetes注解,因为它很简洁,也容易上手。理论上Ingress对象是一个更好的选择,但Ingress规范一直处于测试阶段,除了管理入口流量的\u0026quot;最基本\u0026quot;功能之外,其他功能还没怎么达成共识。

如下是一个Ambassador的注解示例,它在Kubernetes服务上实现了一个简单的终端到服务的路由:

kind: ServiceapiVersion: v1metadata:  name: my-service  annotations:    getambassador.io/config: |      ---        apiVersion: ambassador/v0        kind:  Mapping        name:  my_service_mapping        prefix: /my-service/        service: my-servicespec:  selector:    app: MyApp  ports:  - protocol: TCP    port: 80    targetPort: 9376

如果你以前配置过边缘代理、反向代理或API网关,那相信你不会对getambassador.io/config中的配置感到陌生,其主要功能就是将发送到终端前缀的流量映射到名为my-service的Kubernetes服务上。由于本文主要关注Ambassador的设计和实现,因此我们不会涵盖可以配置的所有功能,比如路由、金丝雀发布和速率限制等。虽然Ambassador主要是面向开发人员的角色,但它也实现了很多针对运营商的扩展,并且也可以集中配置身份验证、TLS/SNI、追踪和服务网格集成等。

接下来让我们再回到Ambassador,并看一下它在过去两年中的演变。

Ambassador \u0026lt; v0.40:Envoy v1 APIs,模板以及热重启

Ambassador本身是部署在容器中的Kubernetes服务,并使用Kubernetes Services注解作为其核心配置模型。这种方法使应用程序开发人员可以将路由管理作为Kubernetes服务定义的一部分(也称作\u0026quot;GitOps\u0026quot;方法)。将简单的Ambassador注解转换为有效的Envoy v1配置并不是一项简单的任务,毕竟Ambassador与Envoy的配置在设计上并不相同。因此,在进行配置转换的时候,我们本着聚合和简化操作的目的,更改了很多Ambassador内部的逻辑。

具体来讲,Kubernetes用户可按照如下步骤完成对Ambassador的配置:

  1. Kubernetes API异步通知Ambassador的更改。

  2. Ambassador将配置转换为抽象的中间代码(IR)。

  3. 从IR中生成一个Envoy配置文件。

  4. 使用Ambassador验证Envoy配置文件。

  5. 如果配置文件有效,Ambassador将使用Envoy的热重新启动机制来部署新的配置,并保持连接。

  6. 流量将会在新启动的Envoy进程中传输。

这个初始实现有很多好处:首先机制简单,其次Ambassador同Envoy之间的配置转换是可靠的,最后Ambassador与基于文件的热重启的整合也是可靠的。

但是,这一版本的Ambassador仍有很多需要改善的地方。首先,尽管热重启对于大多数用例有效,但是它还不够快;而且一些用户(特别是那些部署大型应用程序的用户)发现它限制了更改配置的频率;最后,热重启有时也会中断连接,尤其是像websocket或gRPC流这类的长期连接。

更重要的是,尽管初版Ambassador到Envoy的中间代码(IR)允许快速原型设计,但由于功能还太初级,很难做出实质性的改变。虽然这是从一开始就存在的问题,但随着Envoy升级到Envoy v2 API,这变成了一个关键问题。正如Matt Klein在他的博文“通用数据面板API“中所描述的那样,v2 API为Ambassador提供了很多好处:诸如访问新特性、解决上面提到的连接中断问题,但遗憾的是现有的IR实现仍然没有本质的提高。

Ambassador \u0026gt; v0.40: Envoy v2 API(ADS)、中间代码、KAT测试

在与Ambassador社区协商后,Ambassador首席工程师Flynn带领的Datawire团队在2018年对Ambassador内部进行了重构。重构主要有两个关键目标。首先,能够集成Envoy v2的配置格式,这将支持诸如服务器名称指示、基于标签的速率限制和改进的身份验证等特性。其次,能够对Envoy配置进行更强大的语义验证,毕竟它的复杂性越来越高(特别是在大规模应用程序部署的时候)。

Datawire团队首先按照多遍编译器的思路调整了Ambassador的内部结构。添加了类层次结构以便可以更好的反映Ambassador配置资源、中间代码和Envoy配置资源之间的分离。而Datawire以外的社区也重新设计了Ambassador的核心部分。

之所以采取这些方法是处于如下几个方面的考虑:首先,Envoy代理仍是一个快速发展的项目,我们不能每次都因为Envoy配置的微小变化就对Ambassador进行重新设计。而且,我们也希望能够实现配置文件的语义验证。

随着对Envoy v2整合工作的深入,Datawire团队很快又发现了一个测试上的问题,那就是随着Ambassador开始支持越来越多的特征,它在处理不太常见但又正确的特征组合时出现了非常多的错误。这促使他们创建一个新的测试要求,而这意味着需要对Ambassador的测试套件进行重新编写,以便能够自动测试大部分的特性组合,而不是依靠人工来编写每个测试用例。此外,这套测试套件需要快速,以便最大限度地提高工程效率。

因此,在对Ambassador重构的同时,Datawire团队创建了新的Kubernetes验收测试(KAT)框架。KAT是一个可扩展的测试框架,它的主要功能如下:

  1. 将一组服务(连同Ambassador)部署到Kubernetes集群

  2. 对分离出来的API运行一系列验证查询

  3. 对这些查询结果执行一组断言测试

KAT是为性能而设计的——它提前将测试设置打包,然后在步骤3中使用高性能HTTP客户机异步查询。KAT中的流量驱动程序使用了另一个开源工具Telepresence,这也让调试诊断变的更加容易。

随着KAT测试框架的到位,Datawire团队又遇到了一些关于Envoy v2配置和热重启的问题,他们开始考虑转换到Envoy的ADS(聚合发现服务)API。这样完全消除了配置更改时重启的限制,也避免了高负载或长时间连接下的中断问题。Datawire团队最终决定使用GO编写的Envoy控制面板连接到ADS。但是,也增加了Ambassador代码库对GO的依赖。

使用新的测试框架,新的中间代码生成有效的Envoy v2配置以及ADS,Ambassador 0.50的主要体系结构更改已经完成。现在,用户使用含有Ambassador注解的Kubernetes清单的步骤如下:

\"\"

就在发布之前,Datawire团队又碰到了一个问题,Azure Kubernetes服务(AKS)不能正确检测到Ambassador注解的更改。通过与AKS工程团队的高效合作,他们很快找到了问题点——Kubernetes API服务器中的代理链丢掉了很多访问请求,对此最好的缓解措施是使用支持调用API服务器的FQDN。不幸的是,这需要AKS中的mutating webhook,而官方的Kubernetes Python客户端并没有实现这个特性。因此,我们使用了Kubernetes Golang客户端,这也是基于Go的第二个依赖项。

总结

正如Matt Klein在EnvoyCon开幕式上所提到的,随着目前Envoy代理在云原生技术领域的流行,不知道Envoy的人群反而成了少数。我们知道,谷歌的Istio已经在Kubernetes用户中提高了Envoy的知名度,现在主要的云供应商都在关注Envoy,例如,在AWS app网格和Azure Service Fabric网格。在EnvoyCon上,我们还听说了一些大公司如eBay、Pinterest和Groupon都在使用Envoy作为他们的主要边缘代理,还有一些其他基于Envoy的边缘代理控制面板,如Istio Gateway、Solo.io Gloo和HeptioContour等。Envoy已经成为云原生通信的通用数据面板,当然它在控制面板领域内仍有许多工作。

在本文中,我们讨论了Datawire团队和Ambassador开源社区如何成功地将Envoy v2配置和ADS API应用到Ambassador边缘控制面板。在构建Ambassador 0.50的过程中,我们学到了很多东西:

  • Kubenetes和Envoy是功能非常强大的框架,但它们也在不断成长中——很多时候都没有最新的文档交付,维护人员只能通过阅读源码来学习。

  • Kubernets/Envoy生态系统中支持最好的类库都是用Go编写的。虽然我们喜欢Python,但是我们不得不采用Go,这样我们就不必自己维护太多的组件。

  • 有时候重新设计测试工具也能促进软件开发。通常,重新设计测试工具的真正成本只是将旧的测试移植到新的工具实现。

  • 为边缘代理设计和实现一个有效的控制面板是很困难的,来自Kubernetes、Envoy和Ambassador开源社区的反馈非常有用。

将Ambassador迁移到Envoy v2配置和ADS API是一个漫长而艰难的过程,需要大量架构和设计上的讨论,当然也少不了大量的编码,但早期反馈的结果是积极的。Ambassador 0.50已经发布,你可以试用一下,并在Slack频道或Twitter上分享一下你的意见。

关于作者

丹尼尔·布莱恩特,公司技术变革的领导者,自由职业顾问,Datawire是其客户之一。他目前的工作是实现公司内部开发的敏捷性:引入更好的需求收集和规划技术,关注敏捷开发中架构的相关性,促进持续集成和交付等。Daniel目前的技术专长集中在\u0026quot;DevOps\u0026quot;工具化、云/容器平台和微服务实现。他同时是伦敦Java社区(LJC)的领导者,并参与了很多开源项目。他还为InfoQ、DZone和Voxxed等知名技术网站的撰稿,并定期在QCon、JavaOne和Devoxx等国际会议上发表演讲。

查看英文原文:https://www.infoq.com/articles/ambassador-api-gateway-kubernetes

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/449312.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Linux 文件编辑命令 详细整理

前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。点击跳转到教程。 一、vi编辑器有3种基本工作模式 首先需要知道vi编辑器有3种基本工作模式,分别是:命令模式、文本输入模式、和末…

专访迅雷首席工程师:迅雷的下一代互联网底层技术构想

摘要:互联网合纵连横频频上演,迅雷与小米的联姻也成为了热点,有许多人为迅雷的上市和迅雷的未来担忧,这家像工程师一样的公司,命运会怎样,他们未来会如何走下去?对此CSDN专访了迅雷首席工程师刘…

YASnippet - emacs 的代码片段管理工具

添加 snippet M-x 然后输入 yas-new-snippet 回车 RET&#xff0c;会出现一个新的 buffer # -*- mode: snippet -*-# name: # key: # --在出现的 buffer 中填写相应的数据 # -*- mode: snippet -*-# name: vard# key: vard# --echo <pre>;var_dump($0);die;c-x c…

深入vuex原理(上)

前言 vuex作为vue生态的重要组成部分&#xff0c;是对store进行管理的一柄利剑。简而言之&#xff0c;vuex是vue的状态管理器。使用vuex可用使数据流变得清晰、可追踪、可预测&#xff0c;更可以简单的实现 类似时光穿梭 等高级功能&#xff0c;对于复杂的大型应用来讲&#xf…

Maven入门(含实例教程)

前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。点击跳转到教程。 Maven这个个项目管理和构建自动化工具&#xff0c;越来越多的开发人员使用它来管理项目中的jar包。接下来小宝鸽&#xff0c;将从下面几个…

进阶正则表达式

本文同步自我的博客园&#xff1a;http://www.cnblogs.com/hustskyking/ 关于正则表达式&#xff0c;网上可以搜到一大片文章&#xff0c;我之前也搜集了一些资料&#xff0c;并做了排版整理&#xff0c;可以看这篇文章http://www.cnblogs.com/hustskyking/archive/2013/06/04/…

tkinter拦截关闭事件

import tkinter as tk from tkinter import messageboxroot tk.Tk()def on_closing():if messagebox.askokcancel("Quit", "Do you want to quit?"):root.destroy()root.protocol("WM_DELETE_WINDOW", on_closing) root.mainloop() 转载于:htt…

阿里云服务器一分价钱一分货,切记!

阿里云为了满足低端市场的需求&#xff0c;会推出一些价格非常便宜的机器&#xff0c;但是这些机器是为新手练手用或者做测试用的&#xff0c;性能不行。你不要指望花每月9.5元&#xff0c;买一台学生机&#xff0c;就可以放置流量大的网站还不卡&#xff0c;那个不现实。阿里云…

请记住:你的付出都会以该有的方式归来(图)

人&#xff0c;这一生就像一个耕种的农民。你不是在付出&#xff0c;就是在收获。当然&#xff0c;有人说&#xff0c;付出并不一定有回报。这是大多数人都认同的&#xff0c;也就是付出与得到不一定成正比&#xff0c;不是付出的越多就得到的越多。但我想告诉你的是&#xff0…

c++primer plus笔记

> 第六版 操作符重载 #include<iostream> using namespace std;class Time { public:Time(){hm0;}Time(int _h,int _m){h _h;m _m;}void show(){printf("%02d:%02d \n",h,m);}Time operator(const Time &t){Time result;result.m t.m m;result.h t…

Luogu P3975 [TJOI2015]弦论

题目链接 \(Click\) \(Here\) 题目大意&#xff1a; 重复子串不算的第\(k\)大子串重复子串计入的第\(k\)大子串写法&#xff1a;后缀自动机。 和\(OI\) \(Wiki\)上介绍的写法不太一样&#xff0c;因为要同时解决两个问题。 把字符串每个前缀所在等价类的\(siz\)记为\(1\)&#…

《 图解 HTTP 》读书笔记

前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。点击跳转到教程。 1. TCP/IP 协议族按层次分为&#xff1a;应用层、传输层、网络层、数据链路层。 2. IP 协议的作用是把各种数据包传送给对方。 3. IP …

身体出现危险时会发出信号 这太重要了 一定收藏 !(组图)

太重要了&#xff01;真的太重要了&#xff01; 心脏有问题时———左边手臂会酸、麻、痛。 肝脏有问题时———小腿晚上睡觉时容易抽筋。 肾脏出现问题时———声音就会出不来&#xff0c;就会沙哑。 脾胃出现问题时———偏头痛。 任何试图更改生物钟的行为&#xff0c;都将给…

数据结构与算法-概念

计算机从解决数值计算问题到解决生活中的问题 现实生活中的问题涉及不同个体间的复杂联系 需要在计算机程序中描述生活中个体间的联系数据结构主要研究非数值计算程序问题中的操作对象以及它们之间的关系而不是研究复杂的算法 数据结构 基本概念 数据&#xff1a;程序的操作对象…

腾讯联手联通推出车联网“网卡”,打“内容”+“流量”的组合拳

车载生态已经成为了一个兵家必争之地了&#xff0c;于商业前景而言&#xff0c;这是一个BAT都无法忽视的掘金胜地。 从市场数据来看&#xff0c;全球车联网市场年复合增长率达到25%&#xff0c;根据汽车之家大数据显示&#xff1a;自2014年以来&#xff0c;车联网上市新车型渗…

编程面试中的十个常见错误

本文由 伯乐在线 - darkinlight 翻译自 thegeekstuff。欢迎加入技术翻译小组。转载请参见文章末尾处的要求。 身为程序员&#xff0c;你肯定知道和其他技术工作面试比起来&#xff0c;编程工作的面试流程略有不同。 这篇文章会就你在编程面试中应当避免的10个问题展开讨…

费曼技巧与博客

费曼技巧与博客 什么是费曼技巧&#xff1f; 费曼技巧是一种学习方法&#xff0c;核心是以教促学。 具体实践 以学习费曼技巧为例&#xff1a; 确定学习目标为学习费曼技巧。寻找资料&#xff08;网络、书籍、报刊等&#xff09;学习费曼技巧&#xff0c;直到自己认为已经理解了…

阿里云服务器 CentOS 7上-- Docker 安装 网关(API-Getway)--KONG

前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。点击跳转到教程。 全程操作按官方文档来就可以了。 1.将 Kong 连接到 Cassandra 或 PostgreSQL 容器 Kong支持 2 种数据库&#xff1a;Cassandra 或 Post…

每个程序员都应该了解的内存知识

英文原文&#xff1a;lwn.net&#xff0c;翻译&#xff1a;开源中国 [编辑的话: Ulrich Drepper最近问我们&#xff0c;是不是有兴趣发表一篇他写的内存方面的长文。我们不用看太多就已经知道&#xff0c;LWN的读者们会喜欢这篇文章的。内存的使用常常是软件性能的决定性因子&…

山区建小学

题目描述 政府在某山区修建了一条道路&#xff0c;恰好穿越总共nn个村庄的每个村庄一次&#xff0c;没有回路或交叉&#xff0c;任意两个村庄只能通过这条路来往。已知任意两个相邻的村庄之间的距离为d_idi​&#xff08;为正整数&#xff09;&#xff0c;其中&#xff0c;0<…