概述毫无疑问,SpringCloud是目前微服务架构领域的佼佼者,无数的书籍和博客都在阐述这项技术。但是大部分的讲解还停留在使用SpringCloud功能的层面,很多人可能并不清楚底层原理。因此,本文将通过大量的手绘图来讲述SpringCloud微服务架构的底层原理。事实上,SpringCloud是一个包含很多组件的全家桶技术栈。本文从其核心组件入手,分析其底层工作原理。即Eureka、Ribbon、Feign、Hystrix、Zuul的组件。业务场景介绍首先给大家讲一个业务场景。假设我们现在正在开发一个电子商务网站,想要实现支付订单的功能。流程如下:创建订单后,如果用户立即为订单付款,我们需要更新订单状态为“已付款”。扣除相应的商品存货。通知仓库中心发货。为用户的购物添加相应的积分。对于以上流程,我们需要订单服务、库存服务、仓储服务、积分服务。整个流程的大致思路是这样的:用户完成订单支付后,会去订单服务,更新订单状态并调用库存服务完成相应的功能。订单服务调用仓库服务完成相应的功能。订单服务调用积分服务完成相应的功能。至此,整个支付订单的业务流程结束。下图清晰的展示了服务间的调用过程:好!结合业务场景,我们来看看这些组件在SpringCloud微服务架构中是如何相互协作的,它们各自的作用以及背后的原理。SpringCloud核心组件:Eureka先来考虑第一个问题:如果订单服务要调用库存服务、仓储服务、积分服务,如何调用?订单服务甚至不知道库存服务在哪台机器上!就算他想提出要求,也不知道该向谁提出,所以他也无能为力!这时候就轮到SpringCloudEureka上场了。Eureka是微服务架构中的注册中心,负责服务的注册和发现。我们来看下图,结合图片仔细分析一下整个过程:用于存储此服务的信息向EurekaServer注册。说白了,就是告诉EurekaServer自己在哪台机器上,监听在哪个端口上。而EurekaServer是一个注册中心,里面有一个注册中心,里面保存着每一个服务的机器和端口号。订单服务中也有一个EurekaClient组件。这个EurekaClient组件会向EurekaServer询问:库存服务在哪台机器上?哪个端口正在监听?仓储服务怎么样?积分服务呢?然后就可以从EurekaServer注册中心拉取相关信息到本地缓存中。这时候如果订单服务要调用库存服务,能不能找到你本地的EurekaClient,问下库存服务在哪台机器上?监听哪个端口?收到响应后,可以立即发送请求调用库存服务接口扣减库存!同理,如果订单服务需要调用仓储服务和积分服务,也是一样的。总结一下:EurekaClient:负责将这个服务的信息注册到EurekaServer中。EurekaServer:Registry,里面有一个注册中心,里面保存着每一个服务的机器和端口号。SpringCloud核心组件:Feign的订单服务现在知道库存服务、点服务和仓库服务在哪里,它正在监听哪些端口号。但是新的问题又出现了:订单服务是否需要写很多代码,与其他服务建立网络连接,然后构造一个复杂的请求,然后发送请求,最后返回的响应结果写很多代码?处理吗?这是上面过程翻译的代码片段,一起来看看,体会一下这种绝望无助的感觉吧!友情提醒,前方高能:看完上面的大段代码,是不是感觉背脊发凉,冒冷汗?其实你在服务之间调用的时候,如果每次都手写代码的话,代码量至少会比上面这段大好几倍,所以这件事根本就不是地球人能做的。既然如此,我们该怎么办?不用担心,Feign已经为我们提供了优雅的解决方案。让我们看看如果你使用Feign,你的订单服务调用库存服务的代码会发生什么?看完上面的代码感觉如何?是不是觉得整个世界都干净了,找到了重新活下去的勇气!没有建立连接、构造请求、解析响应的底层代码,只需要定义一个带注解的FeignClient接口,然后调用该接口即可。人们的FeignClient会根据你在底层的注解,与你指定的服务建立连接,构造请求,发起请求,获取响应,解析响应等。这一系列脏活累活都是Feign帮你完成的。那么问题来了,Feign是怎么做到如此神奇的呢?很简单,Feign的一个关键机制是使用动态代理。我们来看下图,结合图来分析一下:首先,如果你在一个接口上定义了@FeignClient注解,Feign会为这个接口创建一个动态代理。那么调用那个接口的话,其实质就是调用Feign创建的动态代理,是核心中的核心。Feign的动态代理会根据你在接口上的@RequestMapping等注解动态构造你要请求的服务地址。最后针对这个地址发起请求,解析响应。SpringCloud核心组件:Ribbon完成Feign,还没完成。现在新问题又来了,如果库存服务部署在5台机器上,如下:192.168.169:9000192.168.170:9000192.168.171:9000192.168.172:9000192.168.173:9000这下麻烦了!**Feign如何知道请求哪台机器?这就是SpringCloudRibbon派上用场的地方。Ribbon就是为了解决这个问题而设计的。它的作用是负载均衡,会帮你为每个请求选择一台机器,把请求平均分配到每台机器上。Ribbon的负载均衡默认使用最经典的RoundRobin轮询算法。这是什么?简单来说,如果订单服务向库存服务发起10次请求,那么让你请求第一台机器,然后是第二台机器,第三台机器,第四台机器,第五台机器,然后再来——一个循环,第一台机器,第二台机器。..等等。另外,Ribbon与Feign和Eureka紧密配合完成工作,具体如下:首先,Ribbon会从EurekaClient获取对应的服务注册中心,并知道所有的服务部署在哪些机器上,监控哪些端口号。然后Ribbon就可以使用默认的RoundRobin算法,选择一台Feign构造的机器,并向这台机器发起请求。对于上面的整个过程,再放一张图帮助大家更深入的理解:SpringCloud核心组件:Hystrix在微服务架构中,一个系统会有很多服务。以本文的业务场景为例:订单服务在一个业务流程中需要调用三个服务。现在假设订单服务本身最多只有100个线程可以处理请求。然后,点服务不幸挂了。订单服务每次调用积分服务都会卡住几秒,然后抛出超时异常。下面我们一起来分析一下,这会造成什么问题呢?如果系统处于高并发场景,当大量请求进来时,订单服务的100个线程会卡在请求点服务。结果,订单服务没有可以处理请求的线程。然后别人请求订单服务时,发现订单服务也挂了,不响应任何请求。以上就是微服务架构中可怕的服务雪崩问题,如下图所示:如上图,这么多服务互相调用,如果不做保护,如果某个服务挂了,会造成连锁反应,导致其他服务也挂掉。比如点服务挂了,那么订单服务的所有线程都会卡在请求点服务,所有线程都无法工作,导致订单服务瞬间挂掉,订单的所有请求别人的服务会卡住,无法响应。不过大家想一想,即使暂停积分服务,订单服务也不需要暂停!为什么?我们结合业务来看:下单付款时,只要扣掉库存,然后通知仓库发货就OK了。要是积分服务挂了,大不了等他恢复过来,慢慢靠人肉手动恢复数据!为什么一定要因为一个积分服务而暂停订单服务?不能接受的!既然分析了问题,那么如何解决呢?这时候,就轮到Hystrix登场了。Hystrix是一个隔离、断路、降级的框架。你是什??么意思?说白了Hystrix会创建很多小的线程池。比如订单服务请求库存服务是一个线程池,请求存储服务是一个线程池,请求信用服务是一个线程池。线程池中的每个线程仅用于请求该服务。打个比方:不幸的是,积分服务现在宕机了,会发生什么?当然,订单服务中调用积分服务的线程会卡住,无法工作!但由于订单服务调用库存服务和仓储服务的两个线程池都正常工作,因此这两个服务不会受到任何影响。这时候如果别人请求订单服务,订单服务仍然可以正常调用库存服务扣除库存,调用仓储服务通知发货。只是在调用积分服务时,每次都会报错。但是如果信用服务都挂了,为什么每次调用都要卡几秒呢?**是否有意义?当然不是!**所以我们可以直接融合积分服务。比如你在5分钟内请求积分服务,它会直接返回。不要去网络请求卡了几秒。这个过程就是所谓的断路器!那人又说,哥,如果积分服务失败,你就掉线了。无论如何,你在做什么!为什么什么都不做就直接回去?没问题,降级吧:每次调用积分服务,都会在数据库中记录一条消息,说你给某个用户加了多少积分,因为积分服务宕机了,所以加不成功!这样,当积分服务恢复后,你就可以根据这些记录手动加分了。此过程称为降级。为了帮助大家更直观的理解,我们用一张图来梳理一下Hystrix隔离、熔断、降级的全过程:网关。该组件负责网络路由。不懂网络路由?好吧,我告诉你,如果没有Zuul,你的日常工作会怎样?假设你后台部署了上百个服务,现在有个前端小哥,人家的请求直接从浏览器发出来。比如:如果有人要请求一个库存服务,你还让他们记住这个服务的名字是inventory-service吗?部署在5台机器上?就算人家愿意记住这个,你有后台上百个服务的名称和地址吗?难不成别人要了一个,就得记住一个?要这么玩,真是友谊之舟,说来就翻!上面的情况简直是不现实的。所以在一般的微服务架构中,一定要在里面设计一个网关,比如android,ios,pc前端,微信小程序,H5等等,你不需要关心后端几百个服务,你知道有一个网关,所有的请求都走网关,网关会根据请求的一些特征,把请求转发给各个后端服务。而且有了网关之后,还有很多好处,比如统一降级、限流、认证授权、安全等等。总结最后总结一下以上SpringCloud核心组件在微服务架构中的作用:Eureka:每个服务启动时,EurekaClient会向EurekaServer注册该服务,EurekaClient也可以轮流使用。EurekaServer拉取注册表,以便它知道其他服务在哪里。Ribbon:服务之间发起请求时,基于Ribbon进行负载均衡,选择一个服务的多台机器中的一台。Feign:基于Feign的动态代理机制,根据注解和选择的机器,拼接请求URL地址并发起请求。Hystrix:通过Hystrix的线程池发起请求。不同的服务使用不同的线程池,实现了不同服务调用的隔离,避免了服务雪崩的问题。Zuul:前端和移动端如果要调用后端系统,统一通过Zuul网关进入,Zuul网关将请求转发给相应的服务。以上就是我们通过一个电商业务场景来讲解SpringCloud微服务架构的几个核心组件的底层原理。文字摘要不够直观?没问题!我们通过一张图把SpringCloud的五个核心组件串联起来,再直观感受一下底层的架构原理:
