手撸分布式框架
缘起
一直在用各种各样的rpc框架,如spring cloud Dubbo 等等。但只是使用未免理解不深,感触不浓。有时候就想自己试着去实现一个简单的rpc框架。虽然是个简陋的丑轮子,但是对于个人的成长还是很有帮助的。这篇博客就是为了记录一下实现过程中的思路和遇到的问题。
思路
首先考虑的是rpc框架最核心的部分:远程过程调用 。像spring cloud一般都是用http协议为基础,json作为序列化协议。Dubbo则通常基于TCP长链接,Hessian二进制序列化协议,内部使用的是mina作为异步通信框架。正好最近学习了netty的源码。于是决定使用netty作为我自己的rpc通信框架。由于作为远程调用,具体的实现方法在服务端,本地通过通信框架将参数和结果交互。自然想到使用代理,代理类封装通信方法“伪装”成服务端的服务类,给客户端调用。这里我使用了jdk自带的动态代理方法实现。至于Bean的生命周期都交给spring来托管想必能省不少的事情。整体架构类似Dubbo,至此雏形的思路已经可以开始实现了。
结构示意图
问题点拆分
- 服务类动态代理->动态代理的类加入springContext->扫描指定服务包(已实现)
- netty的使用,复用(序列化机制与选择),长连接(异步转同步)确定对应线程对应返回值(已实现)
- 实现服务发现与下线,负载均衡(考虑解决方案,使用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();
}
}
未完待续