手撸RPC分布式框架

手撸分布式框架


缘起

一直在用各种各样的rpc框架,如spring cloud Dubbo 等等。但只是使用未免理解不深,感触不浓。有时候就想自己试着去实现一个简单的rpc框架。虽然是个简陋的丑轮子,但是对于个人的成长还是很有帮助的。这篇博客就是为了记录一下实现过程中的思路和遇到的问题。


思路

首先考虑的是rpc框架最核心的部分:远程过程调用 。像spring cloud一般都是用http协议为基础,json作为序列化协议。Dubbo则通常基于TCP长链接,Hessian二进制序列化协议,内部使用的是mina作为异步通信框架。正好最近学习了netty的源码。于是决定使用netty作为我自己的rpc通信框架。由于作为远程调用,具体的实现方法在服务端,本地通过通信框架将参数和结果交互。自然想到使用代理,代理类封装通信方法“伪装”成服务端的服务类,给客户端调用。这里我使用了jdk自带的动态代理方法实现。至于Bean的生命周期都交给spring来托管想必能省不少的事情。整体架构类似Dubbo,至此雏形的思路已经可以开始实现了。
实现流程
结构示意图


问题点拆分

  1. 服务类动态代理->动态代理的类加入springContext->扫描指定服务包(已实现)
  2. netty的使用,复用(序列化机制与选择),长连接(异步转同步)确定对应线程对应返回值(已实现)
  3. 实现服务发现与下线,负载均衡(考虑解决方案,使用redis 做服务发现中心,服务生产者定时注册本服务信息并设置过期时间到redis上,消费者定时同步redis上的信息到本地。同时消费者监听redis过期回调,当有服务自动下线时,修改本地存储的服务信息。)

具体实现

问题1实现

动态代理的类加入springContext & 扫描指定服务包

/**
 * @Auther: xut
 * @Date: 2019/7/24 18:22
 * @Description:
 */
@Component
public class InterfaceBeanRegistry implements BeanDefinitionRegistryPostProcessor, ResourceLoaderAware, ApplicationContextAware {

    public static ResourceLoader resourceLoader;


    @Override
    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry beanDefinitionRegistry) throws BeansException {
        Set<Class<?>> classSet = new HashSet();
        try {
        //扫描该包路径下的接口类
            classSet = packageScan("xut/nettyrpc/demo/service/");
        } catch (IOException e) {
            e.printStackTrace();
        }
        classSet.stream().forEach(i->{
            BeanDefinitionBuilder beanDefinitionBuilder= BeanDefinitionBuilder.genericBeanDefinition(i);
            GenericBeanDefinition definition = (GenericBeanDefinition)beanDefinitionBuilder.getRawBeanDefinition();
            //给对应对象属性注入实例
            definition.getConstructorArgumentValues().addGenericArgumentValue(i);
            //这里set的是一个bean工厂,工厂的构造函数是当前代理的bean
            definition.setBeanClass(RPCServiceFactory.class);
            //设置发现的类型
            definition.setAutowireMode(GenericBeanDefinition.AUTOWIRE_BY_TYPE);
            // 将代理实例注册到spring的容器中
            beanDefinitionRegistry.registerBeanDefinition(i.getSimpleName(), definition);

        });

    }

    @Override
    //在beanDenifition加载完之后,bean实例化之前加载该方法。可以修改bean的默认配置
    public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {

    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {

    }

    @Override
    //用于包扫描
    //spring包扫描机制主要基于jdk的ClassLoader.getResources()或ClassLoader.getSystemResources()实现的,但根据不同打包情况资源解析规则会有变化
    public void setResourceLoader(ResourceLoader resourceLoader) {
        this.resourceLoader = resourceLoader;
    }

    private Set<Class<?>> packageScan(String path) throws IOException {
        ResourcePatternResolver resolver = ResourcePatternUtils.getResourcePatternResolver(resourceLoader);
        MetadataReaderFactory metaReader = new CachingMetadataReaderFactory(resourceLoader);
        Resource[] resources = resolver.getResources("classpath*:"+path+"*.class");
        return Arrays.stream(resources).map(i -> {
            Class<?> interfaceClass = null;
                try {
                    String className = metaReader.getMetadataReader(i).getClassMetadata().getClassName();
                    interfaceClass = Class.forName(className);
                } catch (IOException e) {
                    e.printStackTrace();
                } catch (ClassNotFoundException r){
                    r.printStackTrace();
                }
            return interfaceClass;
            }).collect(Collectors.toSet());
    }
}

生成代理类的工厂方法:

/**
 * @Auther: xut
 * @Date: 2019/7/25 17:09
 * @Description:
 */
public class RPCServiceFactory<T>  implements FactoryBean<T> {

    private Class<T> interfaceType;

    public RPCServiceFactory(Class<T> interfaceType) {
//传入服务类的类型
        this.interfaceType = interfaceType;

    }
    @Override
    public T getObject() throws Exception {
    //每个服务都会有自己的netty启动类
        NettyInitialization<T> nettyInitialization = new NettyInitialization(interfaceType.getMethods()[0].getReturnType());
        nettyInitialization.connect();
        DynamicProxyHandler dynamicProxyHandler = new DynamicProxyHandler(interfaceType,nettyInitialization);
        return (T) Proxy.newProxyInstance(interfaceType.getClassLoader(),new Class[] {interfaceType},dynamicProxyHandler);
    }

    @Override
    public Class<?> getObjectType() {
        return interfaceType;
    }

    @Override
    public boolean isSingleton() {
        return false;
    }
}

代理类handler

/**
 * @Auther: xut
 * @Date: 2019/7/24 17:50
 * @Description:
 */
public class DynamicProxyHandler<T> implements InvocationHandler {

    private Class<T> interfaceClass;

    private NettyInitialization nettyInitialization;

    public DynamicProxyHandler(Class<T> clazz, NettyInitialization nettyInitialization) {
        this.nettyInitialization = nettyInitialization;
        interfaceClass = clazz;
    }

    @Override
    public T invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //RequestBean 记录了参数序列化信息
        RequestBeanInfo requestBeanInfo = new RequestBeanInfo();
        requestBeanInfo.setClassName(interfaceClass.getSimpleName());
        requestBeanInfo.setArgs(args);
        requestBeanInfo.setArgsTypes(Arrays.stream(method.getParameterTypes()).map(i -> i.getTypeName()).collect(Collectors.toList()).toArray(new String[method.getParameterTypes().length]));
        //记录每次通信的id,用于长连接时多线程返回值的对应的确认
        requestBeanInfo.setRequestId(UUID.randomUUID().toString());
        //messageExchane 是服务的通信方法
        ReponseBeanInfo reponseBeanInfo = nettyInitialization.messageExchane(requestBeanInfo);
        return (T) reponseBeanInfo.getResult();
    }
}

未完待续

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值