Zuul+Ribbon实现服务重试机制

ZUUL、Ribbon 实现服务重试机制

前言

版本简介:SpringCloud 采用Hoxton.SR8 版本,采用集群方式部署项目

zuul 是什么?

Zuul 是 Netflix 开源的一个网关组件 API Gateway 服务器,在云平台上提供动态路由,监控,弹性,安全等边缘服务的框架。Zuul 相当于是设备和 Netflix 流应用的 Web 网站后端所有请求的前门。

1、过滤器机制

zuul 的核心是一系列的 filters, 其作用可以类比 Servlet 框架的 Filter,或者 AOP。

zuul 把 Request route 到 用户处理逻辑 的过程中,这些 filter 参与一些过滤处理,比如 Authentication,Load Shedding 等。

Zuul 提供了一个框架,可以对过滤器进行动态的加载,编译,运行。

Zuul 的过滤器之间没有直接的相互通信,他们之间通过一个 RequestContext 的静态类来进行数据传递的。RequestContext 类中有 ThreadLocal 变量来记录每个 Request 所需要传递的数据。

Zuul 的过滤器是由 Groovy 写成,这些过滤器文件被放在 Zuul Server 上的特定目录下面,Zuul 会定期轮询这些目录,修改过的过滤器会动态的加载到 Zuul Server 中以便过滤请求使用。

(1) PRE:这种过滤器在请求被路由之前调用。我们可利用这种过滤器实现身份验证、在集群中选择请求的微服务、记录调试信息等。

(2) ROUTING:这种过滤器将请求路由到微服务。这种过滤器用于构建发送给微服务的请求,并使用 Apache HttpClient 或 Netfilx Ribbon 请求微服务。

(3) POST:这种过滤器在路由到微服务以后执行。这种过滤器可用来为响应添加标准的 HTTP Header、收集统计信息和指标、将响应从微服务发送给客户端等。

(4) ERROR:在其他阶段发生错误时执行该过滤器。

内置的特殊过滤器

zuul 还提供了一类特殊的过滤器,分别为:StaticResponseFilter 和 SurgicalDebugFilter

StaticResponseFilter:StaticResponseFilter 允许从 Zuul 本身生成响应,而不是将请求转发到源。

SurgicalDebugFilter:SurgicalDebugFilter 允许将特定请求路由到分隔的调试集群或主机。

自定义的过滤器

除了默认的过滤器类型,Zuul 还允许我们创建自定义的过滤器类型。

例如,我们可以定制一种 STATIC 类型的过滤器,直接在 Zuul 中生成响应,而不将请求转发到后端的微服务。

问题引领

系统简单拓扑图如下

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-imfSL1nA-1605865781408)(C:\Users\stone\AppData\Roaming\Typora\typora-user-images\image-20201120170327202.png)]

当其中有一个serve异常时,eureka 并不会及时的将server从注册中心剔除,而是默认会等待其恢复正常,这样,当请求通过网关时,根据负载均衡策略,可能会被分发到已经处于异常的server上,导致系统出错,不能保证系统的高可用。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0dEfVuHY-1605865781410)(C:\Users\stone\AppData\Roaming\Typora\typora-user-images\image-20201120170850287.png)]

Ribbon 为我们提供了一个重试机制,当发现服务不可用时,首先会根据指定的次数进行尝试,如果继续不行,就会切换server…如果最后一个server也不可用,则会返回错误。

Ribbon 配置参数


ribbon:
  ConnectTimeout: 1000   #连接超时时间
  ReadTimeout: 1000     # Ribbon的数据读取超时时间
  MaxAutoRetries: 1     #  本次实例的重试次数  即失败之后,重试几次 
  MaxAutoRetriesNextServer: 2   # 切换实例的重试次数, 即发生错误之后,切换几次实例
  OkToRetryOnAllOperations: true  # 是否对所有操作都进行重试 

需要引入依赖包

<dependency>
            <groupId>org.springframework.retry</groupId>
            <artifactId>spring-retry</artifactId>
</dependency>

在实际的操作过程中,有多种导致ribbon的重试机制不起效果,总结原因如下:

1.ribbon 和hystrix 超时时间不合理

hystrix:
  command:
    default:
      execution:
        isolation:
          thread:
            timeoutInMilliseconds: 130000

ribbon:
  #NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RoundRobinRule  
  ConnectTimeout: 1000
  ReadTimeout: 1000
  MaxAutoRetries: 1
  MaxAutoRetriesNextServer: 2
  OkToRetryOnAllOperations: true
  
  # hystrix 的超时时间必须大于ribbon 的超时时间,不然重试机制不会触发, 规则为 timeoutInMilliseconds > (ConnectTimeout + ReadTimeout) * (MaxAutoRetries+1)*(MaxAutoRetriesNextServer+1)

2.zuul 没有启用重试机制

zuul:
  sensitive-headers:
  routes:
    #标识你服务的名字,这里可以自己定义,一般方便和规范来讲还是跟自己服务的名字一样
    stone-zuul:
      #服务映射的路径,通过这路径就可以从外部访问你的服务了,目的是为了不爆露你机器的IP,面向服务的路由了,给你选一个可用的出来,
      #这里zuul是自动依赖hystrix,ribbon的,不是面向单机
      path: /api/**
      #这里一定要是你Eureka注册中心的服务的名称,是所以这里配置serviceId因为跟eureka结合了,如果单独使用zuul,那么就必须写自己机器的IP了,
      #如url:http://localhost:8080/  这样的不好就是写死IP了,万一这IP挂了,这高可用性,服务注册那套东西就用不起来了
      serviceId: stone-server
  retryable : true   ##这个一定要加

3.这几个参数配置已经默认

spring
  cloud:
    loadbalancer:
      retry:
        enabled: true # 开启Spring Cloud的重试功能

ribbon:
 # ConnectTimeout: 1000
 # ReadTimeout: 1000
 # MaxAutoRetries: 1
 # MaxAutoRetriesNextServer: 2
 # OkToRetryOnAllOperations: true
  restclient:
    enabled: true

问题:

在启用重试机制之后,保证了系统的高可用性,但是在某一服务异常的一段时间内,请求的速度会大幅下降,对于并大量较大的请求并不会太友好。

附:完成的pom文件 以及 yaml配置文件

server:
  port: 8403
  tomcat:
    uri-encoding: UTF-8
  servlet:
    encoding:
      enabled: true
      force: true
      charset: UTF-8

spring:
  application:
    name: stone-zuul
  servlet:
    multipart:
      max-file-size: 200MB
      max-request-size: 200MB
#  cloud:
#    loadbalancer:
#      retry:
#        enabled: true # 开启Spring Cloud的重试功能
zuul:
  sensitive-headers:
  routes:
    #标识你服务的名字,这里可以自己定义,一般方便和规范来讲还是跟自己服务的名字一样
    stone-zuul:
      #服务映射的路径,通过这路径就可以从外部访问你的服务了,目的是为了不爆露你机器的IP,面向服务的路由了,给你选一个可用的出来,
      #这里zuul是自动依赖hystrix,ribbon的,不是面向单机
      path: /api/**
      #这里一定要是你Eureka注册中心的服务的名称,是所以这里配置serviceId因为跟eureka结合了,如果单独使用zuul,那么就必须写自己机器的IP了,
      #如url:http://localhost:8080/  这样的不好就是写死IP了,万一这IP挂了,这高可用性,服务注册那套东西就用不起来了
      serviceId: stone-server
  retryable : true

eureka:
  #客户端
  client:
    #注册中心地址
    service-url:
      defaultZone: http://127.0.0.1:8500/eureka/,http://127.0.0.1:8600/eureka/

#  断路器的超时时间需要大于ribbon的超时时间,不然不会触发重试
hystrix:
  command:
    default:
      execution:
        isolation:
          thread:
            timeoutInMilliseconds: 130000

ribbon:
  ConnectTimeout: 1000
  ReadTimeout: 1000
  MaxAutoRetries: 1
  MaxAutoRetriesNextServer: 2
  OkToRetryOnAllOperations: true
#  restclient:
#    enabled: true
#随机式得负载均衡
#stone-server:
#  ribbon:
#    NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RoundRobinRule
#    ConnectTimeout: 250 # Ribbon的连接超时时间
#    ReadTimeout: 1000 # Ribbon的数据读取超时时间
#    OkToRetryOnAllOperations: true # 是否对所有操作都进行重试
#    MaxAutoRetriesNextServer: 3 # 切换实例的重试次数
#    MaxAutoRetries: 1 # 失败对当前实例的重试次数


<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.3.4.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.stoneppy</groupId>
    <artifactId>ec-zuul</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>war</packaging>
    <name>ec-zuul</name>
    <description>Demo project for Spring Boot</description>

    <properties>
        <java.version>1.8</java.version>
        <spring-cloud.version>Hoxton.SR8</spring-cloud.version>
    </properties>

    <dependencies>

        <dependency>
            <groupId>org.springframework.retry</groupId>
            <artifactId>spring-retry</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-loadbalancer</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-zuul</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-tomcat</artifactId>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
            <exclusions>
                <exclusion>
                    <groupId>org.junit.vintage</groupId>
                    <artifactId>junit-vintage-engine</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
    </dependencies>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>${spring-cloud.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

    </dependencies>
</dependencyManagement>

<build>
    <plugins>
        <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
        </plugin>
    </plugins>
</build>

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
要在Zuul中使用WebSocket和SockJS,您需要进行以下配置: 1. 添加依赖项 ```xml <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-zuul</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-hystrix</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-zuul-websocket</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-websocket</artifactId> </dependency> ``` 2. 配置Zuul路由 ```yml zuul: routes: websocket: path: /websocket/** url: ws://localhost:8081 ``` 这将把所有以“/websocket”开头的请求路由到WebSocket服务器上。 3. 配置SockJS ```java @Configuration @EnableWebSocketMessageBroker public class WebSocketConfig extends AbstractWebSocketMessageBrokerConfigurer { @Override public void configureMessageBroker(MessageBrokerRegistry config) { config.enableSimpleBroker("/topic"); config.setApplicationDestinationPrefixes("/app"); } @Override public void registerStompEndpoints(StompEndpointRegistry registry) { registry.addEndpoint("/websocket").setAllowedOrigins("*").withSockJS(); } } ``` 这将配置一个SockJS端点,它将处理所有以“/websocket”开头的请求,并使用简单的代理模式将消息转发到“/topic”目的地。 4. 启用Zuul ```java @SpringBootApplication @EnableZuulProxy public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } } ``` 这将启用Zuul代理,并将它们路由到相应的WebSocket服务器和SockJS端点。 现在,您应该可以在Zuul中使用WebSocket和SockJS了。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值