java retry重试 spring retry guava retrying 详解.docx

上传人:b****7 文档编号:16556682 上传时间:2023-07-14 格式:DOCX 页数:33 大小:77.79KB
下载 相关 举报
java retry重试 spring retry guava retrying 详解.docx_第1页
第1页 / 共33页
java retry重试 spring retry guava retrying 详解.docx_第2页
第2页 / 共33页
java retry重试 spring retry guava retrying 详解.docx_第3页
第3页 / 共33页
java retry重试 spring retry guava retrying 详解.docx_第4页
第4页 / 共33页
java retry重试 spring retry guava retrying 详解.docx_第5页
第5页 / 共33页
java retry重试 spring retry guava retrying 详解.docx_第6页
第6页 / 共33页
java retry重试 spring retry guava retrying 详解.docx_第7页
第7页 / 共33页
java retry重试 spring retry guava retrying 详解.docx_第8页
第8页 / 共33页
java retry重试 spring retry guava retrying 详解.docx_第9页
第9页 / 共33页
java retry重试 spring retry guava retrying 详解.docx_第10页
第10页 / 共33页
java retry重试 spring retry guava retrying 详解.docx_第11页
第11页 / 共33页
java retry重试 spring retry guava retrying 详解.docx_第12页
第12页 / 共33页
java retry重试 spring retry guava retrying 详解.docx_第13页
第13页 / 共33页
java retry重试 spring retry guava retrying 详解.docx_第14页
第14页 / 共33页
java retry重试 spring retry guava retrying 详解.docx_第15页
第15页 / 共33页
java retry重试 spring retry guava retrying 详解.docx_第16页
第16页 / 共33页
java retry重试 spring retry guava retrying 详解.docx_第17页
第17页 / 共33页
java retry重试 spring retry guava retrying 详解.docx_第18页
第18页 / 共33页
java retry重试 spring retry guava retrying 详解.docx_第19页
第19页 / 共33页
java retry重试 spring retry guava retrying 详解.docx_第20页
第20页 / 共33页
亲,该文档总共33页,到这儿已超出免费预览范围,如果喜欢就下载吧!
下载资源
资源描述

java retry重试 spring retry guava retrying 详解.docx

《java retry重试 spring retry guava retrying 详解.docx》由会员分享,可在线阅读,更多相关《java retry重试 spring retry guava retrying 详解.docx(33页珍藏版)》请在冰点文库上搜索。

java retry重试 spring retry guava retrying 详解.docx

javaretry重试springretryguavaretrying详解

系列说明

javaretry的一步步实现机制。

java-retry源码地址

情景导入

简单的需求

产品经理:

实现一个按条件,查询用户信息的服务。

小明:

好的。

没问题。

代码

•UserService.java

publicinterfaceUserService{

/**

*根据条件查询用户信息

*@paramcondition条件

*@returnUser信息

*/

UserqueryUser(QueryUserConditioncondition);

}

•UserServiceImpl.java

publicclassUserServiceImplimplementsUserService{

privateOutServiceoutService;

publicUserServiceImpl(OutServiceoutService){

this.outService=outService;

}

@Override

publicUserqueryUser(QueryUserConditioncondition){

outService.remoteCall();

returnnewUser();

}

}

谈话

项目经理:

这个服务有时候会失败,你看下。

小明:

OutService在是一个RPC的外部服务,但是有时候不稳定。

项目经理:

如果调用失败了,你可以调用的时候重试几次。

你去看下重试相关的东西

重试

重试作用

对于重试是有场景限制的,不是什么场景都适合重试,比如参数校验不合法、写操作等(要考虑写是否幂等)都不适合重试。

远程调用超时、网络突然中断可以重试。

在微服务治理框架中,通常都有自己的重试与超时配置,比如dubbo可以设置retries=1,timeout=500调用失败只重试1次,超过500ms调用仍未返回则调用失败。

比如外部RPC调用,或者数据入库等操作,如果一次操作失败,可以进行多次重试,提高调用成功的可能性。

V1.0支持重试版本

思考

小明:

我手头还有其他任务,这个也挺简单的。

5分钟时间搞定他。

实现

•UserServiceRetryImpl.java

publicclassUserServiceRetryImplimplementsUserService{

@Override

publicUserqueryUser(QueryUserConditioncondition){

inttimes=0;

OutServiceoutService=newAlwaysFailOutServiceImpl();

while(times

try{

outService.remoteCall();

returnnewUser();

}catch(Exceptione){

times++;

if(times>=RetryConstant.MAX_TIMES){

thrownewRuntimeException(e);

}

}

}

returnnull;

}

}

V1.1代理模式版本

易于维护

项目经理:

你的代码我看了,功能虽然实现了,但是尽量写的易于维护一点。

小明:

好的。

(心想,是说要写点注释什么的?

代理模式

为其他对象提供一种代理以控制对这个对象的访问。

在某些情况下,一个对象不适合或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介作用。

其特征是代理与委托类有同样的接口。

实现

小明想到以前看过的代理模式,心想用这种方式,原来的代码改动量较少,以后想改起来也方便些。

•UserServiceProxyImpl.java

publicclassUserServiceProxyImplimplementsUserService{

privateUserServiceuserService=newUserServiceImpl();

@Override

publicUserqueryUser(QueryUserConditioncondition){

inttimes=0;

while(times

try{

returnuserService.queryUser(condition);

}catch(Exceptione){

times++;

if(times>=RetryConstant.MAX_TIMES){

thrownewRuntimeException(e);

}

}

}

returnnull;

}

}

V1.2动态代理模式

方便拓展

项目经理:

小明啊,这里还有个方法也是同样的问题。

你也给加上重试吧。

小明:

好的。

小明心想,我在写一个代理,但是转念冷静了下来,如果还有个服务也要重试怎么办呢?

•RoleService.java

publicinterfaceRoleService{

/**

*查询

*@paramuser用户信息

*@return是否拥有权限

*/

booleanhasPrivilege(Useruser);

}

代码实现

•DynamicProxy.java

publicclassDynamicProxyimplementsInvocationHandler{

privatefinalObjectsubject;

publicDynamicProxy(Objectsubject){

this.subject=subject;

}

@Override

publicObjectinvoke(Objectproxy,Methodmethod,Object[]args)throwsThrowable{

inttimes=0;

while(times

try{

//当代理对象调用真实对象的方法时,其会自动的跳转到代理对象关联的handler对象的invoke方法来进行调用

returnmethod.invoke(subject,args);

}catch(Exceptione){

times++;

if(times>=RetryConstant.MAX_TIMES){

thrownewRuntimeException(e);

}

}

}

returnnull;

}

/**

*获取动态代理

*

*@paramrealSubject代理对象

*/

publicstaticObjectgetProxy(ObjectrealSubject){

//我们要代理哪个真实对象,就将该对象传进去,最后是通过该真实对象来调用其方法的

InvocationHandlerhandler=newDynamicProxy(realSubject);

returnProxy.newProxyInstance(handler.getClass().getClassLoader(),

realSubject.getClass().getInterfaces(),handler);

}

}

•测试代码

@Test

publicvoidfailUserServiceTest(){

UserServicerealService=newUserServiceImpl();

UserServiceproxyService=(UserService)DynamicProxy.getProxy(realService);

Useruser=proxyService.queryUser(newQueryUserCondition());

LOGGER.info("failUserServiceTest:

"+user);

}

@Test

publicvoidroleServiceTest(){

RoleServicerealService=newRoleServiceImpl();

RoleServiceproxyService=(RoleService)DynamicProxy.getProxy(realService);

booleanhasPrivilege=proxyService.hasPrivilege(newUser());

LOGGER.info("roleServiceTest:

"+hasPrivilege);

}

V1.3动态代理模式增强

对话

项目经理:

小明,你动态代理的方式是挺会偷懒的,可是我们有的类没有接口。

这个问题你要解决一下。

小明:

好的。

(谁?

写服务竟然不定义接口)

•ResourceServiceImpl.java

publicclassResourceServiceImpl{

/**

*校验资源信息

*@paramuser入参

*@return是否校验通过

*/

publicbooleancheckResource(Useruser){

OutServiceoutService=newAlwaysFailOutServiceImpl();

outService.remoteCall();

returntrue;

}

}

字节码技术

小明看了下网上的资料,解决的办法还是有的。

•CGLIB

CGLIB是一个功能强大、高性能和高质量的代码生成库,用于扩展JAVA类并在运行时实现接口。

•javassist

javassist(Java编程助手)使Java字节码操作变得简单。

它是Java中编辑字节码的类库;它允许Java程序在运行时定义新类,并在JVM加载类文件时修改类文件。

与其他类似的字节码编辑器不同,Javassist提供了两个级别的API:

源级和字节码级。

如果用户使用源代码级API,他们可以编辑类文件,而不需要了解Java字节码的规范。

整个API只使用Java语言的词汇表进行设计。

您甚至可以以源文本的形式指定插入的字节码;Javassist动态编译它。

另一方面,字节码级API允许用户直接编辑类文件作为其他编辑器。

•ASM

ASM是一个通用的Java字节码操作和分析框架。

它可以用来修改现有的类或动态地生成类,直接以二进制形式。

ASM提供了一些通用的字节码转换和分析算法,可以从这些算法中构建自定义复杂的转换和代码分析工具。

ASM提供与其他Java字节码框架类似的功能,但主要关注性能。

因为它的设计和实现都尽可能地小和快,所以非常适合在动态系统中使用(当然也可以以静态的方式使用,例如在编译器中)。

实现

小明看了下,就选择使用CGLIB。

•CglibProxy.java

publicclassCglibProxyimplementsMethodInterceptor{

@Override

publicObjectintercept(Objecto,Methodmethod,Object[]objects,MethodProxymethodProxy)throwsThrowable{

inttimes=0;

while(times

try{

//通过代理子类调用父类的方法

returnmethodProxy.invokeSuper(o,objects);

}catch(Exceptione){

times++;

if(times>=RetryConstant.MAX_TIMES){

thrownewRuntimeException(e);

}

}

}

returnnull;

}

/**

*获取代理类

*@paramclazz类信息

*@return代理类结果

*/

publicObjectgetProxy(Classclazz){

Enhancerenhancer=newEnhancer();

//目标对象类

enhancer.setSuperclass(clazz);

enhancer.setCallback(this);

//通过字节码技术创建目标对象类的子类实例作为代理

returnenhancer.create();

}

}

•测试

@Test

publicvoidfailUserServiceTest(){

UserServiceproxyService=(UserService)newCglibProxy().getProxy(UserServiceImpl.class);

Useruser=proxyService.queryUser(newQueryUserCondition());

LOGGER.info("failUserServiceTest:

"+user);

}

@Test

publicvoidresourceServiceTest(){

ResourceServiceImplproxyService=(ResourceServiceImpl)newCglibProxy().getProxy(ResourceServiceImpl.class);

booleanresult=proxyService.checkResource(newUser());

LOGGER.info("resourceServiceTest:

"+result);

}

V2.0AOP实现

对话

项目经理:

小明啊,最近我在想一个问题。

不同的服务,重试的时候次数应该是不同的。

因为服务对稳定性的要求各不相同啊。

小明:

好的。

(心想,重试都搞了一周了,今天都周五了。

下班之前,小明一直在想这个问题。

刚好周末,花点时间写个重试小工具吧。

设计思路

•技术支持

spring

java注解

•注解定义

注解可在方法上使用,定义需要重试的次数

•注解解析

拦截指定需要重试的方法,解析对应的重试次数,然后进行对应次数的重试。

实现

•Retryable.java

@Target({ElementType.METHOD})

@Retention(RetentionPolicy.RUNTIME)

@Documented

public@interfaceRetryable{

/**

*Exceptiontypethatareretryable.

*@returnexceptiontypetoretry

*/

Class

extendsThrowable>value()defaultRuntimeException.class;

/**

*包含第一次失败

*@returnthemaximumnumberofattempts(includingthefirstfailure),defaultsto3

*/

intmaxAttempts()default3;

}

•RetryAspect.java

@Aspect

@Component

publicclassRetryAspect{

@Pointcut("execution(public*com.github.houbb.retry.aop..*.*(..))&&"+

"@annotation(com.github.houbb.retry.aop.annotation.Retryable)")

publicvoidmyPointcut(){

}

@Around("myPointcut()")

publicObjectaround(ProceedingJoinPointpoint)throwsThrowable{

Methodmethod=getCurrentMethod(point);

Retryableretryable=method.getAnnotation(Retryable.class);

//1.最大次数判断

intmaxAttempts=retryable.maxAttempts();

if(maxAttempts<=1){

returnpoint.proceed();

}

//2.异常处理

inttimes=0;

finalClass

extendsThrowable>exceptionClass=retryable.value();

while(times

try{

returnpoint.proceed();

}catch(Throwablee){

times++;

//超过最大重试次数or不属于当前处理异常

if(times>=maxAttempts||

!

e.getClass().isAssignableFrom(exceptionClass)){

thrownewThrowable(e);

}

}

}

returnnull;

}

privateMethodgetCurrentMethod(ProceedingJoinPointpoint){

try{

Signaturesig=point.getSignature();

MethodSignaturemsig=(MethodSignature)sig;

Objecttarget=point.getTarget();

returntarget.getClass().getMethod(msig.getName(),msig.getParameterTypes());

}catch(NoSuchMethodExceptione){

thrownewRuntimeException(e);

}

}

}

方法的使用

•fiveTimes()

当前方法一共重试5次。

重试条件:

服务抛出AopRuntimeExption

@Override

@Retryable(maxAttempts=5,value=AopRuntimeExption.class)

publicvoidfiveTimes(){

LOGGER.info("fiveTimescalled!

");

thrownewAopRuntimeExption();

}

•测试日志

2018-08-0815:

49:

33.814INFO[main]com.github.houbb.retry.aop.service.impl.UserServiceImpl:

66-fiveTimescalled!

2018-08-0815:

49:

33.815INFO[main]com.github.houbb.retry.aop.service.impl.UserServiceImpl:

66-fiveTimescalled!

2018-08-0815:

49:

33.815INFO[main]com.github.houbb.retry.aop.service.impl.UserServiceImpl:

66-fiveTimescalled!

2018-08-0815:

49:

33.815INFO[main]com.github.houbb.retry.aop.service.impl.UserServiceImpl:

66-fiveTimescalled!

2018-08-0815:

49:

33.815INFO[main]com.github.houbb.retry.aop.service.impl.UserServiceImpl:

66-fiveTimescalled!

java.lang.reflect.UndeclaredThrowableException

...

V3.0spring-retry版本

对话

周一来到公司,项目经理又和小明谈了起来。

项目经理:

重试次数是满足了,但是重试其实应该讲究策略。

比如调用外部,第一次失败,可以等待5S在次调用,如果又失败了,可以等待10S再调用。

小明:

了解。

思考

可是今天周一,还有其他很多事情要做。

小明在想,没时间写这个呀。

看看网上有没有现成的。

spring-retry

SpringRetry为Spring应用程序提供了声明性重试支持。

它用于Spring批处理、Spring集成、ApacheHadoop(等等)的Spring。

在分布式系统中,为了保证数据分布式事务的强一致性,大家在调用RPC接口或者发送MQ时,针对可能会出现网络抖动请求超时情况采取一下重试操作。

大家用的最多的重试方式就是MQ了,但是如果你的项目中没有引入MQ,那就不方便了。

还有一种方式,是开发者自己编写重试机制,但是大多不够优雅。

注解式使用

•RemoteService.java

重试条件:

遇到RuntimeException

重试次数:

3

重试策略:

重试的时候等待5S,后面时间依次变为原来的2倍数。

熔断机制:

全部重试失败,则调用recover()方法。

@Service

publicclassRemoteService{

privatestaticfinalLoggerLOGGER=LoggerFactory.getLogger(RemoteService.class);

/**

*调用方法

*/

@Retryable(value=RuntimeException.class,

maxAttempts=3,

backoff=@Backoff(delay=5000L,multiplier=2))

publicvoidcall(){

LOGGER.info("Callsomething...");

thrownewRuntimeException("RPC调用异常");

}

/**

*recove

展开阅读全文
相关资源
猜你喜欢
相关搜索
资源标签

当前位置:首页 > 经管营销

copyright@ 2008-2023 冰点文库 网站版权所有

经营许可证编号:鄂ICP备19020893号-2