springboot项目统一接口超时机制设计
作者:mmseoamin日期:2023-12-25

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 doExecute(HttpServletRequest httpServletRequest
            , HttpServletResponse httpServletResponse, long timeout, E request, TemplateInterface templateInterface, T timoutResponse, T exceptionResponse) {
        //执行逻辑
        setBizSeq(request);
        try {
            Future 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  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

//
@FunctionalInterface
public interface TemplateInterface {
    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 list = playerDAO.selectPlayerList(player);
        PlayerResponseDTO responseDTO = new PlayerResponseDTO();
        responseDTO.setPlayerList(list);
        return responseDTO;
    }

达到预期效果

在这里插入图片描述