SpringCloud底层服务之间是怎么相互调⽤的?
⽂章⽬录
概述
毫⽆疑问,Spring Cloud是⽬前微服务架构领域的翘楚,⽆数的书籍博客都在讲解这个技术。不过⼤多数讲解还停留在对Spring Cloud功能使⽤的层⾯,其底层的很多原理,很多⼈可能并不知晓。因此本⽂将通过⼤量的⼿绘图,给⼤家谈谈Spring Cloud微服务架构的底层原理。
实际上,Spring Cloud是⼀个全家桶式的技术栈,包含了很多组件。本⽂先从其最核⼼的⼏个组件⼊⼿,来剖析⼀下其底层的⼯作原理。也就是Eureka、Ribbon、Feign、Hystrix、Zuul这⼏个组件。
⼀、业务场景介绍
先来给⼤家说⼀个业务场景,假设咱们现在开发⼀个电商⽹站,要实现⽀付订单的功能,流程如下:
创建⼀个订单之后,如果⽤户⽴刻⽀付了这个订单,我们需要将订单状态更新为“已⽀付”
扣减相应的商品库存
通知仓储中⼼,进⾏发货
给⽤户的这次购物增加相应的积分
针对上述流程,我们需要有订单服务、库存服务、仓储服务、积分服务。整个流程的⼤体思路如下:
spring系列框架有哪些⽤户针对⼀个订单完成⽀付之后,就会去订单服务,更新订单状态
订单服务调⽤库存服务,完成相应功能
订单服务调⽤仓储服务,完成相应功能
订单服务调⽤积分服务,完成相应功能
⾄此,整个⽀付订单的业务流程结束
下图这张图,清晰表明了各服务间的调⽤过程:
好!有了业务场景之后,咱们就⼀起来看看Spring Cloud微服务架构中,这⼏个组件如何相互协作,各⾃发挥的作⽤以及其背后的原理。
⼆、Spring Cloud核⼼组件:Eureka
咱们来考虑第⼀个问题:订单服务想要调⽤库存服务、仓储服务,或者是积分服务,怎么调⽤?
订单服务压根⼉就不知道⼈家库存服务在哪台机器上啊!他就算想要发起⼀个请求,都不知道发送给谁,有⼼⽆⼒!
这时候,就轮到Spring Cloud Eureka出场了。Eureka是微服务架构中的注册中⼼,专门负责服务的注册与发现。
咱们来看看下⾯的这张图,结合图来仔细剖析⼀下整个流程:
如上图所⽰,库存服务、仓储服务、积分服务中都有⼀个Eureka Client组件,这个组件专门负责将这个服务的信息注册到Eureka Server 中。说⽩了,就是告诉Eureka Server,⾃⼰在哪台机器上,监听着哪个端⼝。⽽Eureka Server是⼀个注册中⼼,⾥⾯有⼀个注册表,保存了各服务所在的机器和端⼝号。
订单服务⾥也有⼀个Eureka Client组件,这个Eureka Client组件会Eureka Server问⼀下:库存服务在哪台机器啊?监听着哪个端⼝啊?仓储服务呢?积分服务呢?然后就可以把这些相关信息从Eureka Server的注册表中拉取到⾃⼰本地缓存起来。
这时如果订单服务想要调⽤库存服务,不就可以⾃⼰本地的Eureka Client问⼀下库存服务在哪台机器?监听哪个端⼝吗?收到响应后,紧接着就可以发送⼀个请求过去,调⽤库存服务扣减库存的那个接⼝!同理,如果订单服务要调⽤仓储服务、积分服务,也是如法炮制。
总结⼀下:
Eureka Client:负责将这个服务的信息注册到Eureka Server中
Eureka Server:注册中⼼,⾥⾯有⼀个注册表,保存了各个服务所在的机器和端⼝号
三、Spring Cloud核⼼组件:Feign
现在订单服务确实知道库存服务、积分服务、仓库服务在哪⾥了,同时也监听着哪些端⼝号了。但是新问题⼜来了:难道订单服务要⾃⼰写⼀⼤堆代码,跟其他服务建⽴⽹络连接,然后构造⼀个复杂的请求,接着发送请求过去,最后对返回的响应结果再写⼀⼤堆代码来处理吗?
这是上述流程翻译的代码⽚段,咱们⼀起来看看,体会⼀下这种绝望⽽⽆助的感受
友情提⽰,前⽅⾼能:
看完上⾯那⼀⼤段代码,有没有感到后背发凉、⼀⾝冷汗?实际上你进⾏服务间调⽤时,如果每次都⼿写代码,代码量⽐上⾯那段要多⾄少⼏倍,所以这个事⼉压根⼉就不是地球⼈能⼲的。
既然如此,那怎么办呢?别急,Feign早已为我们提供好了优雅的解决⽅案。来看看如果⽤Feign的话,你的订单服务调⽤库存服务的代码会变成啥样?
看完上⾯的代码什么感觉?是不是感觉整个世界都⼲净了,⼜到了活下去的勇⽓!没有底层的建⽴连接、构造请求、解析响应的代码,直接就是⽤注解定义⼀个 FeignClient接⼝,然后调⽤那个接⼝就可以了。⼈家Feign Client会在底层根据你的注解,跟你指定的服务建⽴连接、构造请求、发起靕求、获取响应、解析响应,等等。这⼀系列脏活累活,⼈家Feign全给你⼲了。
那么问题来了,Feign是如何做到这么神奇的呢?很简单,Feign的⼀个关键机制就是使⽤了动态代理。咱们⼀起来看看下⾯的图,结合图来分析:
⾸先,如果你对某个接⼝定义了@FeignClient注解,Feign就会针对这个接⼝创建⼀个动态代理
接着你要是调⽤那个接⼝,本质就是会调⽤ Feign创建的动态代理,这是核⼼中的核⼼
Feign的动态代理会根据你在接⼝上的@RequestMapping等注解,来动态构造出你要请求的服务的地址
最后针对这个地址,发起请求、解析响应
四、Spring Cloud核⼼组件:Ribbon
说完了Feign,还没完。现在新的问题⼜来了,如果⼈家库存服务部署在了5台机器上,如下所⽰:
192.168.169:9000
192.168.170:9000
192.168.171:9000
192.168.172:9000
192.168.173:9000
这下⿇烦了!⼈家Feign怎么知道该请求哪台机器呢?
这时Spring Cloud Ribbon就派上⽤场了。Ribbon就是专门解决这个问题的。它的作⽤是负载均衡,会帮你在每次请求时选择⼀台机器,均匀的把请求分发到各个机器上
Ribbon的负载均衡默认使⽤的最经典的Round Robin轮询算法。这是啥?简单来说,就是如果订单服务对库存服务发起10次请求,那就先让你请求第1台机器、然后是第2台机器、第3台机器、第4台机器、第5台机器,接着再来—个循环,第1台机器、第2台机器。
。。以此类推。
此外,Ribbon是和Feign以及Eureka紧密协作,完成⼯作的,具体如下:
⾸先Ribbon会从 Eureka Client⾥获取到对应的服务注册表,也就知道了所有的服务都部署在了哪些机器上,在监听哪些端⼝号。
然后Ribbon就可以使⽤默认的Round Robin算法,从中选择⼀台机器
Feign就会针对这台机器,构造并发起请求。
对上述整个过程,再来⼀张图,帮助⼤家更深刻的理解:
五、Spring Cloud核⼼组件:Hystrix
在微服务架构⾥,⼀个系统会有很多的服务。以本⽂的业务场景为例:订单服务在⼀个业务流程⾥需要调⽤三个服务。现在假设订单服务⾃⼰最多只有100个线程可以处理请求,然后呢,积分服务不幸的挂了,每次订单服务调⽤积分服务的时候,都会卡住⼏秒钟,然后抛出—个超时异常。
咱们⼀起来分析⼀下,这样会导致什么问题?
1. 如果系统处于⾼并发的场景下,⼤量请求涌过来的时候,订单服务的100个线程都会卡在请求积分服务这块。导致订单服务没有⼀个
线程可以处理请求
2. 然后就会导致别⼈请求订单服务的时候,发现订单服务也挂了,不响应任何请求了