毫无疑问,SpringCloud是目前微服务架构领域的佼佼者,无数的书籍和博客都在讲解这项技术。但是大部分的讲解还停留在使用SpringCloud功能的层面,很多人可能并不清楚底层原理。因此,本文将通过大量的手绘图来讲述SpringCloud微服务架构的底层原理。事实上,SpringCloud是一个包含很多组件的全家桶技术栈。本文从核心组件Eureka、Ribbon、Feign、Hystrix、Zuul入手,分析其底层工作原理。业务场景介绍先给大家讲一个业务场景。假设我们现在正在开发一个电子商务网站,想要实现支付订单的功能。流程如下:创建订单后,如果用户立即为订单付款,我们需要将订单状态更新为“已付款”。扣除相应的商品存货。通知仓库中心发货。为用户的购物添加相应的积分。对于以上流程,我们需要订单服务、库存服务、仓储服务、积分服务。整个流程的大致思路是这样的:用户完成订单支付后,会去订单服务更新订单状态。订单服务调用库存服务完成相应的功能。订单服务调用仓库服务完成相应的功能。订单服务调用积分服务完成相应的功能。至此,整个支付订单的业务流程结束。下图清晰的展示了服务之间的调用过程:OK!有了业务场景,我们再来看看这些组件是如何相互协作的,它们各自的作用以及SpringCloud微服务架构中的其他组件。其背后的基本原理。SpringCloud核心组件:Eureka先考虑第一个问题:订单服务要调用库存服务、仓储服务、积分服务,怎么调用?订单服务甚至不知道别人的库存服务在哪台机器上!就算它要发起请求,也不知道发给谁,也没有权力!这时候就轮到SpringCloudEureka上场了。Eureka是微服务架构中的注册中心,负责服务的注册和发现。我们来看下图,结合图片仔细分析一下整个过程:如上图所示,库存服务、仓储服务、积分服务中都有一个EurekaClient组件,负责将此服务的信息注册到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隔离、熔断、降级的全过程:SpringCloud核心组件:Zuul说完Hystrix,再给大家说说最后一个组件:Zuul微服务网关。该组件负责网络路由。不懂网络路由?好吧,我告诉你,如果没有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的五个核心组件,然后直观感受底层架构原理:中华石山:BAT架构十年以上经验,一线互联网公司技术总监。带领数百人团队开发过亿级大流量高并发系统。多年工作积累的研究手稿和经验总结,现整理成文,一一传授。
