微服务(二)——OpenFeign、Hystrix 实现服务降级(结合RedisTemplete 、处理异常、继承类中的缓存、请求合并、OpenFeign + Hystrix 服务降级)
一、OpenFeign(简化 RestTemplate)
1、介绍
前面都是通过手动调用 RestTemplate 来实现远程调用的。使用 RestTemplate 存在一个问题:繁琐,每一个请求,参数不同,请求地址不同,返回数据类型不同,其他都是一样的,所以我们希望能够对请求进行简化。
我们希望对请求进行简化,简化方案就是 OpenFeign。
RestTemplate 和 OpenFeign 的底层都是 Http 请求,OpenFeign 更方便一些。
一开始这个组件不叫这个名字,OpenFeign 一开始就叫 Feign,Netflix Feign,但是 Netflix 中的组件现在已经停止开源工作,OpenFeign 是 Spring Cloud 团队在 Netflix Feign 的基础上开发出来的声明式服务调用组件。关于 OpenFeign 组件的 Issue:https://github.com/OpenFeign/feign/issues/373
2、前期准备
SpringBoot 项目:
导入 service:
配置文件:
启动类配置注解:
3、代码实现(第一次简化)
上篇博客中使用了 RedisTemplate 实现了很多增删改查的功能,但是其实很多东西都是重复套用的,所以接下来就通过 openFeign 来简化 redisTemplete :
主要就是通过一个接口以及注解来简化 (注意:在 openFeign,这个 @RequestBody 注解是一定要加的,在 OpenFeign 里面传输的对象一定是 Json 格式的对象,所以当要传输对象的时候都要加这个注解,比如:put 方法和 post 方法。):
注意,写到这里博主发现了上篇博客的一个问题,就是 storage 部分,这里需要加个接收 Json 格式的注解:
不加这个注解返回去的对象的值是都为 null 的。
然后把接口写好以后,就写一个 接口 来调用:
然后结果:
结果都没有问题。
4、第二次代码实现(最终简化)
上面的代码其实可以再简化,因为会发现接收的 Controller 层跟发送的接口这一块好像都高度相似的,所以这里是可以合并的。
a、创建新 maven 项目为 api
所以这里是可以弄一个 maven 工程,设置一个公共的 api:
依赖准备:
然后定义 api:
b、storage 增加依赖
然后 storage 需要添加 api 的依赖:
c、storage 代码实现
然后原本的 controller 层也不用写很多注解,只需要像下面这样即可:
d、openFeign 代码实现
openFeign 添加 api 依赖:
然后修改接口层,这里接口就只需要继承即可:
e、小结
但是这样有个缺陷:
首先是 openFeign 和 storage 这两个服务之间的耦合度提高了,原本是比较散的,因为有了共同的依赖;但是这样修改了 IBookController 这个接口的话,两边都需要改。但是这样写起来就简单了。
如果是之前那种方式,耦合度没那么高的时候,修改就无所谓,各顾各的。
所以在写项目的时候,就需要考虑:
1、如果这两个服务本身耦合度就很高的话(强关联的服务),就可以使用这种继承的方式。
2、如果这两个服务只是偶尔调用一下,就没必要使用这种方式。
二、Hystrix 实现服务降级
1、前期准备
SpringBoot 项目(注意是阿里的,阿里的版本一般都偏老,可能是为了稳定。):
2、引言和介绍
Hystrix 叫做断路器/熔断器。微服务系统中,整个系统出错的概率非常高,因为在微服务系统中,涉及到的模块太多了,每一个模块出错,都有可能导致整个服务出错,当所有模块都稳定运行时,整个服务才算是稳定运行。
我们希望当整个系统中,某一个模块无法正常工作时,能够通过我们提前配置的一些东西,来使得整个系统正常运行,即单个模块出问题,不影响整个系统。
举个例子:
假设一个项目的出问题的概率是 0.0001,也就是正常运行的概率是 99.99%,这种概率是相当稳定了吧。如果有一千个项目呢?那项目正常运行的该流程就是 90.48%,如果是一万个项目呢?那就是 36.78%,可以看到此时的概率是相当的低了;虽然上到一万个服务的项目很少。所以随着项目的增加,出错的概率是上升的。所以是希望如果项目出错的话,能把错误局限在一个服务里面,不让它扩散开来,避免影响其他服务。这里用到的 Hystrix 解决的就是这种问题。
Hystrix 可以用在 RedisTemplete 和 Feign 中。
3、Hystrix 结合 RestTemplete 服务降级
a、注解的方式
首先是启动类,这里的组合注解功能相当于上面三个注解的组合(因为下面三个被注释的注解在 Hystrix 中经常使用,已经是标配了,所以用这个 @SpringCloudApplication 组合注解来代替这三个注解。):
service 层:
接口测试:
(这里可以自己进行故意出异常)效果:
注意:Hystrix 里面有个默认的等待时间,发起请求或者调用接口如果超过了这个等待时间就不会再等了,直接进行服务降级。
这里只是返回了字符串,比较简单。实际上是可以调用其他的服务;比如调用数据库不行就去调用缓存的;如果还不通,可以一直降级。
b、继承的方式
下图中的 super(…)这里是给线程取个名字,这个名字叫 zhangsan。
然后 controller 层要注入 restTemplate:
接着接口测试代码:
这里有两个方法可以调用:
一个是 execute()——执行,这个执行是同步执行,会发生阻塞。
另一个是 queue()——入队,异步执行,返回的是 Future< String >,调用 Futrue 里面的 get()方法才会返回具体的结果。
小提示:
这里其实可以在 HelloCommand 方法里传进 url 参数,然后再传给 run 方法,run 方法就可以指定跳转到这个 url 指定的地址,这样可以每次调用的地址都不同。这么一来可以在 getFallback 里面再 new一个 HelloCommand ,传入新参数,然后调用 execute 方法。或者再写个类,作为它的回调,调用 execute 方法;以此类推,可以降级很多次。不过要注意的是,上图中一个 helloCommand 对象只能执行一次 execute 或者 queue。
对上面的提示的举例:
这里可传参数:
效果:
4、Hystrix 处理异常
a、注解的方式
如果是被注解 @HystrixCommand 修饰的方法内部出了错误,希望在 error 方法获取具体的异常,可以这样:
效果:
还有种需求,就是出异常了,但是不想降级,可以在注解中添加参数即可解决:
b、继承类的方式
看下图:
效果:
5、Hystrix 请求中的缓存(看截图中的注释)
Hystrix 会自动的帮远程请求的结果缓存起来;可以通过注解来处理缓存,也可以通过继承类的方式来处理缓存。
a、注解方式
这里依然是拿 book 这个实体类作例子,所以首先是先加入依赖:
然后是 service 层代码:
接口测试代码:
测试后可以发现有两次服务降级了,但是只调用了一次。
b、继承方式
以上就是继承方式实现缓存的方式。
6、Hystrix 请求合并
a、代码实现
举个例子:比如有个按 id 查询书的接口,现在连着发送了三个请求,三个请求都是按照 id 来查询书。在调用时,只是参数不一样,那么这样情况下,我们就可以将多个请求合并成一个,然后接收以后再拆分成三个。这样本来需要发三个的现在只需要发一个。这样可以有效提高请求发送的效率。
首先是 storage 的接口:
然后是 Hystrix :
这里就是处理请求合并的类(下图中继承的类的泛型解释:第一个是批处理返回的类型是 book 的集合,第二个是单个响应的类型,第三个是请求的参数类型。):
写好了合并的类和方法以后,接着写调用接口:
然后 service 层(这里重新弄了个 service。因为接口需要传一个参数,所以这里随便传了个 1,跟本小节的内容没有关系):
然后跟之前相似的,配一下线程里面的东西,然后取名字:
效果:
接着就是测试,访问 Hystrix 的 test2 接口:
这里四个请求合并成一个:
这里是取回数据后拆分的结果:
这里可以自行测试睡眠超过 200 毫秒的结果。
b、执行流程总结
1、首先是访问 Hystrix 服务的接口,然后 Hystrix 通过 BookCollapseCommand 创建了四个请求;在BookCollapseCommand 中首先获取了传过来的参数(getRequestArgument 方法),然后通过把框架收集好的请求拆分再合并成 List 数组(createCommand 方法);
2、接着给到 BookBatchCommand ,BookBatchCommand 中的 run 方法会调用 BookService 中的方法,然后在这里面通过 restTemplate 来进行远程调用,调用到 storage 服务中的 book 接口,这个接口会返回 books 的集合,那么这里 storage 就处理完了;接着就是一路原地返回 createCommand 方法;
3、接着继续来到 BookCollapseCommand 中最后一个方法 mapResponseToRequests,这里的两个参数,一个是 bookList ,也就是 storage 中返回的 List 数组集合,还有一个是 collection,合并起来的请求都在这个集合中,接着就是把 List 中的每一个结果,也就是 book 对象,赋给每一个请求;所以这里通过循环的把每一个 book 对象赋予给每一个请求的响应结果,也就是 request.setResponse(bookList.get(index++));
4、那么接着就回到 Hystrix 服务的接口;每一个 Futrue 对象执行完 get 方法后的返回值就是 book 对象。完毕。
下图可以通过打开新标签页看的更清楚:
7、OpenFeign + Hystrix 服务降级
a、前期准备
阿里云的 SpringBoot 项目:
最后记得要导入 service 的依赖。
b、代码实现
先定义一个接口类:
然后是服务降级的类:
最后是测试接口:
效果:
然后关闭 storage 服务测试降级:
可以看到没有问题。