最近给我的rpc框架添加了声明式调用的功能。
这里遇到最大的难题就是如何去查找需要实现的接口,如何将接口动态的实现,并将实现类实例对象加载到springIOC容器中。
这里我们需要一个自定义的类实现三个接口ImportBeanDefinitionRegistrar(帮助我们注册BeanDefinition),ResourceLoaderAware(获取到资源加载器), EnvironmentAware(获取到当前环境)。
在这里environment的主要作用是帮助我们获取到候选组件提供者
resourceLoader的主要作用是提供资源加载器,获取到类加载的路径
这里需要插一嘴,如果这个加载类是通过spring.factories文件引入的话(本质是被@SpringbootApplication注解引入,那我们就拿不到我们自己的注解@SyyrjxRpcCallEnable注解所指定的类,所以@SyyrjxRpcCallEnable需要有@Import注解来引入这个加载类)
public class SyyrjxRpcCallLoadAware implements ImportBeanDefinitionRegistrar,
ResourceLoaderAware, EnvironmentAware, ApplicationContextAware {
private final static Logger LOGGER = LoggerFactory.getLogger(SyyrjxRpcCallLoadAware.class);
private ResourceLoader resourceLoader;
private Environment environment;
@Override
public void setEnvironment(Environment environment) {
LOGGER.debug("载入环境");
this.environment = environment;
}
@Override
public void setResourceLoader(ResourceLoader resourceLoader) {
LOGGER.debug("载入资源加载器");
try {
LOGGER.debug("加载路径为" + resourceLoader.getResource("").getURI());
} catch (IOException e) {
e.printStackTrace();
}
this.resourceLoader = resourceLoader;
}
@Override
public void registerBeanDefinitions(AnnotationMetadata metadata
, BeanDefinitionRegistry registry) {
LOGGER.debug("进行接口声明式调用创建");
ClassPathScanningCandidateComponentProvider scanner = getScanner();
scanner.setResourceLoader(this.resourceLoader);
//获取启动注解标注的接口类名数组
Map attributes = metadata.getAnnotationAttributes(SyyrjxRpcCallEnable.class.getName());
Class>[] callClientsClassesArray = ((Class>[])attributes.get("value"));
Set> callClientClassSet = new HashSet<>();
if (callClientsClassesArray != null) {
callClientClassSet.addAll(Arrays.asList(callClientsClassesArray));
}
//遍历接口类对象数组,实现接口并注册为BeanDefinition
for (Class> callClientClass : callClientClassSet) {
//获取Bean创建工厂(这里的工厂需要我们组件定义)
BeanDefinitionBuilder definition = BeanDefinitionBuilder.genericBeanDefinition(SyyrjxRpcCallClientFactoryBean.class);
definition.addConstructorArgValue(callClientClass);
//为Bean创建工厂提供构造参数
long timeout = 0;
Class extends Exception>[] ignoreExceptions = new Class[0];
SyyrjxRpcBreaker callClientAnnotation = callClientClass.getAnnotation(SyyrjxRpcBreaker.class);
if (callClientAnnotation != null) {
definition.addConstructorArgValue(true);
timeout = callClientAnnotation.timeout();
ignoreExceptions = callClientAnnotation.ignoreException();
}
definition.addConstructorArgValue(timeout);
definition.addConstructorArgValue(ignoreExceptions);
//创建BeanDefinition
AbstractBeanDefinition handleDefinition = definition.getBeanDefinition();
handleDefinition.setPrimary(true);
//注册BeanDefinication
BeanDefinitionHolder holder = new BeanDefinitionHolder(handleDefinition, callClientClass.getName());
BeanDefinitionReaderUtils.registerBeanDefinition(holder, registry);
}
LOGGER.debug("代理实现类bean注册完毕");
}
private ClassPathScanningCandidateComponentProvider getScanner() {
return new ClassPathScanningCandidateComponentProvider(false, this.environment);
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
LOGGER.debug("上下文加载完毕后置处理");
}
}
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import({xyz.syyrjx.call.load.SyyrjxRpcCallLoadAware.class,xyz.syyrjx.call.load.SyyrjxRpcCallApplicationContextAware.class})
public @interface SyyrjxRpcCallEnable {
Class>[] value() default {};
}
我们既然需要动态的实现接口,那么肯定需要使用到动态代理模式。当我们去调用接口的某个方法的时候,我们实际调用的是代理类实现的方法。
@Data
@NoArgsConstructor
@AllArgsConstructor
public class SyyrjxRpcCallClientFactoryBean implements FactoryBean
这里是动态代理的内容,InvocationHandler的invoke()方法是代理方法,当调用被代理类的某个方法是,实际是通过invoke()去调用这个方法,所以我们可以吧对接口方法的实现写在invoke()中。
@Data
@NoArgsConstructor
@AllArgsConstructor
public class SyyrjxRpcCallClientProxy implements InvocationHandler {
private final static Logger LOGGER = LoggerFactory.getLogger(SyyrjxRpcCallClientProxy.class);
//声明式调用接口
private Class> interfaceClass;
//降级类对象
private Object fallbackEntity;
//超时时间
private long timeout;
//需要忽略的异常
private Class extends Exception>[] ignoreExpects;
//接口及其连续失败次数映射表
private final static Map, Integer> API_FAIL_COUNT_MAP = new ConcurrentHashMap<>();
@Setter
private static SyyrjxRpcConsumerClient client;
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if (!API_FAIL_COUNT_MAP.containsKey(interfaceClass)) {
API_FAIL_COUNT_MAP.put(interfaceClass, 0);
}
int count = API_FAIL_COUNT_MAP.get(interfaceClass);
Class extends Exception>[] methodIgnore = null;
long methodTimeout = 0;
SyyrjxRpcBreaker breaker = method.getAnnotation(SyyrjxRpcBreaker.class);
if (breaker != null) {
methodIgnore = breaker.ignoreException();
methodTimeout = breaker.timeout();
}
long useTimeout = methodTimeout > this.timeout ? methodTimeout : this.timeout;
//连续调用十次出现问题,熔断该服务,不再远程调用
if (count < 10) {
try {
LOGGER.debug("代理调用发送服务");
Object res = client.remoteCall(interfaceClass.getName(), method.getName(), useTimeout, args);
count -= 1;
API_FAIL_COUNT_MAP.put(interfaceClass, (count > 0 ? count : 0));
return res;
} catch (Exception e) {
//确定抛出的异常
Throwable caues = e;
if (Objects.equals(e.getClass(), java.util.concurrent.ExecutionException.class)){
caues = caues.getCause();
}
//不需要回调或者异常被忽略
if (fallbackEntity == null
|| isIgnore(caues,this.ignoreExpects)
|| isIgnore(caues,methodIgnore)) {
throw new SyyrjxRpcBreakerException$RemoteCallException();
} else {
//确定是超时还是其它异常
API_FAIL_COUNT_MAP.put(interfaceClass, count + 1);
return fallback(caues, method, args);
}
}
} else {
return fallback(new SyyrjxRpcBreakerException(SyyrjxRpcBreakerException.SERVICE_HAS_BEEN_BREAK)
, method, args);
}
}
public Object fallback(Throwable caues, Method method, Object[] args) throws InvocationTargetException, IllegalAccessException {
String fallbackMethodName = method.getName();
Object[] newArgs = args;
if (Objects.equals(caues.getClass(), java.util.concurrent.TimeoutException.class)) {
fallbackMethodName += "Timeout";
}else {
fallbackMethodName += "Exception";
//调整入参
int length = 1;
if (args != null){
length = args.length + 1;
}
newArgs = new Object[length];
newArgs[0] = caues;
for (int i = 1; i < length; i++) {
newArgs[i] = args[i - 1];
}
}
//拿到回调方法
Method[] methods = fallbackEntity.getClass().getMethods();
Method fallbackMethod = null;
for (Method m : methods) {
if (Objects.equals(m.getName(), fallbackMethodName)) {
fallbackMethod = m;
break;
}
}
if (fallbackMethod == null) {
throw new SyyrjxRpcBreakerException(SyyrjxRpcBreakerException.NOT_DEFINE_FALLBACK_METHOD);
}
return fallbackMethod.invoke(fallbackEntity, newArgs);
}
private boolean isIgnore(Throwable throwable, Class extends Exception>[] array) {
LOGGER.debug("忽略异常判断:" + throwable);
LOGGER.debug("忽略列表:" + Arrays.toString(array));
//获取当前异常的全部父类
List> throwableParents = new ArrayList<>();
throwableParents.add(throwable.getClass());
for (int i = 0; i < throwableParents.size(); i++){
Class> currentClass = throwableParents.get(i);
Class> superclass = currentClass.getSuperclass();
if (!Objects.equals(superclass, Object.class)) {
throwableParents.add(superclass);
}
}
//判断是否需要忽略
if (array != null) {
for (Class extends Exception> ignoreExpect : array) {
if (throwableParents.contains(ignoreExpect)) {
return true;
}
}
}
return false;
}
}
文末附上rpc的git地址:
syyrjx-rpc: 手写一个简单的rpc框架 - Gitee.com