重试机制!javaretry(重试)springretry,guavaretrying详解⼩Hub领读:
⼩明同学与产品经理的⽃智⽃勇过程,当接⼝有时候异常想重试,你会怎么办?随着需求的不断提出,怎么去迭代升级,看看这篇⽂章,写得很好!!
⼀定要看完,哈哈!然后点个在看。
作者:叶⽌⽔
系列说明
java retry 的⼀步步实现机制。
情景导⼊
简单的需求
产品经理:实现⼀个按条件,查询⽤户信息的服务。
⼩明:好的。没问题。
代码
UserService.java
public interface UserService {
/**
* 根据条件查询⽤户信息
* @param condition 条件
* @return User 信息
*/
User queryUser(QueryUserCondition condition);
}
UserServiceImpl.java
public class UserServiceImpl implements UserService {
private OutService outService;
public UserServiceImpl(OutService outService) {
this.outService = outService;
}
@Override
public User queryUser(QueryUserCondition condition) {
return new User();
}
}
谈话
项⽬经理:这个服务有时候会失败,你看下。
⼩明:OutService 在是⼀个 RPC 的外部服务,但是有时候不稳定。
项⽬经理:如果调⽤失败了,你可以调⽤的时候重试⼏次。你去看下重试相关的东西
重试
重试作⽤
对于重试是有场景限制的,不是什么场景都适合重试,⽐如参数校验不合法、写操作等(要考虑写是否幂等)都不适合重试。
远程调⽤超时、⽹络突然中断可以重试。在微服务治理框架中,通常都有⾃⼰的重试与超时配置,⽐如dubbo可以设置retries=1,timeout=500调⽤失败只重试1次,超过500ms调⽤仍未返回则调⽤失败。
⽐如外部 RPC 调⽤,或者数据⼊库等操作,如果⼀次操作失败,可以进⾏多次重试,提⾼调⽤成功的可能性。
V1.0 ⽀持重试版本
思考
⼩明:我⼿头还有其他任务,这个也挺简单的。5 分钟时间搞定他。
实现
UserServiceRetryImpl.java
public class UserServiceRetryImpl implements UserService {
@Override
public User queryUser(QueryUserCondition condition) {
int times = 0;
OutService outService = new AlwaysFailOutServiceImpl();
while (times < RetryConstant.MAX_TIMES) {
try {
return new User();
} catch (Exception e) {
times++;
if(times >= RetryConstant.MAX_TIMES) {
throw new RuntimeException(e);
}
}
}
return null;
}
}
V1.1 代理模式版本
易于维护
项⽬经理:你的代码我看了,功能虽然实现了,但是尽量写的易于维护⼀点。
⼩明:好的。(⼼想,是说要写点注释什么的?)
为其他对象提供⼀种代理以控制对这个对象的访问。
在某些情况下,⼀个对象不适合或者不能直接引⽤另⼀个对象,⽽代理对象可以在客户端和⽬标对象之间起到中介作⽤。其特征是代理与委托类有同样的接⼝。
实现
⼩明想到以前看过的代理模式,⼼想⽤这种⽅式,原来的代码改动量较少,以后想改起来也⽅便些。
UserServiceProxyImpl.java
public class UserServiceProxyImpl implements UserService {
private UserService userService = new UserServiceImpl();
@Override
public User queryUser(QueryUserCondition condition) {
int times = 0;
while (times < RetryConstant.MAX_TIMES) {
try {
return userService.queryUser(condition);
} catch (Exception e) {
times++;
if(times >= RetryConstant.MAX_TIMES) {
throw new RuntimeException(e);
}
java dubbo}
}
return null;
}
}
V1.2 动态代理模式
⽅便拓展
项⽬经理:⼩明啊,这⾥还有个⽅法也是同样的问题。你也给加上重试吧。
⼩明:好的。
⼩明⼼想,我在写⼀个代理,但是转念冷静了下来,如果还有个服务也要重试怎么办呢?
RoleService.java
public interface RoleService {
/**
* 查询
* @param user ⽤户信息
* @return 是否拥有权限
*/
boolean hasPrivilege(User user);
}
DynamicProxy.java
public class DynamicProxy implements InvocationHandler {
private final Object subject;
public DynamicProxy(Object subject) {
this.subject = subject;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
int times = 0;
while (times < RetryConstant.MAX_TIMES) {
try {
// 当代理对象调⽤真实对象的⽅法时,其会⾃动的跳转到代理对象关联的handler对象的invoke⽅法来进⾏调⽤ return method.invoke(subject, args);
} catch (Exception e) {
times++;
if (times >= RetryConstant.MAX_TIMES) {
throw new RuntimeException(e);
}
}
}
return null;
}
/**
* 获取动态代理
*
* @param realSubject 代理对象
*/
public static Object getProxy(Object realSubject) {
// 我们要代理哪个真实对象,就将该对象传进去,最后是通过该真实对象来调⽤其⽅法的
InvocationHandler handler = new DynamicProxy(realSubject);
Class().getClassLoader(),
}
}
测试代码
@Test
public void failUserServiceTest() {
UserService realService = new UserServiceImpl();
UserService proxyService = (UserService) Proxy(realService);
User user = proxyService.queryUser(new QueryUserCondition());
LOGGER.info("failUserServiceTest: " + user);
}
@Test
public void roleServiceTest() {
RoleService realService = new RoleServiceImpl();
RoleService proxyService = (RoleService) Proxy(realService);
boolean hasPrivilege = proxyService.hasPrivilege(new User());
LOGGER.info("roleServiceTest: " + hasPrivilege);
}
V1.3 动态代理模式增强
对话
项⽬经理:⼩明,你动态代理的⽅式是挺会偷懒的,可是我们有的类没有接⼝。这个问题你要解决⼀下。⼩明:好的。(谁?写服务竟然不定义接⼝)
ResourceServiceImpl.java
public class ResourceServiceImpl {
/**
* 校验资源信息
* @param user ⼊参
* @return 是否校验通过
*/
public boolean checkResource(User user) {
OutService outService = new AlwaysFailOutServiceImpl();
return true;
}
}
字节码技术
⼩明看了下⽹上的资料,解决的办法还是有的。
CGLIB
CGLIB 是⼀个功能强⼤、⾼性能和⾼质量的代码⽣成库,⽤于扩展JAVA类并在运⾏时实现接⼝。
javassist
javassist (Java编程助⼿)使Java字节码操作变得简单。
它是Java中编辑字节码的类库;它允许Java程序在运⾏时定义新类,并在JVM加载类⽂件时修改类⽂件。与其他类似的字节码编辑器不同,Javassist提供了两个级别的API:源级和字节码级。
如果⽤户使⽤源代码级API,他们可以编辑类⽂件,⽽不需要了解Java字节码的规范。
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论