当前位置:首页 > 科技  > 软件

Springboot自定义重试注解@Retryable

来源: 责编: 时间:2023-10-13 14:37:58 358观看
导读一、概述微服务之间相互调用,难免会出现形形色色的异常,出现异常时有些情况可能需要先落重试任务表,然后通过任务调度等进行定时重试;通过自定义重试注解@Retryable,减少对核心业务代码入侵,增强代码可读性、可维护性。下面

LZo28资讯网——每日最新资讯28at.com

一、概述

微服务之间相互调用,难免会出现形形色色的异常,出现异常时有些情况可能需要先落重试任务表,然后通过任务调度等进行定时重试;通过自定义重试注解@Retryable,减少对核心业务代码入侵,增强代码可读性、可维护性。下面通过实战,开发自定义重试注解@Retryable。诸位可根据业务需要,稍作改造直接使用;如果有疑问、或者好的想法,欢迎留言,经验共享。LZo28资讯网——每日最新资讯28at.com

二、实战

重试任务表定义(retry_task):LZo28资讯网——每日最新资讯28at.com

CREATE TABLE `retry_task` (  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键值',  `business_type_code` varchar(32) COLLATE NOT NULL DEFAULT '' COMMENT '业务类型编码',  `business_type_desc` varchar(100) COLLATE NOT NULL DEFAULT '' COMMENT '业务类型描述',  `retry_service_name` varchar(100) COLLATE NOT NULL DEFAULT '' COMMENT '重试的service名称',  `business_param` text COLLATE NOT NULL DEFAULT '' COMMENT '业务参数',  `wait_retry_times` int(11) NOT NULL DEFAULT 3 COMMENT '待重试次数',  `already_retry_times` int(11) NOT NULL DEFAULT 0 COMMENT '已重试次数',  `retry_result_code` varchar(36) COLLATE NOT NULL DEFAULT '' COMMENT '重试结果码',  `retry_result_msg` varchar(255) COLLATE NOT NULL DEFAULT '' COMMENT '重试结果描述',  `create_user` varchar(36) COLLATE NOT NULL DEFAULT '' COMMENT '创建人',  `create_time` datetime NOT NULL COMMENT '创建时间',  `update_user` varchar(36) COLLATE NOT NULL DEFAULT '' COMMENT '更新人',  `update_time` datetime NOT NULL COMMENT '更新时间',  PRIMARY KEY (`id`),  KEY `idx_create_time` (`create_time`),  KEY `idx_business_type_code` (`business_type_code`)) COMMENT='重试任务表';

重试任务表实体类(RetryTaskEntity):LZo28资讯网——每日最新资讯28at.com

@Datapublic class RetryTaskEntity implements Serializable {    private static final long serialVersionUID = -1950778520234119369L;    /**     * 主键值     */    private BigInteger id;    /**     * 业务类型编码     */    private String businessTypeCode;    /**     * 业务类型描述     */    private String businessTypeDesc;    /**     * 重试的service名称     */    private String retryServiceName;    /**     * 业务参数     */    private String businessParam;    /**     * 待重试的次数     */    private Integer waitRetryTimes;    /**     * 已重试的次数     */    private Integer alreadyRetryTimes;    /**     * 重试结果码     */    private String retryResultCode;    /**     * 重试结果描述     */    private String retryResultMsg;    /**     * 创建人     */    private String createUser;    /**     * 创建时间     */    private Date createTime;    /**     * 更新人     */    private String updateUser;    /**     * 更新时间     */    private Date updateTime;}

重试任务表mapper和对应的xml文件:LZo28资讯网——每日最新资讯28at.com

public interface RetryTaskMapper {    int addRetryTask(RetryTaskEntity retryTaskEntity);}
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE mapper        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"        "http://mybatis.org/dtd/mybatis-3-mapper.dtd"><mapper namespace="com.boot.demo.mapper.RetryTaskMapper">    <insert id="addRetryTask" parameterType="com.boot.demo.pojo.RetryTaskEntity">        INSERT INTO retry_task(business_type_code,                               business_type_desc,                               retry_service_name,                               business_param,                               wait_retry_times,                               already_retry_times,                               retry_result_code,                               retry_result_msg,                               create_user,                               create_time,                               update_user,                               update_time)        VALUES (#{businessTypeCode},                #{businessTypeDesc},                #{retryServiceName},                #{businessParam},                #{waitRetryTimes},                #{alreadyRetryTimes},                #{retryResultCode},                #{retryResultMsg},                #{createUser},                #{createTime},                #{updateUser},                #{updateTime})    </insert></mapper>

重试任务表service和对应的serviceImpl:LZo28资讯网——每日最新资讯28at.com

public interface RetryTaskService {    void addRetryTask(RetryTaskEntity retryTaskEntity);}
@Servicepublic class RetryTaskServiceImpl implements RetryTaskService {    @Autowired    private RetryTaskMapper retryTaskMapper;    @Override    public void addRetryTask(RetryTaskEntity retryTaskEntity) {        retryTaskMapper.addRetryTask(retryTaskEntity);    }}

业务类型枚举类(RetryTaskDefinitionEnum):LZo28资讯网——每日最新资讯28at.com

/** * 重试任务枚举 */public enum RetryTaskDefinitionEnum {    ADD_STOCK("101", "采购入库成功后新增库存异常重试", "purchaseService", 3);    /**     * 业务类型编码     */    private final String businessTypeCode;    /**     * 业务类型描述     */    private final String businessTypeDesc;    /**     * 重试的service名称     */    private final String retryServiceName;    /**     * 重试次数     */    private final Integer retryTimes;    RetryTaskDefinitionEnum(String businessTypeCode, String businessTypeDesc, String retryServiceName, Integer retryTimes) {        this.businessTypeCode = businessTypeCode;        this.businessTypeDesc = businessTypeDesc;        this.retryServiceName = retryServiceName;        this.retryTimes = retryTimes;    }    public static RetryTaskDefinitionEnum getTaskDefinitionByBusinessTypeCode(String businessTypeCode) {        if (StringUtils.isBlank(businessTypeCode)) {            return null;        }        for (RetryTaskDefinitionEnum taskDefinition : values()) {            if (taskDefinition.getBusinessTypeCode().equals(businessTypeCode)) {                return taskDefinition;            }        }        return null;    }    public String getBusinessTypeCode() {        return businessTypeCode;    }    public String getBusinessTypeDesc() {        return businessTypeDesc;    }    public String getRetryServiceName() {        return retryServiceName;    }    public Integer getRetryTimes() {        return retryTimes;    }}

自定义注解(@MyRetryable):LZo28资讯网——每日最新资讯28at.com

import java.lang.annotation.*;@Retention(RetentionPolicy.RUNTIME)@Target(ElementType.METHOD)@Inherited@Documentedpublic @interface MyRetryable {    RetryTaskDefinitionEnum businessType();}

自定义注解切面(MyRetryableAspect):LZo28资讯网——每日最新资讯28at.com

import com.alibaba.fastjson.JSON;import lombok.extern.slf4j.Slf4j;import com.boot.demo.result.Result;import com.boot.demo.result.ResultCode;import com.boot.demo.pojo.RetryTaskEntity;import org.aspectj.lang.annotation.Around;import org.aspectj.lang.annotation.Aspect;import com.boot.demo.annotation.MyRetryable;import org.aspectj.lang.annotation.Pointcut;import org.aspectj.lang.ProceedingJoinPoint;import com.boot.demo.service.RetryTaskService;import org.aspectj.lang.reflect.MethodSignature;import org.springframework.stereotype.Component;import com.boot.demo.annotation.RetryTaskDefinitionEnum;import org.springframework.beans.factory.annotation.Autowired;import java.util.Date;@Slf4j@Aspect@Componentpublic class MyRetryableAspect {    @Autowired    private RetryTaskService retryTaskService;    @Pointcut("@annotation(com.boot.demo.annotation.MyRetryable)")    public void pointCut() {    }    @Around(value = "pointCut()")    public Object around(ProceedingJoinPoint joinPoint) {        Result result = null;        try {            // 执行目标方法            result = (Result) joinPoint.proceed();            // 目标方法返回:成功结果码(200),则无需重试            if (ResultCode.SUCCESS.getCode() == result.getCode()) {                return result;            }            // 目标方法返回:非成功结果码(非200)则需重试(此次可根据需要判断什么样的返回码需要重试)            dealAddRetryTask(joinPoint);,            return result;        } catch (Throwable e) {            log.error("myRetryableAspectLog error param: {} result: {} e: ", joinPoint.getArgs(), result, e);            // 此处捕获异常之后,也可以根据需要重试,这里就仅输出异常日志            return result;        }    }    private void dealAddRetryTask(ProceedingJoinPoint joinPoint) {        // 获取重试注解信息        MyRetryable myRetryableAnnotation = ((MethodSignature) joinPoint.getSignature()).getMethod().getAnnotation(MyRetryable.class);        if (null == myRetryableAnnotation) {            return;        }        // 根据业务类型编码,获取枚举中定义的业务类型描述、重试的service、重试次数等信息        String businessTypeCode = myRetryableAnnotation.businessType().getBusinessTypeCode();        RetryTaskDefinitionEnum retryTaskDefinition = RetryTaskDefinitionEnum.getTaskDefinitionByBusinessTypeCode(businessTypeCode);        if (null == retryTaskDefinition) {            return;        }        RetryTaskEntity retryTaskEntity = new RetryTaskEntity();        retryTaskEntity.setBusinessTypeCode(businessTypeCode);        retryTaskEntity.setBusinessTypeDesc(retryTaskDefinition.getBusinessTypeDesc());        retryTaskEntity.setRetryServiceName(retryTaskDefinition.getRetryServiceName());        retryTaskEntity.setBusinessParam(JSON.toJSONString(joinPoint.getArgs()[0]));        retryTaskEntity.setWaitRetryTimes(retryTaskDefinition.getRetryTimes());        retryTaskEntity.setAlreadyRetryTimes(0);        retryTaskEntity.setRetryResultCode("");        retryTaskEntity.setRetryResultMsg("");        retryTaskEntity.setCreateUser("SYS");        retryTaskEntity.setCreateTime(new Date());        retryTaskEntity.setUpdateUser("SYS");        retryTaskEntity.setUpdateTime(new Date());        retryTaskService.addRetryTask(retryTaskEntity);    }}

基础类(Result、ResultCode、ResultGenerator)。LZo28资讯网——每日最新资讯28at.com

Result类:LZo28资讯网——每日最新资讯28at.com

public class Result {    private int code;    private String message;    private Object data;    public Result setCode(ResultCode resultCode) {        this.code = resultCode.getCode();        return this;    }    public int getCode() {        return code;    }    public Result setCode(int code) {        this.code = code;        return this;    }    public String getMessage() {        return message;    }    public Result setMessage(String message) {        this.message = message;        return this;    }    public Object getData() {        return data;    }    public Result setData(Object data) {        this.data = data;        return this;    }    @Override    public String toString() {        final StringBuilder sb = new StringBuilder("Result{");        sb.append("code=").append(code);        sb.append(", message='").append(message).append('/'');        sb.append(", data=").append(data);        sb.append('}');        return sb.toString();    }}

ResultCode类:LZo28资讯网——每日最新资讯28at.com

public enum ResultCode {    SUCCESS(200),    FAIL(400),    UNAUTHORIZED(401),    FORBIDDEN(403),    NOT_FOUND(404),    INTERNAL_SERVER_ERROR(500);    private final int code;    ResultCode(int code) {        this.code = code;    }    public int getCode() {        return code;    }}

ResultGenerator类:LZo28资讯网——每日最新资讯28at.com

public class ResultGenerator {    private static final String DEFAULT_SUCCESS_MESSAGE = "SUCCESS";    private ResultGenerator() {    }    public static Result genSuccessResult() {        return new Result().setCode(ResultCode.SUCCESS).setMessage(DEFAULT_SUCCESS_MESSAGE);    }    public static Result genSuccessResult(Object data) {        return new Result().setCode(ResultCode.SUCCESS).setMessage(DEFAULT_SUCCESS_MESSAGE).setData(data);    }    public static Result genFailResult(String message) {        return new Result().setCode(ResultCode.FAIL).setMessage(message);    }    public static Result genFailResult(ResultCode code, String message) {        return new Result().setCode(code).setMessage(message);    }    public static Result genFailResult(String message, Object data) {        return new Result().setCode(ResultCode.FAIL).setMessage(message).setData(data);    }}

测试controller(PurchaseController):LZo28资讯网——每日最新资讯28at.com

@RestController@RequestMapping("/purchase")public class PurchaseController {    @Autowired    private PurchaseService purchaseService;    @GetMapping("/test")    public String test(String param) {        purchaseService.addStock(param);        return "success";    }}

测试PurchaseService、和PurchaseServiceImplLZo28资讯网——每日最新资讯28at.com

public interface PurchaseService {    Result addStock(String param);}
@Service("purchaseService")public class PurchaseServiceImpl implements PurchaseService {    @Override    // 在需要重试的业务方法上新增重试注解即可    @MyRetryable(businessType = RetryTaskDefinitionEnum.ADD_STOCK)    public Result addStock(String param) {//     return ResultGenerator.genSuccessResult();        return ResultGenerator.genFailResult("系统异常...");    }}

三、总结

新增重试任务成功之后,我们可通过调度平台(比如:xxlJob),定时查询重试任务表,然后调用RetryTaskDefinitionEnum中定义的重试的service(retryServiceName),这里可以定义一个模板方法,根据retryServiceName,从spring中获取到对应的bean,执行具体的业务方法,然后更新任务状态和重试次数即可。LZo28资讯网——每日最新资讯28at.com

本文链接:http://www.28at.com/showinfo-26-13587-0.htmlSpringboot自定义重试注解@Retryable

声明:本网页内容旨在传播知识,若有侵权等问题请及时与本网联系,我们将在第一时间删除处理。邮件:2376512515@qq.com

上一篇: 聊聊 Reddit Programming 板块的未来

下一篇: Java遍历一个 List 有哪些方式?每种的实现原理以及哪种最高效?

标签:
  • 热门焦点
  • Find N3入网:最高支持16+1TB

    OPPO将于近期登场的Find N3折叠屏目前已经正式入网,型号为PHN110。本次Find N3在外观方面相比前两代有很大的变化,不再是小号的横向折叠屏,而是跟别的厂商一样采用了较为常见的
  • 直屏旗舰来了 iQOO 12和K70 Pro同台竞技

    旗舰机基本上使用的都是双曲面屏幕,这就让很多喜欢直屏的爱好者在苦等一款直屏旗舰,这次,你们等到了。据博主数码闲聊站带来的最新爆料称,Redmi下代旗舰K70 Pro和iQOO 12两款手
  • 女孩租房开2小时空调用完100元电费引热议:5级能耗惹不起 月薪过万电费也交不起

    近日,江苏苏州一女孩租房当天充值了100元电费,开着空调不到2小时发现电费已用完。对于为什么这个快,房东表示,电表坏了这种情况很多,之前也遇到过,给租客换
  • 谷歌KDD'23工作:如何提升推荐系统Ranking模型训练稳定性

    谷歌在KDD 2023发表了一篇工作,探索了推荐系统ranking模型的训练稳定性问题,分析了造成训练稳定性存在问题的潜在原因,以及现有的一些提升模型稳定性方法的不足,并提出了一种新
  • 虚拟键盘 API 的妙用

    你是否在遇到过这样的问题:移动设备上有一个固定元素,当激活虚拟键盘时,该元素被隐藏在了键盘下方?多年来,这一直是 Web 上的默认行为,在本文中,我们将探讨这个问题、为什么会发生
  • 一文掌握 Golang 模糊测试(Fuzz Testing)

    模糊测试(Fuzz Testing)模糊测试(Fuzz Testing)是通过向目标系统提供非预期的输入并监视异常结果来发现软件漏洞的方法。可以用来发现应用程序、操作系统和网络协议等中的漏洞或
  • 一文搞定Java NIO,以及各种奇葩流

    大家好,我是哪吒。很多朋友问我,如何才能学好IO流,对各种流的概念,云里雾里的,不求甚解。用到的时候,现百度,功能虽然实现了,但是为什么用这个?不知道。更别说效率问题了~下次再遇到,
  • 花7万退货退款无门:谁在纵容淘宝珠宝商家造假?

    来源:极点商业作者:杨铭在淘宝购买珠宝玉石后,因为保证金不够赔付,店铺关闭,退货退款难、维权无门的比比皆是。&ldquo;提供相关产品鉴定证书,支持全国复检,可以30天无理由退换货。&
  • 华为Mate60标准版细节曝光:经典星环相机模组回归

    这段时间以来,关于华为新旗舰的爆料日渐密集。据此前多方爆料,今年华为将开始恢复一年双旗舰战略,除上半年推出的P60系列外,往年下半年的Mate系列也将
Top