编码素养5:减少冗余代码-让一段方法Template-callback

有3个方法,签名完全不同,但执行时都可能发生异常。要求:无论哪个方法异常,都需要重试3次。假设重试3次的逻辑很复杂,但3个方法的逻辑都是相同的。如何复用重试代码?


public class FFMPEGManagerRedo implements FFMPEGManager {

    private static final Logger LOG = LoggerFactory.getLogger(FFMPEGManagerRedo.class);

    /** 重试的次数和间隔 */
    private int[] retryInterval;

    private FFMPEGManager ffmpegManager;

    public FFMPEGManagerRedo(FFMPEGManager ffmpegManager, int[] retryInterval) {
        super();
        if (retryInterval == null || retryInterval.length == 0) {
            throw new IllegalArgumentException("retry times should be greater than 0");
        }
        this.ffmpegManager = ffmpegManager;
        this.retryInterval = retryInterval;
    }

    public FFMPEGManagerRedo(FFMPEGManager ffmpegManager) {
        this(ffmpegManager, new int[] { 1, 2, 4 });//指数递增
    }

    @Override
    public MetaInfo readMetaInfo(String videoFileName) throws Exception {
        try {
            return ffmpegManager.readMetaInfo(videoFileName);//重试的时候才反射(减少反射对性能影响)
        } catch (Exception e) {
            Class<?>[] argsType = new Class<?>[] { String.class };
            Object[] argsValue = new Object[] { videoFileName };
            return (MetaInfo) retryMethod(ffmpegManager.getClass().getMethod("readMetaInfo", argsType), ffmpegManager,
                    argsValue);
        }
    }

    @Override
    public void extractThumb(String videoFileName, ThumbSpec thumbSpec, String thumbFileName) throws Exception {
        try {
            ffmpegManager.extractThumb(videoFileName, thumbSpec, thumbFileName);//重试的时候才反射(减少反射对性能影响)
        } catch (Exception e) {
            Class<?>[] argsType = new Class<?>[] { String.class, ThumbSpec.class, String.class };
            Object[] argsValue = new Object[] { videoFileName, thumbSpec, thumbFileName };
            retryMethod(ffmpegManager.getClass().getMethod("extractThumb", argsType), ffmpegManager, argsValue);
        }
    }

    @Override
    public void transcode(String videoFileName, TranscodeSpec transcodeSpec, String transcodeFileName) throws Exception {
        try {
            ffmpegManager.transcode(videoFileName, transcodeSpec, transcodeFileName);//重试的时候才反射(减少反射对性能影响)
        } catch (Exception e) {
            Class<?>[] argsType = new Class<?>[] { String.class, TranscodeSpec.class, String.class };
            Object[] argsValue = new Object[] { videoFileName, transcodeSpec, transcodeFileName };
            retryMethod(ffmpegManager.getClass().getMethod("transcode", argsType), ffmpegManager, argsValue);
        }

    }

    /* Retry Template-Callback */
    protected Object retryMethod(Method method, Object instance, Object... args) throws Exception {
        int retryCnt = 1;
        Exception lastException = null;
        do {
            try {
                Object r = method.invoke(instance, args);//method callback
                LOG.info("method {} invoke retry succ at time {}", method.getName(), retryCnt);
                return r;

            } catch (java.lang.reflect.InvocationTargetException targetException) {
                Throwable targetThrowable = targetException.getTargetException();
                if (targetException instanceof Exception) {//TargetException会被转译
                    lastException = (Exception) targetThrowable;
                } else {
                    lastException = targetException;
                }

            } catch (Exception e) {
                lastException = e;
            }
            if (lastException != null) {
                LOG.warn("method {} invoke fail at time {}, retry ...", method.getName(), retryCnt, lastException);
                if (retryCnt < retryInterval.length) {//最后一次不用睡眠
                    Thread.sleep(1000L * retryInterval[retryCnt]);//retry interval
                }
                retryCnt++;
            }

        } while (retryCnt < retryInterval.length);

        throw lastException;
    }

}

TestCase:

public class FFMPEGManagerRedoTest {

    private static final Logger LOG = LoggerFactory.getLogger(FFMPEGManagerRedoTest.class);

    //非public时,反射获取不到
    public static class CountedFFMPEGManager implements FFMPEGManager {

        private int metaCnt = 0;
        private int thumbCnt = 0;
        private int transCnt = 0;

        private int maxFailCnt = 3;

        public CountedFFMPEGManager(int maxFailCnt) {
            super();
            this.maxFailCnt = maxFailCnt;
        }

        @Override
        public MetaInfo readMetaInfo(String videoFileName) throws Exception {
            metaCnt++;
            if (metaCnt < maxFailCnt) {
                throw new Exception("mock fail");
            }
            MetaInfo meta = new MetaInfo();
            meta.setSizeByte(1024);
            return meta;
        }

        @Override
        public void extractThumb(String videoFileName, ThumbSpec thumbSpec, String thumbFileName) throws Exception {
            thumbCnt++;
            if (thumbCnt < maxFailCnt) {
                throw new Exception("mock fail");
            }
        }

        @Override
        public void transcode(String videoFileName, TranscodeSpec transcodeSpec, String transcodeFileName)
                throws Exception {
            transCnt++;
            if (transCnt < maxFailCnt) {
                throw new Exception("mock fail");
            }
        }

        public int getMetaCnt() {
            return metaCnt;
        }

        public int getThumbCnt() {
            return thumbCnt;
        }

        public int getTransCnt() {
            return transCnt;
        }
    }

    private CountedFFMPEGManager counted;
    private FFMPEGManagerRedo redo;

    @Before
    public void setUp() throws Exception {
        counted = new CountedFFMPEGManager(3);//最多允许失败3次
        redo = new FFMPEGManagerRedo(counted, new int[] { 1, 2, 4 });//如果失败,还可以重试3次
    }

    @Test
    public void testMeta() throws Exception {
        MetaInfo meta = redo.readMetaInfo("videoFileName");
        LOG.info("meta cnt: {}", counted.getMetaCnt());
        Assert.assertEquals(3, counted.getMetaCnt());
        Assert.assertNotNull(meta);
        Assert.assertEquals(1024, meta.getSizeByte());
    }

    @Test
    public void testThrumb() throws Exception {
        ThumbSpec thumbSpec = new ThumbSpec(120, 90, 2);
        redo.extractThumb("videoFileName.mp4", thumbSpec, "thumbFileName.jpg");
        LOG.info("thumb cnt: {}", counted.getThumbCnt());
        Assert.assertEquals(3, counted.getThumbCnt());
    }

    @Test
    public void testTrans() throws Exception {
        TranscodeSpec transcodeSpec = new TranscodeSpec();
        redo.transcode("videoFileName.flv", transcodeSpec, "transcodeFileName.mp4");
        LOG.info("trans cnt: {}", counted.getTransCnt());
        Assert.assertEquals(3, counted.getTransCnt());
    }

    @Test
    public void testBeyondRetryTimes() {
        redo = new FFMPEGManagerRedo(counted, new int[] { 1 });//只重试1次,Mock会连续失败3次,最终结果是失败
        Exception exception = null;
        MetaInfo meta = null;
        try {
            meta = redo.readMetaInfo("videoFileName");
        } catch (Exception e) {
            exception = e;
            System.err.println(e);
        }

        Assert.assertNull(meta);
        Assert.assertNotNull(exception);
        Assert.assertEquals("mock fail", exception.getMessage());

    }

}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值