007 SpringCloud_Ribbon Retry重试策略详解(一)

Spring Cloud Ribbon Retry机制

        当使用Spring Cloud Ribbon实现客户端负载均衡的时候,通常都会利用@LoadBalanced来让RestTemplate具备客户端负载功能,从而实现面向服务名的接口访问。

大多数情况下,上面的实现没有任何问题,但是总有一些意外发生,比如:有一个实例发生了故障而该情况还没有被服务治理机制及时的发现和摘除,这时候客户端访问该节点的时候自然会失败。所以,为了构建更为健壮的应用系统,我们希望当请求失败的时候能够有一定策略的重试机制,而不是直接返回失败。这个时候就需要开发人员人工的来为上面的RestTemplate调用实现重试机制。

不过,从Spring Cloud Camden SR2版本开始,我们就不用那么麻烦了。从该版本开始,Spring Cloud整合了Spring Retry来实现重试逻辑,而对于开发者只需要做一些配置即可。

简单配置ribbonretry重试机制:

对于ConnectTimeoutReadTimeout这两个配置,底层代码似乎有bug,代码发现并没有对超时处理进行生效,所以我建议使用RestTemplate原始配置即可。

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

模拟原生retry:

1.添加依赖(pom):

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <!-- 标示这个工程是一个服务,需要引入此jar -->
      <dependency>
          <groupId>org.springframework.cloud</groupId>
          <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
      </dependency>
      <!-- 动态刷新的一个模块jar -->
      <dependency>
          <groupId>org.springframework.boot</groupId>
          <artifactId>spring-boot-starter-actuator</artifactId>
      </dependency>
      <!-- 引入ribbon -->
      <dependency>
          <groupId>org.springframework.cloud</groupId>
          <artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
      </dependency>
      <!-- 引入spring原生retry -->
      <dependency>
          <groupId>org.springframework.retry</groupId>
          <artifactId>spring-retry</artifactId>
      </dependency>
      <!-- 目前版本需要引入aspectj,否则启动异常 -->
      <!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver -->
    <dependency>
        <groupId>org.aspectj</groupId>
        <artifactId>aspectjweaver</artifactId>
    </dependency>
      
</dependencies>
<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-dependencies</artifactId>
            <version>Edgware.SR4</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>
<build>
    <finalName>spring-cloud-02-retry</finalName>
    <plugins>
        <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
            <configuration>
                <mainClass>com.cc.springcloud.Application</mainClass>
            </configuration>
        </plugin>
    </plugins>
</build>

</project>

2.此次使用application.yml方式配置:

spring:
  application:
    name: retry-service

server:
  context-path: /
  port: 7004
  
eureka:
  client:
    service-url:
      defaultZone: http://eureka1:8001/eureka

3.创建UserService:

package com.cc.springcloud.service;

import org.springframework.remoting.RemoteAccessException;
import org.springframework.retry.annotation.Backoff;
import org.springframework.retry.annotation.Recover;
import org.springframework.retry.annotation.Retryable;
import org.springframework.stereotype.Service;

@Service
public class UserService {
    
    /**
     * 需要进行重试的方法
     *加@Retryabl注解
     *value= {}数组,可以添加多个参数,(如果重试失败会根据具体异常来调用具体降级方法)
     *RemoteAccessException.class远程方法调用异常时重试
     */
    //@Retryable(value= {RemoteAccessException.class}, //需要在捕获什么异常的情况下进行重试
    @Retryable(value= {RemoteAccessException.class,NullPointerException.class},
            maxAttempts = 3,    //重试次数
            //重试策略,通过@Backoff注解加入相关策略:delay=4000重试间隔时间(重试延迟时间)为4秒,线程数:multiplier = 1单线程;
            backoff=@Backoff(delay=4000,multiplier = 1) 
    )
    public void call() throws Exception {
        System.out.println("do something.....");
        //抛出异常模拟重试
        //throw new RemoteAccessException("Rpc调用异常.....");
        throw new NullPointerException("空指针异常.....");
    }
    
    /**
     * 如果依照规定的重试策略,重试3次依然失败
     * 启动retry提供的降级策略;
     * recover(RemoteAccessException e)参数即重试策略的条件,参数必须匹配
     */
    @Recover
    public void recover(RemoteAccessException e) {
        System.out.println("最终处理结果1:"+e.getMessage());
    }
    //参数与重试策略不对应不会运行
    //@Retryable(value= {RemoteAccessException.class,NullPointerException.class}, 如果添加入参数数组,就可运行降级
    @Recover
    public void recover(NullPointerException e) {
        System.out.println("最终处理结果2:"+e.getMessage());
    }
}
 

4.创建Application:

package com.cc.springcloud;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.retry.annotation.EnableRetry;

@EnableDiscoveryClient   //标示是一个具体的服务,需要向注册中心注册
@SpringBootApplication   //springboot 核心配置
@EnableRetry             //使用spring原生retry必须开启此注解,即开启重试
public class Application {

    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}
 

5.创建测试用例:

package com.cc.springcloud.service;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

import com.cc.springcloud.Application;

//单元测试
@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(classes = Application.class)
public class RetryTests {
    @Autowired
    private UserService userService;
    
    @Test
    public void retryTest() throws Exception {
        userService.call();
    }
}
 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值