SpringCloud2.0.1--Ribbon 重试监听

在SpringCloud中,通常使用的是客服端发现作为负载均衡。SpringCloud全家桶中提供了Ribbon作为客户端负载的优秀框架。
服务器之间的调用直接使用Feign进行调用。Feign默认使用了Ribbon作为负载的实现。
通常来说,进行服务拆分后,每个服务之间都需要支持幂等性,因为可能由于网络的原因,一个接口可能会被重试多次,如果没有做好幂等性控制的话,容易出现数据重复等异常情况。
比如将Ribbon的连接和读取时间设置为2s,最大调用次数为4次,则有如下配置:

ribbon.ConnectTimeout=2000
ribbon.ReadTimeout=2000
ribbon.MaxAutoRetriesNextServer=1
ribbon.MaxAutoRetries=1
ribbon.OkToRetryOnAllOperations=true

在接口调用过程中,所有的异常都会自动重试,直到4次调用都失败后,才会抛出异常。这时可以使用try catch就非常简单的捕获到了异常信息。
但是如果前三次调用都失败了,第四次成功的话,try catch就获取不到中间的异常信息了,微服务之间应该是高可用的,因为都是内网的调用,出现异常的情况是比较少的,所以需要将所有失败的情况记录下来,以便及时发现问题。
首先,添加自己的LoadBalancedRetryFactory

@Configuration
public class RibbonConfiguration {
    @Bean
    public LoadBalancedRetryFactory loadBalancedRetryPolicyFactory(final SpringClientFactory clientFactory) {
        return new DefaultRibbonLoadBalanceRetryFactory(clientFactory);
    }
}

其次添加 DefaultRibbonLoadBalanceRetryFactory

public class DefaultRibbonLoadBalanceRetryFactory extends RibbonLoadBalancedRetryFactory {
    public DefaultRibbonLoadBalanceRetryFactory(SpringClientFactory clientFactory) {
        super(clientFactory);
    }

    @Override
    public org.springframework.retry.RetryListener[] createRetryListeners(String service) {
        return new RetryListener[]{new RibbonRetryListener()};
    }
}

最后是重试监听的实现:RibbonRetryListener


public class RibbonRetryListener implements RetryListener {

    private Logger logger = LoggerFactory.getLogger(getClass());

    @Override
    public  boolean open(RetryContext context, RetryCallback callback) {
        return true;
    }

    @Override
    public  void close(RetryContext context, RetryCallback callback, Throwable throwable) {

    }

    @Override
    public  void onError(RetryContext context, RetryCallback callback, Throwable throwable) {
        String service = ((LoadBalancedRetryContext) context).getServiceInstance().getServiceId();
        logger.error(service + "微服务调用错误:", throwable);
    }
}

这样,所有的失败调用都会log出来,这里可以根据实际业务情况落表或者进行报警处理。

测试使用

这里使用用户服务和订单服务作为测试,首先需要启动config-server,eureka-server两个基础服务。
用户服务中提供接口:


    @RequestMapping("/retryListener")
    @ResponseBody
    public String testRetryListener() {
        try {
            return orderApi.testRetry();
        } catch (Exception e) {
            logger.error("", e);
            return "error:" + e.getMessage();
        }
    }

这里实现非常简单,只是调用order的接口而已。
订单服务则模拟失败的情况,访问的次数不为3的倍数,则休眠2.1s(因为用户服务配置超时时间为2s)。

    private AtomicLong counter = new AtomicLong(1);

    /**
     * 每次請求計數器加一,当请求的次数不为3的倍数时,休眠2.1s返回
     *
     * @return
     * @throws InterruptedException
     */
    @ResponseBody
    @RequestMapping("/testRetry")
    public String testRetry() throws InterruptedException {
        if (counter.incrementAndGet() % 3 != 0) {
            Thread.sleep(2100);
        }
        return "port:" + port;
    }

访问接口:http://localhost:8300/retryListener 可以看到返回了:port:8100数据,查看控制台:
SpringCloud2.0.1--Ribbon 重试监听_第1张图片

异常数据完美记录下来了。

文章中的代码已提交到:
github:https://github.com/cmlbeliever/SpringCloudBucket
码云:https://gitee.com/cmlbeliever/springcloud
工程中包含了微服务的常用实现方式, 欢迎star与fork

你可能感兴趣的:(java,微服务)