利用Netflix打造的组件和各种知名工具,成功应对微服务和分布式计算带来的技术挑战。在过去的一年里,微服务已经成为软件架构领域的一个热门新名词,我们可以很容易地举出它带来的诸多比较优势。然而,我们必须清醒地认识到,一旦我们开始按照微服务的思想拆分现有的架构体系,就意味着我们将不可避免地进入分布式系统领域。在上一篇文章中,我们讨论了分布式计算的八个误区*。可见,这种制度本身就充满了风险,一旦犯了这八个错误中的任何一个,后果自负。结果。在我个人看来,如果要把这些误解归纳为一个观点,那就是:对于分布式系统来说,任何关于一致性或者可靠性的表述都没有任何保证可言。我们需要假设系统中的各种行为和组件位置始终处于不断变化的状态。造成的后果主要有两方面:组件有时会导致服务质量不佳甚至服务下线,我们只能统称为“故障”,很难说清楚到底是哪里出了问题。一旦处理不当,此类故障将导致中断和停机,这意味着系统将无法按照既定设计为用户提供服务。鉴于此,要想享受微服务带来的众多优势(包括松散耦合、自治服务、去中心化治理、易于持续交付等),就必须避免单一故障导致系统崩溃的恐怖。碰撞。关于这一点,Erlang语言之父JoeArmstrong曾在一篇题为《如何构建永远运行、自我修复且可扩展的系统》的文章中进行了透彻的阐述。在他看来,这样的系统看起来非常类似于我们所说的微服务架构,但强调容错是一种自愈系统。那么对于我们来说,如何才能构建出如此扎实可靠的系统解决方案呢?Netflix在微服务架构的实施和推广方面一直扮演着先行者的角色。作为其业务建设的原则之一,Netflix认为系统解决方案必须能够承受任何组件的突然故障,同时整个系统仍然可以继续正常运行(这意味着我们仍然可以在平台上观看电影,而且Netflix还可以继续记录用户的观看偏好)。在尝试构建这样一个系统时,我们遇到了以下常见的技术挑战:由于系统需要拆分成多个分布式进程,那么我们如何将这些配置分发到这些进程当中呢?当需要修改这些配置方案时,如何在不重新部署所有进程的情况下更新配置内容呢?在这样的系统中,尤其是部署在云环境中的系统,每个进程不仅在内容上而且在位置上都在不断变化(尤其是在故障转移的情况下)。如何准确判断需要协作的进程的具体位置?一旦我们检测到当前进程亲和性的几个可能位置,我们如何选择下一个与之通信的进程实例?假设选择了一个流程实例,并且在与该实例通信的过程中,该实例失败了。我们如何防止由此引起的级联故障?当系统的综合运行行为不断给自治服务带来进化拓扑时,我们如何维护它的状态?目视监控,从而做出有针对性的精准调整?事实上,您可以部署各种样板模式和开源工具来解决上述技术挑战。Netflix构建并开源了各种组件,并在生产环境中进行了一系列测试。理论上,我们可以利用这些工具来构建能够“永远运行、自我修复和扩展”的系统。对于刚开始构建分布式系统的朋友,我们当前的首要任务是了解这些实现模型,掌握Netflix组件并应用,然后将这些组件部署、管理和集成到自己的系统中。由于采用任何新技术依赖性都会引入软件工程解决方案中从未见过的复杂元素,我们建议您通过直接采用Netflix的堆栈来最大程度地减少这种潜在的摩擦。Spring工程团队自成立以来一直在努力打造一个能够应对Java复杂性的强大武器。我们早期的重点是消除J2EE对企业应用程序开发人员的生产力影响。着眼于近期,我们的主要精力已经转移到实现云原生应用的构建上,这方面的大部分工作成果都被纳入或围绕着SpringCloud项目展开。SpringCloud项目的既定目标是为Spring开发者提供一整套简单易用的工具,以确保他们能够轻松构建自己需要的分布式系统解决方案。为了实现这个目标,SpringCloud在NetflixOSS栈的基础上,集成并封装了大量的实现栈。然后可以通过各种众所周知的基于注释的配置工具、Java配置工具和基于模板的编程工具来交付这些堆栈。下面我们来看一下SpringCloud中的一些常用组件。SpringCloudConfigServerSpringCloudConfigServer可以提供具有水平扩展性的集中式配置服务。它使用的数据存储在当前支持本地存储、Git和Subversion的可插拔库层中。通过使用版本控制系统作为配置存储方案,开发者可以方便地调整版本内容和审计配置。图1:SpringCloudConfigServer配置内容将以Java属性或YAML文件的形式体现。ConfigServer会将这些文件组合成一个环境对象,该对象包含一个易于理解的Spring属性模型和作为RESTAPI存在的配置文件。任何应用程序都可以直接调用RESTAPI中包含的配置数据,但我们也可以在SpringBoot应用程序中添加智能客户端绑定方案,后者自动将从ConfigServer接收到的配置信息分发到任何本地配置中。SpringCloudBusSpringCloudConfigServer是一种强大的配置分发机制,可以在保证一致性的前提下,将配置内容分发到多个应用实例。但是,根据其设计思路的局限性,我们只能在应用启动时更新其配置。在Git中向属性发送新值时,我们需要手动重启每个应用程序进程,以确保该值真正包含在应用程序中。显然,您需要能够在不重启的情况下更新应用程序配置内容。图2:带有SpringCloudBus的SpringCloudConfigServerSpringCloudBus的工作是向应用程序实例添加管理背板。它目前依赖于将一组客户端绑定到一组AMQP交换器和队列,但这个后端也被设计为可插入的。SpringCloudBus为我们的应用程序带来了更多的管理端点。在图2中,我们可以看到greeting属性的值被发送到Git,然后请求被发送到应用程序A中的/bus/refresh端点。该请求将触发以下三个事件:应用程序A请求最新的来自配置服务器的配置内容的版本。任何标有@RefreshScope的SpringBean都将重新初始化并加载新配置。应用程序A向AMQP交换器发送一条消息,表明它已收到更新指示。通过监听AMQP队列加入到CloudBus中的应用B和应用C,将获取到上述消息,并按照与应用A相同的方式进行配置更新,现在我们可以在不重启的情况下更新应用配置了。#p#SpringCloudNetflixSpringCloudNetflix提供了各种Netflix组件的封装方案,包括Eureka、Ribbon、Hystrix和Zuul。接下来,我将分别对其进行说明。Eureka是一套弹性服务注册实现方案。服务注册是服务发现模式的一种实现机制(如图3所示)。图3:使用服务注册表的服务发现SpringCloudNetflix通过直接将spring-cloud-starter-eureka-server关联添加到SpringBoot应用程序,然后将该应用程序的配置类与@EnableEurekaServer集成来嵌入Eureka服务器的部署工作。应用程序可以通过添加spring-cloud-starter-eureka关联并将其配置类与@EnableDiscoveryClient集成来加入服务发现过程。通过集成,我们可以将配置好的合适的DiscoveryClient实例注入到任何SpringBean中。在我们列出的示例中,DiscoveryClient作为服务发现的抽象机制可以通过Eureka实现,但您也可以将其与其他替代堆栈(如Consul)集成。DiscoveryClient可以通过服务的逻辑标识符提供位置信息(如网络地址)和其他与注册到Eureka的服务实例相关的元数据。Eureka提供的负载均衡机制只支持单周期情况。Ribbon提供的客户端IPC库更加完善,它还具有可配置的负载均衡机制和容错能力。可以使用从Eureka服务器获得的动态服务器列表填充功能区。SpringCloudNetflix通过向SpringBoot应用程序添加spring-cloud-starter-ribbon依赖项与Ribbon集成。这组额外的库允许用户将正确配置的LoadBalancerClient实例注入SpringBeans以实现客户端负载平衡(如图4所示)。图4:使用客户端负载均衡机制在此类任务中,我们可以使用Ribbon来实现额外的负载均衡算法,包括可用性过滤、加权响应时间和可用性域亲和性。SpringCloudNetflix还通过自动创建可注入任何SpringBean的Ribbon增强型RestTemplate实例,进一步改进了Spring开发人员的Ribbon使用。之后,开发者可以方便地将URL提供的逻辑服务名提交给RestTemplate:@Autowired@LoadBalancedprivateRestTemplaterestTemplate;@RequestMapping("/")publicStringconsume(){ProducerResponseresponse=restTemplate.getForObject("http://producer",ProducerResponse。班级);returnString.format("{\"value\":%s}",response.getValue());}Hystrix可以为断路器和闭门模型等分布式系统提供一套通用的容错实现。断路器通常用作状态机,如图5所示。图5:断路器状态机断路器可以放置在服务及其远程依赖项之间。如果该电路关闭,所有对该亲和力的调用通常都会直接通过。如果调用失败,则计算失败。一旦故障次数在可配置的时间间隔内达到阈值,电路将跳闸。而在断开连接状态下,将不再向该关系发送调用,结果结果将是可定制的(包括报告异常、返回虚假数据、调用其他关系等)。状态机周期性地进入所谓的“半开”状态,其目的是检测关联是否处于健康运行状态。在这种状态下,请求通常会继续通过。当请求成功通过时,设备返回到关闭状态。如果请求失败,设备将返回到断开连接状态。#p#SpringCloud应用程序可以通过添加spring-cloud-starter-hystrix依赖项并将其配置类与@EnableCircuitBreaker集成来利用Hystrix。之后,您可以通过与@HystrixCommand集成将断路器机制集成到任何SpringBean方法中:@HystrixCommand(fallbackMethod="getProducerFallback");}在上面的示例中指定了一个名为getProducerFallback的备用方法。当断路器断开时,这个方法会代替getValue接受调用:privateProducerResponsegetProducerFallback(){returnnewProducerResponse(42);}除了实现状态机机制,Hystrix还可以提供来自各个断路器机制的重要遥测指标流,具体来说,它包括请求计量、响应时间直方图以及成功、失败和短路请求的数量(如图6所示)。图6:Hystrix仪表板Zuul处理对Netflix边缘服务的所有传入请求。它可以与Ribbon和Hystrix等其他Netflix组件结合,提供灵活弹性的Netflix服务路由层。Netflix在Zuul中加载动态过滤机制,实现如下功能:认证与安全:识别各种资源的认证要求,拒绝不符合要求的请求。审核监控:在边缘追踪有意义的数据和统计结果,为我们带来准确的生产状态结论。动态路由:根据需要将请求动态路由到不同的后端集群。压力测试:逐渐增加针对集群的负载流量以计算性能水平。负载分配:为每种负载类型分配相应的容量,并丢弃超过限制的请求。静态响应处理:直接在边缘构建部分响应,防止它们流入内部集群。多区域弹性:跨AWS区域的请求路由,旨在多样化ELB使用并使边缘尽可能靠近用户。此外,Netflix还利用Zuul的功能,通过金丝雀发布实现精准路由和压力测试。SpringCloud建立了嵌入式Zuul代理机制,简化了UI应用在常见用例中需要代理调用一个或多个后端服务的相应开发流程。对于需要将UI代理到后端服务的用例,此功能非常方便,避免了管理CORS(即跨域资源共享)和对所有后端进行独立身份验证的复杂性。Zuul代理机制的一个重要应用就是实现API网关模式(如图7所示)。图7:API网关模式下的SpringCloud增强了内嵌的Zuulagent使其能够自动实现文件上传处理。配合SpringCloudSecurity后,可以轻松实现OAuth2单点登录,将token传递给下游服务。Zuul使用Ribbon作为其客户端和所有出站请求的负载平衡机制。Ribbon的动态服务器列表内容通常由Eureka填充,但SpringCloud也可以从其他来源填充??列表。SpringCloudLattice项目已经能够通过轮询CloudFoundryDiego的ReceptorAPI来填充功能区的服务器列表。涉足微服务世界的决定意味着我们正式接受了分布式系统带来的许多挑战,这绝不是一个“凑合”的解决方案。因此,我们必须假设系统中各个组件的行为和位置总是在变化,甚至经常呈现出不可预知的状态。在今天的文章中,我们谈到了几种现成的模式可以帮助你解决这样的挑战,并且这些模式已经在NetflixOSS和SpringCloud中得到了实践验证。我个人建议您在着手构建您理想的“始终运行、自我修复和可扩展”的系统解决方案之前先尝试和体验它们。*备注:这八个误区是:1.网络环境可靠2.延迟级别为零3.传输带宽无限4.网络环境安全5.拓扑不会改变6.总会有管理员帮助解决问题7.流量成本为零8.网络中的各个组件具有同质性原标题:用SpringCloud构建自愈分布式系统
