- 資訊首頁(yè) > 開(kāi)發(fā)技術(shù) >
- 基于SpringCloud手寫(xiě)一個(gè)簡(jiǎn)易版Sentinel
隨著(zhù)微服務(wù)的流行,服務(wù)和服務(wù)之間的穩定性變得越來(lái)越重要。Sentinel 以流量為切入點(diǎn),從流量控制、熔斷降級、系統負載保護等多個(gè)維度保護服務(wù)的穩定性。
不可否認的是,Sentinel功能豐富,并且在提供好用的dashboard提供配置,但是Sentinel在集成到項目中時(shí)需要引入多個(gè)依賴(lài),并且需要閱讀相關(guān)文檔,以及dashboard中的相關(guān)配置才可以接入到項目中,這個(gè)過(guò)程還是較為復雜的。
如果我們的項目并不需要這么多的功能,只是需要當某個(gè)方法或者某個(gè)功能發(fā)生異常的時(shí)候可以實(shí)現降級,并不是直接中斷程序,該業(yè)務(wù)功能不是主流程,那么我們?yōu)榱藢?shí)現這樣一個(gè)小功能的時(shí)候,將Sentinel集成到項目中的過(guò)程顯然是較為復雜的,那么這個(gè)時(shí)候,就需要我們實(shí)現一個(gè)簡(jiǎn)答的功能降級的通用方式,下面就一起看看一個(gè)簡(jiǎn)易版的Sentinel的實(shí)現
當然,實(shí)現這個(gè)功能,只需要一個(gè)try-catch就可以搞定個(gè),但是我們需要的是try-catch嗎?No! 我們需要的是優(yōu)雅~ 我想你也不想看到滿(mǎn)屏的try-catch吧,如果哪天這個(gè)方法無(wú)需降級的時(shí)候,再去一行一行刪代碼嗎?
示例代碼已收錄到Github:
第一步,定義一個(gè)通用注解,這個(gè)注解可以幫助我們無(wú)侵入性的實(shí)現功能降級,并且提供豐富的屬性,讓注解的通用性和靈活性更強
@Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) @Documented @Inherited public @interface DegradeResource { // 降級的方法名稱(chēng) String fallback(); // 降級的類(lèi)名稱(chēng),可選 Class<?>[] fallbackClass() default {}; // 指定降級異常,可選 Class<? extends Throwable>[] exceptionHandle() default {}; }
降級方法須知:
必須為public
方法返回類(lèi)型、方法參數必須和原始方法保持一致,最后一個(gè)參數允許多一個(gè)Throwable,用來(lái)接收發(fā)生的異常
當資源降級注解定義之后,我們就需要一個(gè)切面處理器,對定義的降級注解做切面處理,當調用的方法上有@DegradeResource注解時(shí),會(huì )通過(guò)切面處理器進(jìn)行處理
@Aspect public class DegradeResourceAspect { @Around("@annotation(degradeResource)") public Object doAround(ProceedingJoinPoint pjp, DegradeResource degradeResource) throws Throwable { try { return pjp.proceed(); } catch(Throwable e){ // need to trace exception list Class<? extends Throwable>[] exceptions = degradeResource.exceptionHandle(); if(exceptions.length > 0) { List<Class<? extends Throwable>> exceptionList = Arrays.asList(exceptions); // 判斷是否為同一個(gè)個(gè)異常 if (exceptionBelongTo(e, exceptionList)) { return handleFallbackMethod(pjp, degradeResource, e); } else { throw e; } } return handleFallbackMethod(pjp, degradeResource, e); } } /** * if the throw exception is belong to exception trace list * * @param e * @param exceptionList * @return */ private boolean exceptionBelongTo(Throwable e, List<Class<? extends Throwable>> exceptionList) { for (Class<? extends Throwable> aClass : exceptionList) { if(aClass.isAssignableFrom(e.getClass())) { return true; } } return false; } /** * invoke fallback method * */ private Object handleFallbackMethod(ProceedingJoinPoint pjp, DegradeResource degradeResource, Throwable e) throws Throwable { // fallback method String fallback = degradeResource.fallback(); if(StringUtils.isEmpty(fallback)) { throw e; } // fallback class Class<?> clazz = degradeResource.fallbackClass().length > 0 ? degradeResource.fallbackClass()[0] : pjp.getTarget().getClass(); // 獲取當前執行的方法名稱(chēng) Method fallbackMethod = findFallbackMethod(pjp, clazz, fallback); if(Objects.isNull(fallbackMethod)) { throw e; } // fallback method args Object[] args; Object[] originArgs = pjp.getArgs(); int paramCount = fallbackMethod.getParameterTypes().length; if(originArgs.length == paramCount) { args = originArgs; } else { // fill throwable to fallback method args args = Arrays.copyOf(originArgs, originArgs.length + 1); args[args.length - 1] = e; } // if static if(Modifier.isStatic(fallbackMethod.getModifiers())) { return fallbackMethod.invoke(null, args); } return fallbackMethod.invoke(clazz.newInstance(), args); } private Method findFallbackMethod(ProceedingJoinPoint pjp, Class<?> clazz, String fallbackName) { MethodSignature signers = (MethodSignature) pjp.getSignature(); Class<?>[] originParams = signers.getParameterTypes(); Class<?>[] paramsWithException = Arrays.copyOf(originParams, originParams.length + 1); paramsWithException[paramsWithException.length - 1] = Throwable.class; // find fallback method with origin params Method method = findMethod(clazz, originParams, fallbackName, signers.getReturnType()); if(method == null) { // find fallback method with exception params method = findMethod(clazz, paramsWithException, fallbackName, signers.getReturnType()); } return method; } private Method findMethod(Class<?> clazz, Class<?>[] paramsType, String fallbackName, Class<?> returnType) { Method[] declaredMethods = clazz.getDeclaredMethods(); for (Method method : declaredMethods) { if(method.getName().equals(fallbackName) && returnType.isAssignableFrom(method.getReturnType()) && Arrays.equals(paramsType, method.getParameterTypes())) { return method; } } return null; } }
總體的流程為:當掃描到切面時(shí),第一步先正常執行方法,當方法發(fā)生異常時(shí),判斷當前是否制定異常,如果沒(méi)有指定異常處理類(lèi)型,那么就默認走降級方法,如果當前指定了降級的異常處理類(lèi)型,那么就判斷當前方法拋出的異常是否為需要處理的異常,如果是則調用降級方法,如果不是需要處理的異常,那么就拋出異常。
符合當前場(chǎng)景的需要,簡(jiǎn)單化的異常降級
總共測試了3中方式的異常降級,分別為默認所有異常降級、指定異常降級、指定降級方法的處理類(lèi)
@Service public class DegradeResourceTestService { @DegradeResource(fallback = "findByIdFromCacheFallback1") public String findById(String id) { int i = Integer.parseInt(id); System.out.println("id=" + id); return "ok = " + id; } @DegradeResource(fallback = "findByIdFromCacheFallback2", exceptionHandle = {NumberFormatException.class}) public String findByIdWithException(String id) { int i = Integer.parseInt(id); System.out.println("id=" + id); return "ok = " + id; } /** * 支持指定fallback method的class,將降級方法統一放置指定的class中 * */ @DegradeResource(fallback = "findByIdFromCacheFallback3", exceptionHandle = {NumberFormatException.class}, fallbackClass = {FallbackClassService.class}) public String findByIdWithFallbackClass(String id) { int i = Integer.parseInt(id); System.out.println("id=" + id); return "ok = " + id; } /** * fallback method可以只接受原始函數的參數 */ public String findByIdFromCacheFallback1(String id) { return "fallback1 = " + id; } /** * fallback method 不僅可以接收原始方法的參數,還可以接收具體的Exception * */ public String findByIdFromCacheFallback2(String id, Throwable e) { System.out.println("fallback method exception:" + e); return "fallback2 = " + id; } }
結果:
可以看到,當發(fā)生異常時(shí),可以通過(guò)降級保證主流程的通常,對于非主流程的功能,我們可以通過(guò)@DegradeResource注解保證流程的完善和降級方案,保證用戶(hù)的體驗和程序的正常。
以上就是基于SpringCloud手寫(xiě)一個(gè)簡(jiǎn)易版Sentinel的詳細內容,更多關(guān)于SpringCloud手寫(xiě)Sentinel的資料請關(guān)注腳本之家其它相關(guān)文章!
免責聲明:本站發(fā)布的內容(圖片、視頻和文字)以原創(chuàng )、來(lái)自互聯(lián)網(wǎng)轉載和分享為主,文章觀(guān)點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權請聯(lián)系QQ:712375056 進(jìn)行舉報,并提供相關(guān)證據,一經(jīng)查實(shí),將立刻刪除涉嫌侵權內容。
Copyright ? 2009-2021 56dr.com. All Rights Reserved. 特網(wǎng)科技 特網(wǎng)云 版權所有 珠海市特網(wǎng)科技有限公司 粵ICP備16109289號
域名注冊服務(wù)機構:阿里云計算有限公司(萬(wàn)網(wǎng)) 域名服務(wù)機構:煙臺帝思普網(wǎng)絡(luò )科技有限公司(DNSPod) CDN服務(wù):阿里云計算有限公司 中國互聯(lián)網(wǎng)舉報中心 增值電信業(yè)務(wù)經(jīng)營(yíng)許可證B2
建議您使用Chrome、Firefox、Edge、IE10及以上版本和360等主流瀏覽器瀏覽本網(wǎng)站