• 微服务是什么?它的优缺点有哪些?
  • Spring Boot简介
  • Spring Boot项目搭建步骤(超详细)
  • 使用Eureka编写服务提供者
  • 使用Eureka编写服务消费者
  • Eureka注册中心开启密码认证
  • Spring Cloud使用Eureka集群搭建实现高可用服务注册中心
  • Eureka自我保护模式和InstanceID的配置
  • Eureka开发时快速移除失效服务
  • Eureka开发时快速移除失效服务
  • Spring Cloud Ribbon(负载均衡器)介绍及使用
  • Spring Cloud Ribbon结合RestTemplate实现负载均衡
  • Spring Cloud Ribbon配置详解
  • Spring Cloud使用Feign调用服务接口
  • Spring Cloud Feign的自定义配置及使用
  • Spring Cloud Hystrix缓存与合并请求
  • Spring Cloud Zuul网关的介绍及使用
  • Spring Cloud Zuul过滤器介绍及使用(传递数据、拦截请求和异常处理)
  • Spring Cloud使用Zuul实现容错回退功能
  • Spring Cloud Zuul请求响应信息输出
  • Spring Cloud实现Zuul自带的Debug功能
  • Spring Cloud Gateway整合Eureka路由转发
  • Spring Cloud Gateway的常用路由断言工厂
  • Spring Cloud Gateway过滤器工厂的使用
  • Spring Cloud Gateway全局过滤器(GlobalFilter)
  • Smconf(分布式配置管理框架)概述
  • Apollo(分布式配置中心)核心概念及核心功能介绍
  • Apollo本地部署详细步骤
  • Apollo Portal管理后台的使用
  • Apollo高可用设计分析
  • Spring Cloud使用Sleuth在应用中进行日志跟踪
  • Spring Cloud Sleuth与ELK(日志分析系统)配合使用
  • Spring Cloud整合Zipkin进行服务跟踪
  • JWT(Json Web Token)是什么?
  • Spring Cloud基于JWT创建统一的认证服务
  • Zuul中传递Token到路由的服务中
  • Spring Boot Admin的介绍及使用
  • Swagger是什么?Swagger怎么用?
  • 使用Zuul聚合多个微服务的Swagger文档
  • 微服务架构下如何获取用户信息并认证?
  • 服务降级是什么?Spring Cloud如何实现?
  • Guava Cache本地缓存介绍及使用
  • 防止缓存雪崩的方案
  • Spring Cloud使用Zuul实现容错回退功能

    Zuul 主要功能就是转发,在转发过程中我们无法保证被转发的服务是可用的,这个时候就需要容错机制及回退机制。

    容错机制

    容错,简单来说就是当某个服务不可用时,能够切换到其他可用的服务上去,也就是需要有重试机制。在 Zuul 中开启重试机制需要依赖 spring-retry。

    首先在 pom.xml 中添加 spring-retry 的依赖,代码如下所示。

    1. <dependency>
    2. <groupId>org.springframework.retry</groupId>
    3. <artifactId>spring-retry</artifactId>
    4. </dependency>

    在属性文件中开启重试机制以及配置重试次数:

    1. zuul.retryable=true
    2. ribbon.connectTimeout=500
    3. ribbon.readTimeout=5000
    4. ribbon.maxAutoRetries=1
    5. ribbon.maxAutoRetriesNextServer=3
    6. ribbon.okToRetryOnAllOperations=true
    7. ribbon.retryableStatusCodes=500,404,502

    其中:

    • zuul.retryable:开启重试。
    • ribbon.connectTimeout:请求连接的超时时间(ms)。
    • ribbon.readTimeout:请求处理的超时时间(ms)。
    • ribbon.maxAutoRetries:对当前实例的重试次数。
    • ribbon.maxAutoRetriesNextServer:切换实例的最大重试次数。
    • ribbon.okToRetryOnAllOperations:对所有操作请求都进行重试。
    • ribbon.retryableStatusCodes:对指定的 Http 响应码进行重试。

    可以启动两个 hystrix-feign-demo 服务,默认 Ribbon 的转发规则是轮询,然后我们停掉一个 hystrix-feign-demo 服务。没加重试机制之前,当你请求接口的时候肯定有一次是会被转发到停掉的服务上去的,返回的是异常信息。

    当我们加入了重试机制后,你可以循环请求接口,这个时候不会返回异常信息,因为 Ribbon 会根据重试配置进行重试,当请求失败后会将请求重新转发到可用的服务上去。

    回退机制

    在 Spring Cloud 中,Zuul 默认整合了 Hystrix,当后端服务异常时可以为 Zuul 添加回退功能,返回默认的数据给客户端。

    实现回退机制需要实现 ZuulFallbackProvider 接口,代码如下所示。

    1. @Component
    2. public class ServiceConsumerFallbackProvider implements ZuulFallbackProvider {
    3. private Logger log = LoggerFactory.getLogger(ServiceConsumerFallbackProvider.class);
    4. @Override
    5. public String getRoute() {
    6. return "*";
    7. }
    8. @Override
    9. public ClientHttpResponse fallbackResponse(String route, Throwable cause) {
    10. return new ClientHttpResponse() {
    11. @Override
    12. public HttpStatus getStatusCode() throws IOException {
    13. return HttpStatus.OK;
    14. }
    15. @Override
    16. public int getRawStatusCode() throws IOException {
    17. return this.getStatusCode().value();
    18. }
    19. @Override
    20. public String getStatusText() throws IOException {
    21. return this.getStatusCode().getReasonPhrase();
    22. }
    23. @Override
    24. public void close() {
    25. }
    26. @Override
    27. public InputStream getBody() throws IOException {
    28. if (cause != null) {
    29. log.error("", cause.getCause());
    30. }
    31. RequestContext ctx = RequestContext.getCurrentContext();
    32. ResponseData data = ResponseData.fail("服务器内部错误 ", ResponseCode.SERVER_ERROR_CODE.getCode());
    33. return new ByteArrayInputStream(JsonUtils.toJson(data).getBytes());
    34. }
    35. @Override
    36. public HttpHeaders getHeaders() {
    37. HttpHeaders headers = new HttpHeaders();
    38. MediaType mt = new MediaType("application", "json", Charset.forName("UTF-8"));
    39. headers.setContentType(mt);
    40. return headers;
    41. }
    42. };
    43. }
    44. }

    getRoute 方法中返回*表示对所有服务进行回退操作,如果只想对某个服务进行回退,那么就返回需要回退的服务名称,这个名称一定要是注册到 Eureka 中的名称。

    通过 ClientHttpResponse 构造回退的内容。通过 getStatusCode 返回响应的状态码。通过 getStatusText 返回响应状态码对应的文本。通过 getBody 返回回退的内容。通过 getHeaders 返回响应的请求头信息。

    通过 API 网关来访问 hystrix-feign-demo 服务,将 hystrix-feign-demo 服务停掉,然后再次访问,就可以看到回退的内容了,如图 1 所示。

    运行结果
    图 1 运行结果

    Zuul 高可用

    跟业务相关的服务我们都是注册到 Eureka 中,通过 Ribbon 来进行负载均衡,服务可以通过水平扩展来实现高可用。

    现实使用中,API 网关这层往往是给 APP、Webapp、客户来调用接口的,如果我们将 Zuul 也注册到 Eureka 中是达不到高可用的,因为你不可能让你的客户也去操作你的注册中心。

    这时最好的办法就是用额外的负载均衡器来实现 Zuul 的高可用,比如我们最常用的 Nginx,或者 HAProxy、F5 等。

    这种方式也是单体项目最常用的负载方式,当用户请求一个地址的时候,通过 Nginx 去做转发,当一个服务挂掉的时候,Nginx 会把它排除掉。

    如果想要 API 网关也能随时水平扩展,那么我们可以用脚本来动态修改 Nginx 的配置,通过脚本操作 Eureka,发现有新加入的网关服务或者下线的网关服务,直接修改 Nginx 的 upstream,然后通过重载(reload)配置来达到网关的动态扩容。

    如果不用脚本结合注册中心去做的话,就只能提前规划好 N 个节点,然后手动配置上去。