自己写了一个简单的rpc框架-声明式调用的实现

最近给我的rpc框架添加了声明式调用的功能。

这里遇到最大的难题就是如何去查找需要实现的接口,如何将接口动态的实现,并将实现类实例对象加载到springIOC容器中。

一、如何查找需要实现的接口、如何将实现类的实例对象加载到spring容器中

这里我们需要一个自定义的类实现三个接口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[] 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 {};
}

二、如何实现接口

我们既然需要动态的实现接口,那么肯定需要使用到动态代理模式。当我们去调用接口的某个方法的时候,我们实际调用的是代理类实现的方法。

Bean创建工厂

@Data
@NoArgsConstructor
@AllArgsConstructor
public class SyyrjxRpcCallClientFactoryBean implements FactoryBean, InitializingBean {

    private Class interfaceClass;

    private boolean needFallback;

    private long timeout;

    private Class[] ignoreExceptions;

    @Override
    public Object getObject() throws Exception {
        //为代理类提供参数
        SyyrjxRpcCallClientProxy proxy = new SyyrjxRpcCallClientProxy();
        proxy.setInterfaceClass(interfaceClass);
        if (needFallback) {
            String fallbackClassName = interfaceClass.getName() + "Fallback";
            Class fallbackClass = Class.forName(fallbackClassName);
            Object fallbackEntity = fallbackClass.getConstructor().newInstance();
            proxy.setFallbackEntity(fallbackEntity);
            proxy.setIgnoreExpects(ignoreExceptions);
            proxy.setTimeout(timeout);
        }

        //Bean对象通过JDK动态代理生成
        return Proxy.newProxyInstance(interfaceClass.getClassLoader(), new Class[]{interfaceClass}, proxy);
    }

    @Override
    public Class getObjectType() {
        //Bean的类型
        return interfaceClass;
    }

    @Override
    public void afterPropertiesSet() throws Exception {

    }
}

代理类

这里是动态代理的内容,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[] 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[] 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[] 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 ignoreExpect : array) {
                if (throwableParents.contains(ignoreExpect)) {
                    return true;
                }
            }
        }
        return false;
    }
}

文末附上rpc的git地址:

syyrjx-rpc: 手写一个简单的rpc框架 - Gitee.com

你可能感兴趣的:(java,spring,开发语言,rpc)