当前位置: 首页 > 科技观察

本文详解SpringCloud微服务架构的五脏六腑!

时间:2023-03-16 16:11:34 科技观察

SpringCloud是一个基于SpringBoot实现的微服务框架,包含了实现微服务架构所需的各种组件。注:简单理解SpringBoot是为了简化Spring项目的构建、配置、组合。由于与构建微服务本身没有直接关系,本文不展开SpringBoot。此外,本文中的一些示例涉及到Spring和SpringBoot。建议阅读本文前先了解一下Spring和SpringBoot。本文的读者主要是没有接触过服务架构,想对其有一个宏观认识的同学。本文将从SpringCloud入手,分两节讲述微服务框架的“五脏六腑”:第一节“服务架构”旨在说明两点,一是什么是服务架构及其必要性;二是服务架构是什么?另一个是服务架构基础组件。为什么第一节写的是服务架构而不是微服务架构?主要原因是微服务架构本身与服务架构有着千丝万缕的联系,而服务架构是微服务架构的基础。在第二节“六脏六腑”中,我们将结合SpringCloud的特例,介绍一个完整的微服务框架的组成。为了便于理解《服务架构》,我先讲一个小故事:(改编自一知乎的回答者)Martin(微服务的作者也叫Martin)刚来的时候是一名基层员工公司,上面有经理和老板,当时大家都听老板指挥。但是两年过去了,公司的人数却增加了。在原有的模式下,整个公司的运作效率太低,管理也很混乱。于是已经走上中层岗位的Martin建议老板分部门(服务化),专门的部门只做专门的事情(单一职责)。比如R&D部门只做R&D,HR部门只做招聘。老板听取了马丁的建议,调整了公司的组织架构。有一天,Martin发现公司的部门越来越多,每个部门都不能完全了解对方在做什么,这给跨部门协作(服务调用)带来了困难。行政部门(注册中心)会记录所有的部门,每当有新的部门行政时,都会记录下来(服务注册),然后发布让所有部门都知道(服务发现)。在新的组织架构下,公司效率逐步提升。老板还给了马丁一大笔奖金作为奖励,马丁赢得了白富美,走上了人生的巅峰。这是一个关于公司组织结构演变的故事。主要讲的是随着公司规模的扩大,组织从集中管理到分散管理的过程。映射到我们的信息系统也是如此。随着我们的体制越来越复杂,越来越难管理,有人就想到了分而治之。在解决复杂问题中,分而治之可以说是屡试不爽的方法。服务化是一种拆解手段。上面括号里的内容其实对应了一个面向服务架构的最小组件,即服务、服务调用、注册中心、服务注册、服务发现。有了这些基础组件,就可以实现最简单的服务架构。Service-OrientedArchitectureandMicroserviceArchitecture面向服务的架构(Service-OrientedArchitecture,SOA)和微服务架构是目前两种主流的面向服务的架构,都符合上面的例子,也具备上面提到的所有组件。关于这两种服务架构,有很多话要说,但与本文关系不大。本文不会过多展开,只是简单介绍一下两者的区别。准确的说微服务就是SOAtoESB(EnterpriseServiceBus)。ESB借鉴了计算机组成原理中的通信模型——总线,所有需要与外部系统通信的系统都通过ESB进行标准化和转换,消除了协议和异构系统的差异,使得现有系统可以用于构建一个新的松散耦合的异构分布式系统。微服务架构去掉了ESB,本质上是一种去中心化的思想。“脏”和“心”沿着上一节的思路,从最简单最核心的问题出发,假设服务A要调用服务B,会出现什么问题?服务在哪里?(服务治理问题)如何调用?(服务调用问题)这两个是核心问题,也是任何微服务框架首先要解决的两个问题。为了解决第一个问题,SpringCloud提供了Eureka、Zookeeper、CloudFoundry、Consul等服务治理框架的集成。他们的工作模式是将所有微服务注册到一台服务器上,然后通过心跳进行服务健康监控。这样,当服务A调用B时,就可以从注册中心获取服务B可用的地址和端口来调用。第二个服务调用可能被视为简单的HTTP或RPC调用,这不是问题。但是在分布式场景下,调用服务需要考虑的因素更多。例如,如果一个服务有多个实例,谁来处理传入的请求以及如何平衡请求到每个实例的负载都是难题。SpringCloud提供了两种服务调用方式:一种是Ribbon+restTemplate,另一种是Feign。其中,Ribbon是基于HTTP和TCP客户端的负载均衡器,restTemplate是Spring提供的Restful远程调用模板。两者结合可以实现远程调用的负载均衡。Feign是一个更具声明性的HTTP客户端。开发者可以像调用本地方法一样调用它。完全不像是远程调用。与Ribbon结合使用,也可用于负载均衡。既然两个问题都解决了,下面我们用一个例子来进一步说明。该示例包括微服务中最基本的三个角色(注册中心、服务提供者和服务消费者):注册中心注解@EnableEurekaServer表示ThisSpringBootapplicationisaregistry。@EnableEurekaServer@SpringBootApplicationpublicclassEurekaserverApplication{publicstaticvoidmain(String[]args){SpringApplication.run(EurekaserverApplication.class,args);}}eureka.client.registerWithEureka:false和fetchRegistry:false表示是eurekaserver。server:port:8080eureka:instance:hostname:localhostclient:registerWithEureka:falsefetchRegistry:falseserviceUrl:defaultZone:http://${eureka.instance.hostname}:${server.port}/eureka/service-hello服务注解@EnableEurekaClient表示他是一个Eureka客户端,它会在注册中心注册自己。注解@RestController表示这是一个controller,@RequestMapping("/hello")表示匹配到请求'/hello'时会调用该方法进行响应。@SpringBootApplication@EnableEurekaClient@RestControllerpublicclassServiceHelloApplication{publicstaticvoidmain(String[]args){SpringApplication.run(ServiceHelloApplication.class,args);}@Value("${server.port}")Stringport;@RequestMapping("/hello")publicStringhome(@RequestParamStringname){return"hello"+name+",iamfromport:"+port;}}注册中心的地址是http://localhost:8080/eureka/,也就是我们上面定义的。服务名称是service-hello,将由调用者使用。eureka:client:serviceUrl:defaultZone:http://localhost:8080/eureka/server:port:8081spring:application:name:service-hello服务消费者service-ribbon假设service-ribbon端口为8082,当我们访问http:///localhost:8080/hello,HelloControler收到请求,调用HelloService中的helloService方法,通过定义的restTemplate调用http://service-hello/hello。这里需要注意的是@LoadBalanced注解,表示开启了负载均衡。@SpringBootApplication@EnableDiscoveryClientpublicclassServiceRibbonApplication{publicstaticvoidmain(String[]args){SpringApplication.run(ServiceRibbonApplication.class,args);}@Bean@LoadBalancedRestTemplaterestTemplate(){returnnewRestTemplate();}}@ServicepublicclassHelloService{@AutowiredRestTemplaterestTemplate;publicStringhelloService(Stringname){returnrestTemplate.getForObject("http://service-hello/hello?name="+name,String.class);}}@RestControllerpublicclassHelloControler{@AutowiredHelloServicehelloService;@RequestMapping(value="/hello")publicStringhello(@RequestParamStringname){returnhelloService.helloService(name);}}至此,一个微服务应用的原型就搭建完成了。服务治理和服务调用可以说是“内脏”的“心脏”。“心”的支撑接下来,我们还需要进一步考虑其余的“五脏六腑”,因为没有它们,人是活不长的。下面介绍一个组件对应的问题或需求。服务“雪崩”及熔断由于网络等原因,无法保证服务100%可用。如果单个服务出现问题,调用这个服务就会出现线程阻塞。这时候如果大量请求涌入,就会耗尽Servlet容器的线程资源,导致服务瘫痪。由于服务之间的依赖,故障会在调用链路上传播,导致整个微服务系统崩溃,这就是服务故障的“雪崩”效应。为了解决这个问题,SpringCloud提供了Hystrix断路器的集成。当服务调用失败的频率达到一定阈值时,就会开启熔断器,降级策略可以由开发者制定,通常会返回一个固定值。这避免了级联故障。另外,SpringCloud还提供了HystrixDashboard和HystrixTurbine来帮助我们进行监控和聚合监控。服务暴露与路由网关微服务中服务较多,直接暴露给用户使用不安全,对用户不友好。因此,在微服务和面向服务的架构中,通常会有一个路由网关角色,负责路由的转发和过滤。对应SpringCloud中Zuul和Gateway组件的可用性。什么是服务网关?路由网关接收所有用户请求,负载高,所以通常是一个集群。用户的请求首先会经过一层负载均衡发送到路由网关。服务配置与配置中心在微服务应用中,存在数量庞大的服务,每个服务在不同的环境下都有不同的配置。为了便于服务配置文件的统一管理和实时更新,需要分布式配置中心组件。需要注意的是,这里的配置和注册中心注册的配置信息是两个概念。这里的配置是服务本身的一些配置信息,如下图所示:SpringCloud提供了SpringCloudConfig组件,支持配置服务放置在配置服务的内存中(也就是本地)也可以放置在远程Git仓库中帮助我们管理服务的配置信息。信息同步和消息总线前面的问题提到每个服务都有一些配置信息,那么配置信息更新了怎么办,手动一个一个更新?当然不是,SpringCloud提供了SpringCloudBus组件,通过轻量级的消息代理连接分布式节点。当配置信息更新时,我们只需要更新一个节点的配置,更新就会广播到分布式系统。问题定位和链路跟踪在微服务系统中,服务之间是可以相互调用的,所以我们可能会有一个请求的调用链,整个系统都会有一个调用网络,任何一个服务调用失败或者网络超时都可能导致整个请求失败。因为调用关系的复杂性,给定位问题带来了很大的难度,这也是为什么需要提供服务链路跟踪的原因。SpringCloud为我们提供了SpringCloudSleuth组件,可以跟进一个请求有哪些服务参与,参与的先后顺序,让每个请求的步骤清晰可见。借助服务链路跟踪,我们可以快速定位问题。至此,SpringCloud的基本组件都介绍完了。但是目前所有的组件介绍都是零散的。它们结合起来会是什么样子?如下图所示:偷懒偷了一张图,图中省略了ConfigServer和linktracking组件。不过结合上面的介绍,我们可以大致搞清楚这两个东西在图中的位置。ConfigServer是一个连接所有服务的服务集群,链路跟踪组件集成在每个服务中。总结服务治理是心脏,依托路由网关、消息中心、断路器、链路跟踪、配置中心等,构建整个微服务框架的“内脏”。当然,一个微服务系统比本文所写的要复杂得多,尤其是在不同的业务场景下,要想更深入地理解,还需要不断地实践。作为前端,了解这些内容,一是为了更好的理解整个请求流程,二是为后续访问SOA中的Node子服务积累相关知识。最后分享一个关于Spring的有趣调侃:Spring没有什么是一个注解解决不了的。如果有,则使用两个注释。