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

来源:互联网 发布:淘宝网男装新款 编辑:程序博客网 时间:2024/05/01 08:16

有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
原创粉丝点击