基于Spring-AOP實(shí)現(xiàn)自定義分片工具詳解
1.背景
隨著數(shù)據(jù)量的增長,發(fā)現(xiàn)系統(tǒng)在與其他系統(tǒng)交互時(shí),批量接口會出現(xiàn)超時(shí)現(xiàn)象,發(fā)現(xiàn)原批量接口在實(shí)現(xiàn)時(shí),沒有做分片處理,當(dāng)數(shù)據(jù)過大時(shí)或超過其他系統(tǒng)閾值時(shí),就會出現(xiàn)錯(cuò)誤。由于與其他系統(tǒng)交互比較多,一個(gè)一個(gè)接口去做分片優(yōu)化,改動量較大,所以考慮通過AOP解決此問題。
2.Spring-AOP
AOP (Aspect Orient Programming),直譯過來就是 面向切面編程。AOP 是一種編程思想,是面向?qū)ο缶幊蹋∣OP)的一種補(bǔ)充。面向?qū)ο缶幊虒⒊绦虺橄蟪筛鱾€(gè)層次的對象,而面向切面編程是將程序抽象成各個(gè)切面。
Spring 中的 AOP 是通過動態(tài)代理實(shí)現(xiàn)的。 Spring AOP 不能攔截對對象字段的修改,也不支持構(gòu)造器連接點(diǎn),我們無法在 Bean 創(chuàng)建時(shí)應(yīng)用通知。
3.功能實(shí)現(xiàn)
自定義分片處理分三個(gè)部分:自定義注解(MethodPartAndRetryer)、重試器(RetryUtil)、切面實(shí)現(xiàn)(RetryAspectAop)。
3.1 MethodPartAndRetryer
源碼
@Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface MethodPartAndRetryer { /** * 失敗重試次數(shù) * @return */ int times() default 3; /** * 失敗間隔執(zhí)行時(shí)間 300毫秒 * @return */ long waitTime() default 300L; /** * 分片大小 * @return */ int parts() default 200; }
@interface說明這個(gè)類是個(gè)注解。
@Target是這個(gè)注解的作用域
public enum ElementType { /** 類、接口(包括注釋類型)或枚舉聲明 */ TYPE, /** 字段聲明(包括枚舉常量) */ FIELD, /** 方法聲明 */ METHOD, /** 正式的參數(shù)聲明 */ PARAMETER, /** 構(gòu)造函數(shù)聲明 */ CONSTRUCTOR, /** 局部變量聲明 */ LOCAL_VARIABLE, /** 注釋類型聲明*/ ANNOTATION_TYPE, /** 程序包聲明 */ PACKAGE, /**類型參數(shù)聲明*/ TYPE_PARAMETER, /**類型的使用*/ TYPE_USE }
@Retention注解的生命周期
public enum RetentionPolicy { /** 編譯器處理完后不存儲在class中*/ SOURCE, /**注釋將被編譯器記錄在類文件中,但不需要在運(yùn)行時(shí)被VM保留。 這是默認(rèn)值*/ CLASS, /**編譯器存儲在class中,可以由虛擬機(jī)讀取*/ RUNTIME }
times():接口調(diào)用失敗時(shí),重試的次數(shù)。
waitTime():接口調(diào)用失敗是,間隔多長時(shí)間再次調(diào)用。
int parts():進(jìn)行分片時(shí),每個(gè)分片的大小。
3.2 RetryUtil
源碼
public class RetryUtil<V> { ??????? public Retryer<V> getDefaultRetryer(int times,long waitTime) { Retryer<V> retryer = RetryerBuilder.<V>newBuilder() .retryIfException() .retryIfRuntimeException() .retryIfExceptionOfType(Exception.class) .withWaitStrategy(WaitStrategies.fixedWait(waitTime, TimeUnit.MILLISECONDS)) .withStopStrategy(StopStrategies.stopAfterAttempt(times)) .build(); return retryer; } }
說明
- RetryerBuilder:是用于配置和創(chuàng)建Retryer的構(gòu)建器。
- retryIfException:拋出runtime異常、checked異常時(shí)都會重試,但是拋出error不會重試。
- retryIfRuntimeException:只會在拋runtime異常的時(shí)候才重試,checked異常和error都不重試。
- retryIfExceptionOfType:允許我們只在發(fā)生特定異常的時(shí)候才重試。
- withWaitStrategy:等待策略,每次請求間隔。
- withStopStrategy:停止策略,重試多少次后停止。
3.3 RetryAspectAop
源碼:
public class RetryAspectAop { public Object around(final ProceedingJoinPoint point) throws Throwable { Object result = null; final Object[] args = point.getArgs(); boolean isHandler1 = isHandler(args); if (isHandler1) { String className = point.getSignature().getDeclaringTypeName(); String methodName = point.getSignature().getName(); Object firstArg = args[0]; List<Object> paramList = (List<Object>) firstArg; //獲取方法信息 Method method = getCurrentMethod(point); //獲取注解信息 MethodPartAndRetryer retryable = AnnotationUtils.getAnnotation(method, MethodPartAndRetryer.class); //重試機(jī)制 Retryer<Object> retryer = new RetryUtil<Object>().getDefaultRetryer(retryable.times(),retryable.waitTime()); //分片 List<List<Object>> requestList = Lists.partition(paramList, retryable.parts()); for (List<Object> partList : requestList) { args[0] = partList; Object tempResult = retryer.call(new Callable<Object>() { @Override public Object call() throws Exception { try { return point.proceed(args); } catch (Throwable throwable) { log.error(String.format("分片重試報(bào)錯(cuò),類%s-方法%s",className,methodName),throwable); throw new RuntimeException("分片重試出錯(cuò)"); } } }); if (null != tempResult) { if (tempResult instanceof Boolean) { if (!((Boolean) tempResult)) { log.error(String.format("分片執(zhí)行報(bào)錯(cuò)返回類型不能轉(zhuǎn)化bolean,類%s-方法%s",className,methodName)); throw new RuntimeException("分片執(zhí)行報(bào)錯(cuò)!"); } result = tempResult; } else if (tempResult instanceof List) { if(result ==null){ result =Lists.newArrayList(); } ((List) result).addAll((List) tempResult); }else { log.error(String.format("分片執(zhí)行返回的類型不支持,類%s-方法%s",className,methodName)); throw new RuntimeException("不支持該返回類型"); } } else { log.error(String.format("分片執(zhí)行返回的結(jié)果為空,類%s-方法%s",className,methodName)); throw new RuntimeException("調(diào)用結(jié)果為空"); } } } else { result = point.proceed(args); } return result; } private boolean isHandler(Object[] args) { boolean isHandler = false; if (null != args && args.length > 0) { Object firstArg = args[0]; //如果第一個(gè)參數(shù)是list 并且數(shù)量大于1 if (firstArg!=null&&firstArg instanceof List &&((List) firstArg).size()>1) { isHandler = true; } } return isHandler; } private Method getCurrentMethod(ProceedingJoinPoint point) { try { Signature sig = point.getSignature(); MethodSignature msig = (MethodSignature) sig; Object target = point.getTarget(); return target.getClass().getMethod(msig.getName(), msig.getParameterTypes()); } catch (NoSuchMethodException e) { throw new RuntimeException(e); } } }
說明:
getCurrentMethod:獲取方法信息即要做分片的批量調(diào)用的接口。
isHandler1:判斷是否要做分片處理,只有第一參數(shù)是list并且list 的值大于1時(shí)才做分片處理。
around:具體分片邏輯。
- 獲取要分片方法的參數(shù)。
- 判斷是否要做分片處理。
- 獲取方法。
- 獲取重試次數(shù)、重試間隔時(shí)間和分片大小。
- 生成重試器。
- 根據(jù)設(shè)置的分片大小,做分片處理。
調(diào)用批量接口并處理結(jié)果。
4.功能使用
4.1 配置文件
4.2 代碼示例
@MethodPartAndRetryer(parts=100) public Boolean writeBackOfGoodsSN(List<SerialDTO> listSerial,ObCheckWorker workerData)
只要在需要做分片的批量接口方法上,加上MethodPartAndRetryer注解就可以,重試次數(shù)、重試間隔時(shí)間和分片大小可以在注解時(shí)設(shè)置,也可以使用默認(rèn)值。
5.小結(jié)
通過自定義分片工具,可以快速地對老代碼進(jìn)行分片處理,而且增加了重試機(jī)制,提高了程序的可用性,提高了對老代碼的重構(gòu)效率。
到此這篇關(guān)于基于SpringAOP實(shí)現(xiàn)自定義分片工具詳解的文章就介紹到這了,更多相關(guān)SpringAOP自定義分片工具內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
java HttpClient傳輸json格式的參數(shù)實(shí)例講解
這篇文章主要介紹了java HttpClient傳輸json格式的參數(shù)實(shí)例講解,具有很好的參考價(jià)值,希望對大家有所幫助。一起跟隨小編過來看看吧2021-01-01java編譯時(shí)出現(xiàn)使用了未經(jīng)檢查或不安全的操作解決方法
這篇文章主要介紹了java編譯時(shí)出現(xiàn)使用了未經(jīng)檢查或不安全的操作的解決方法,需要的朋友可以參考下2014-03-03Spring框架web項(xiàng)目實(shí)戰(zhàn)全代碼分享
這篇文章主要介紹了Spring框架web項(xiàng)目實(shí)戰(zhàn)全代碼分享,具有一定參考價(jià)值,需要的朋友可以了解下。2017-11-11mybatis通過TypeHandler?list轉(zhuǎn)換string類型轉(zhuǎn)換方式
這篇文章主要介紹了mybatis通過TypeHandler?list轉(zhuǎn)換string類型轉(zhuǎn)換方式,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-07-07Java中ArrayIndexOutOfBoundsException 異常報(bào)錯(cuò)的解決方案
本文主要介紹了Java中ArrayIndexOutOfBoundsException 異常報(bào)錯(cuò)的解決方案,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2023-06-06spring-boot實(shí)現(xiàn)增加自定義filter(新)
本篇文章主要介紹了spring-boot實(shí)現(xiàn)增加自定義filter(新),小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2017-05-05