springboot项目统一接口超时机制设计

springboot项目统一接口超时机制以及异常捕获设计

因为不同的业务接口所需超时时间不同,例如上传或者下载,但是大多数接口都基本可以统一一个超时时间,同时捕获异常,方便上下游子系统设置超时时间能够包住,以及业务可以根据错误码更好地判断做对应的补偿措施,前端展示失败原因
主要设计:编写BaseController,提供请求统一入口,线程池提交请求并设置超时时间,超时时间可以自定义,定义一个函数式接口TemplateInterface,请求对象需要继承BaseRequestDTO,响应对象需要继承BaseResponseDTO

BaseController

public class BaseController {

    private static final Logger logger = LoggerFactory.getLogger(BaseController.class);
    protected static final JsonMapper JSON_MAPPER = JsonMapper.nonEmptyMapper();

    @Autowired
    @Qualifier("threadPoolTaskExecutor")
    private ThreadPoolTaskExecutor threadPoolTaskExecutor;

    @Autowired
    private BizSeqUtil bizSeqUtil;
    protected static long timeout = 7000;//默认超时时间


    public <T extends BaseResponseDTO, E extends BaseRequestDTO> T doExecute(HttpServletRequest httpServletRequest
            , HttpServletResponse httpServletResponse, long timeout, E request, TemplateInterface<T, E> templateInterface, T timoutResponse, T exceptionResponse) {
        //执行逻辑
        setBizSeq(request);
        try {
            Future<T> future = threadPoolTaskExecutor.submit(() -> {
                return templateInterface.apply(request);
            });
            T baseResponseDTO = future.get(timeout, TimeUnit.MILLISECONDS);
            baseResponseDTO.setBizSeqNo(request.getBizSeqNo());
            return baseResponseDTO;
        } catch (TimeoutException e) {
            logger.error("{}|submit request timeout exception:{}",request.getBizSeqNo(),e.getMessage());
            timoutResponse.setBizSeqNo(request.getBizSeqNo());
            return timoutResponse;
        } catch (Exception e) {
            logger.error("{}|submit request error:",request.getBizSeqNo(),e);
            exceptionResponse.setBizSeqNo(request.getBizSeqNo());
            return exceptionResponse;
        }
    }

	//设置流水号
    private <E extends BaseRequestDTO> void setBizSeq(E req) {
        if (req.getBizSeqNo() != null && req.getBizSeqNo().length() > 0) {
            return;
        }
        String bizSeqNo = bizSeqUtil.newBizSeq();
        req.setBizSeqNo(bizSeqNo);
    }
}

PlayerController继承BaseController

@RestController
@RequestMapping("/player")
@Slf4j
public class PlayerController extends BaseController {

    @Autowired
    private PlayerService playerService;

    //@RequestCheck
    @RequestMapping("/list")
    public PlayerResponseDTO getPlayerList(HttpServletRequest httpServletRequest,
                                           HttpServletResponse httpServletResponse,
                                           @RequestBody PlayerRequestDTO requestDTO) {

        log.info("getPlayerList requestDTO:{}", JSON_MAPPER.toJson(requestDTO));
        PlayerResponseDTO timeoutResponse = new PlayerResponseDTO(ErrorStatus.TIMEOUT_EXCEPTION);//超时返回
        PlayerResponseDTO errorResponse = new PlayerResponseDTO(ErrorStatus.SYSTEM_ERROR);//异常返回

        return doExecute(httpServletRequest, httpServletResponse, timeout, requestDTO, (request -> {
            PlayerResponseDTO responseDTO = playerService.getPlayerList(request);
            log.info("getPlayerList responseDTO:{}", JSON_MAPPER.toJson(responseDTO));
            return responseDTO;
        }), timeoutResponse, errorResponse);
    }
}

请求父类BaseRequestDTO和响应父类BaseResponseDTO

/**
 * 请求父类
 */
public class BaseRequestDTO {

    private String bizSeqNo;
	//get set方法忽略
}

/**
 * 响应父类
 */
public class BaseResponseDTO {

    private String code = "0";
    private String msg = "success";
    private String bizSeqNo;
    private Object data;


    public BaseResponseDTO() {
    }

    public BaseResponseDTO(Object object) {
        this.code = "0";
        this.msg = "success";
        this.data = object;
    }

    public BaseResponseDTO(ErrorStatus errorStatus) {
        this.code = errorStatus.getErrCode();
        this.msg = errorStatus.getErrMsg();
    }

    public BaseResponseDTO(String code,String msg) {
        this.code = code;
        this.msg = msg;
    }
	//get set方法忽略
}

定义一个函数式接口TemplateInterface,请求对象需要继承BaseRequestDTO,相应对象需要继承BaseResponseDTO

//<T extends BaseResponseDTO, E extends BaseRequestDTO>
@FunctionalInterface
public interface TemplateInterface<T , E> {
    T apply(E r);
}

创建线程池

@Bean("threadPoolTaskExecutor")
public ThreadPoolTaskExecutor threadPoolExecutor(@Value("${threadPool.corePoolSize}") int corePoolSize,
                                   @Value("${threadPool.maxPoolSize}") int maxPoolSize,
                                   @Value("${threadPool.keepAliveSeconds}") int keepAliveSeconds,
                                   @Value("${threadPool.queueCapacity}") int queueCapacity,
                                   @Value("${threadPool.threadNamePrefix}") String threadNamePrefix
                                             ) {
    ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
    executor.setCorePoolSize(corePoolSize);
    executor.setMaxPoolSize(maxPoolSize);
    executor.setKeepAliveSeconds(keepAliveSeconds);
    executor.setQueueCapacity(queueCapacity);
    executor.setThreadNamePrefix(threadNamePrefix);
    executor.initialize();
    return executor;
}

接口方法添加耗时逻辑,查看效果

public PlayerResponseDTO getPlayerList(PlayerRequestDTO requestDTO) {
		//添加耗时逻辑,接口超时时间是7
        try {
            Thread.sleep(10000);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        TPlayer player = new TPlayer();
        BeanUtils.copyProperties(requestDTO,player);
        PageInfo page = requestDTO.getPage() == null ? new PageInfo() : requestDTO.getPage();
        player.setPage(page);
        List<TPlayer> list = playerDAO.selectPlayerList(player);
        PlayerResponseDTO responseDTO = new PlayerResponseDTO();
        responseDTO.setPlayerList(list);
        return responseDTO;
    }

达到预期效果

在这里插入图片描述

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值