代码优化之函数式编程的实践

在这里插入图片描述

背景

业务中经常使用Http调用接口,由于网络抖动,时常会出现调用失败、连接超时、读取超时、远程服务器关闭连接等情况。因此会在使用HTTP调接口的地方加上重试逻辑。这就导致了很多业务逻辑代码都有重试逻辑,代码不够简洁,可重用性也不高。因此需要进一步优化代码,将重试逻辑抽成一个工具类(静态方法)。

需要优化的代码

重试逻辑的伪如下:

CaptureOrderResponse resp = null;
int retryTimes = 0;
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
do {
    try {
        String startTimeStr = formatter.format(LocalDateTime.now());
        resp = execute(null, header, null, restTemplate, CaptureOrderResponse.class, payRouter); // 1
        String endTimeStr = formatter.format(LocalDateTime.now());
        log.info("business log info, response:{}, startTimeStr:{}, endTimeStr:{}, retryTimes:{}", JSON.toJSONString(resp), startTimeStr, endTimeStr, retryTimes);
    } finally {
        if (resp != null && !resp.getCode().equals(HttpStatus.SC_OK)) {
            try {
                Thread.sleep(500L);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        retryTimes++;
    }
} while ((Objects.isNull(resp) || !resp.getCode().equals(HttpStatus.SC_OK)) && retryTimes < 3);

分析:除了上面代码块中标记了1的那行代码(业务逻辑的代码,需要重试的代码)以及一些HTTP响应实体类,其余代码基本都是公用逻辑,所以可以将公用逻辑抽取到工具方法里面,会变的标记1所在的代码以及响应实体类交给静态方法的入参以及函数式编程实现。

优化后的代码

定义可变实体类的接口方法,使得只要继承了该类,无论什么响应实体类都可以在工具类方法中调用接口方法:

@Data
public class PayBaseResponse {
    private Integer code;
    private String responseMateData;
    private String errorMsg;
    private String requestMateData;
}

定义3个函数式接口InvokerLogAfterInvokerLogBeforeInvoker,下面是伪代码:

public class HttpRetryUtil {

	public static <T extends PayBaseResponse> T invokeTilSpecStatus(Invoker<T> invoker,
	                                                              Object requestData,
	                                                              LogBeforeInvoker logBeforeInvoker,
	                                                              LogAfterInvoker logAfterInvoker,
	                                                              int retryTimes,
	                                                              Long sleepInterval,
	                                                              TimeUnit timeUnit,
	                                                              int httpStatus
	                                             ) throws Exception {
	  Objects.requireNonNull(invoker, "Invoker must not null!");
	  Objects.requireNonNull(timeUnit, "TimeUnit must not null!");
	  Assert.assertTrue("The maximum number of retries cannot exceed 3 times", retryTimes <= MAX_RETRY_TIMES);
	  Assert.assertTrue("The minimum number of retries cannot be less than 1", retryTimes >= MIN_RETRY_TIMES);
	
	  long sleepMillis = timeUnit.toMillis(sleepInterval);
	
	  T resp = null;
	  int currentTimes = 0;
	  do {
	      try {
	          if (Objects.nonNull(logBeforeInvoker)) {
	              logBeforeInvoker.logContent();
	          }
	          String startTimeStr = DATE_TIME_FORMATTER.format(LocalDateTime.now());
	          resp = invoker.invoke();
	          if (Objects.nonNull(logAfterInvoker)) {
	              logAfterInvoker.logContent();
	          }
	          String endTimeStr = DATE_TIME_FORMATTER.format(LocalDateTime.now());
	          log.info("HttpRetryUtil invokeTilSpecStatus, request:{}, response:{}, startTimeStr:{}, endTimeStr:{}, currentTimes:{}", JSON.toJSONString(requestData), JSON.toJSONString(resp), startTimeStr, endTimeStr, currentTimes);
	      } finally {
	          if (resp != null && !resp.getCode().equals(httpStatus)) {
	              try {
	                  Thread.sleep(sleepMillis);
	              } catch (InterruptedException e) {
	                  e.printStackTrace();
	              }
	          }
	
	          currentTimes++;
	      }
	  } while ((Objects.isNull(resp) || !resp.getCode().equals(HttpStatus.SC_OK)) && currentTimes < retryTimes);
	
	  return resp;
	}

    /**
     * 函数式接口
     * @param <T>
     */
    @FunctionalInterface
    public interface Invoker<T extends PayBaseResponse> {
        T invoke() throws Exception;
    }
    
    /**
     * 执行方法后打印日志
     *
     */
    @FunctionalInterface
    public interface LogAfterInvoker {
        void logContent();
    }

    /**
     * 执行方法前打印日志
     */
    @FunctionalInterface
    public  interface LogBeforeInvoker {
        void logContent();
    }
}

用法:如下所示,其中用到的()-> xxx就是函数式作为入参

public static void main(String[] args) {
	try {
	    response = HttpRetryUtil.invokeTilSpecStatus(
	            () -> execute(deliverRequest, header, null, restTemplate, PayBaseResponse.class, payRouter),
	            deliverRequest,
	            () -> log.info("log content before invoke"),
	            () -> log.info("log content after invoke"),
	            3,
	            500L,
	            TimeUnit.MILLISECONDS,
	            HttpStatus.SC_OK
	    );
	    log.info("Business, request:{}, response:{}", JSON.toJSONString(deliverRequest), JSON.toJSONString(response));
	} catch (Exception e) {
	    log.error("Business error, exception:", e);
	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值