Spring Cloud 微服务实践 (2) - Gateway 重试机制
本文对上一篇的代码进行重构,并使用Spring Cloud Gateway的重试机制,解决部分服务实例宕机导致服务不可用的问题。
在上一篇里我们遗留了一个问题没有解决,就是部署了多个实例的服务在部分实例罢工后,会出错并直接把错误丢到终端用户面前,体验一点都不友好。理想的状态应该是终端用户对内部实例的“动荡”毫无知觉,也就是运维时做迁移或者升级,不需要通知(打扰)用户,7X24小时提供服务。
本文的源代码: https://github.com/xiaoboey/from-zero-to-n/tree/master/one ,Spring Boot的版本是2.3.3,Spring Cloud的版本是Hoxton.SR8。
1、重构pom.xml
先重构一下pom.xml,前面都是用Spring Initializr单独初始化的项目,在pom.xml上没有体现协作精神。
Maven可以通过modules把项目的多个模块聚合到一起,形成一个整体,所以我们增加一个pom.xml文件,把eureka-server、gateway和service-one聚合在一起。并且把一部分相同的配置都提炼上来,子项目通过parent共享这些配置。
修改子项目的Maven配置(pom.xml),这里以eureka-server为例,其他以此类推。用parent指向父级项目,就是前面配置的聚合pom.xml。另外artifactId我也做了调整,主要是为了跟文章一一对应,代码也是复制过来做的修改,不是在原来的代码上重构。
Maven配置文件(pom.xml)的重构到此结束,现在有了父级聚合pom.xml,编译打包的话就可以直接在这里统一进行,不用分别进到各个子项目下去编译打包。
Maven项目如果做了模块聚合,并且子项目有依赖父级项目或者其他平级项目,建议都用这种方式进行总体的编译打包,特别是依赖项目代码有修改,或者jar包依赖调整等情况都建议总体编译一次,不然直接进入子项目打包可能会报一些莫名其妙的错误。用IDE也是一样,也建议经常用cmd或者shell方式整体编译,这不是什么大招,但可能会少让你少掉些头发。
2、网关增加重试机制
重试,在生活中有很多例子,比如打电话,对方占线的话就过一会再打,对方关机也是隔断时间再试试。拜访某位大佬,去的时候对方没在,那就下一次。
Spring Cloud Gateway的重试机制也类似,就是某个实例没响应就多试试,并且Gateway的重试机制比较完善,可以设定次数,还可以转移,调用别的实例进行重试。说到这里,我们会发现Gateway的重试机制正好可以解决我们的实例宕机问题。
当某个实例宕机(失联)后,Gateway因为还没有收到Eureka Server的通知,不知道这个实例已经罢工了,还是继续转发请求过去,出错或者超时?那就再试一次,还不行就换一个实例。
修改application.yml,增加重试配置:
StripPrefix:去掉前缀,这里设置为1表示去掉一级前缀,也就是把/service-one去掉,比如/service-one/hello这个请求,去掉一级前缀转发到service-one的实例时,请求的路径就是“/hello”;
retries:重试次数,默认重试3次;
series:哪些状态码才会进行重试,默认值是SERVER_ERROR,可以配置多个,其他的取值是INFORMATIONAL, SUCCESSFUL, REDIRECTION, CLIENT_ERROR;
methods:哪些方法需要进行重试,默认值是GET,就是Http的那些Method,还有HEAD, POST, PUT, PATCH, DELETE, OPTIONS等
exceptions:指定哪些异常需要进行重试逻辑,默认值是java.io.IOException。(上面配置里的exceptions配置是多余的,因为设置的值跟默认值一样,没有意义,这里只是为了列出来讲一下)
这里有个小遗憾,重试只能针对具体的service配置,所以网关的自动配置(上面配置中加#注释的那些内容)不能并存,我查了一些资料没找到解决办法。
上面说的小遗憾,Spring Cloud为什么不支持呢?可能是因为有些Method并不适合重试,能重试的Method要满足幂等性,任意多次调用产生的影响均与一次调用的影响相同。从重试的配置也可以看出来,默认是对IO异常进行重试,已经避开了很多坑。我们可以设想一下,如果对一个不幂等又耗时的方法进行超时重试,会产生什么结果呢?数据错误和拖垮系统都不在话下!
3、编译测试
试试看能不能解决服务宕机的问题
把网关Gateway的重试机制配置好之后,编译并启动起来。然后确保eureka-server也已启动,再启动多个service-one实例,还是用开启新的cmd或者shell,执行“mvn spring-boot:run”命令的方式启动新实例。
访问http://localhost:8080/service-one/getPort并反复刷新,直到返回的值出现新启动实例的端口,表示新实例已经注册到Eureka Server,并且Gateway已经拿到新实例的信息并转发请求。终止一个service-one实例,再反复刷新getPort,看看是什么效果?是不是getPort不再报错?
版权声明: 本文为 InfoQ 作者【xiaoboey】的原创文章。
原文链接:【http://xie.infoq.cn/article/cd424bd23643af21a0c2eec42】。文章转载请联系作者。
评论