轻量级RPC设计与实现第四版
在本版本中引入了SPI机制,关于Java的SPI机制与Dubbo的SPI机制在以前的文章中介绍过。
传送门:Dubbo的SPI机制与JDK机制的不同及原理分析
因为设计的RPC框架是基于Spring的,时常会遇到依赖注入问题。Spring中也有SPI机制,但是它有有个缺点,就是在利用SPI机制实例化具体的服务类时,如果具体的服务类中调用其他的bean,就会实例化失败。主要因为该具体的服务类并没有放入到Spring容器中。本项目将有效解决这个问题。
在设计的RPC框架中加入了该机制,来实现不同序列化方式的切换。
Spring的SPI机制
我们知道在SprngBoot中好多的配置和实现都有默认的实现,我们只需要修改部分配置,比如数据库配置,我们只要在配置文件中写上对应的url,username,password就可以使用了。其实他这边用的就是SPI的方式实现的。Spring的SPI机制原理与Java的SPI原理是一致的。
SpringBoot会利用SpringFactoriesLoader
加载META-INF/spring.factories文件,从CLASSPATH下的每个Jar包中搜寻所有META-INF/spring.factories配置文件,然后将解析properties文件,找到指定名称的配置后返回。需要注意的是,其实这里不仅仅是会去ClassPath路径下查找,会扫描所有路径下的Jar包,只不过这个文件只会在Classpath下的jar包中。
调用方式:
List<AService> services = SpringFactoriesLoader.loadFactories(AService.class, null);
for (AService service : services) {
service.info();
}
相关源码:
public static <T> List<T> loadFactories(Class<T> factoryClass, @Nullable ClassLoader classLoader) {
Assert.notNull(factoryClass, "'factoryClass' must not be null");
ClassLoader classLoaderToUse = classLoader;
if (classLoaderToUse == null) {
classLoaderToUse = SpringFactoriesLoader.class.getClassLoader();
}
List<String> factoryNames = loadFactoryNames(factoryClass, classLoaderToUse);
if (logger.isTraceEnabled()) {
logger.trace("Loaded [" + factoryClass.getName() + "] names: " + factoryNames);
}
List<T> result = new ArrayList<>(factoryNames.size());
for (String factoryName : factoryNames) {
result.add(instantiateFactory(factoryName, factoryClass, classLoaderToUse));
}
AnnotationAwareOrderComparator.sort(result);
return result;
}
public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";
// spring.factories文件的格式为:key=value1,value2,value3
// 从所有的jar包中找到META-INF/spring.factories文件
// 然后从文件中解析出key=factoryClass类名称的所有value值
public static List<String> loadFactoryNames(Class<?> factoryClass, ClassLoader classLoader) {
String factoryClassName = factoryClass.getName();
// 取得资源文件的URL
Enumeration<URL> urls = (classLoader != null ? classLoader.getResources(FACTORIES_RESOURCE_LOCATION) : ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
List<String> result = new ArrayList<String>();
// 遍历所有的URL
while (urls.hasMoreElements()) {
URL url = urls.nextElement();
// 根据资源文件URL解析properties文件,得到对应的一组@Configuration类
Properties properties = PropertiesLoaderUtils.loadProperties(new UrlResource(url));
String factoryClassNames = properties.getProperty(factoryClassName);
// 组装数据,并返回
result.addAll(Arrays.asList(StringUtils.commaDelimitedListToStringArray(factoryClassNames)));
}
return result;
}
改进的SPI机制
该机制有两个缓存变量:
private static final ConcurrentHashMap<Class<?>, Map<String, Class<?>>> cacheClasses= new ConcurrentHashMap<>();
private static final ConcurrentHashMap<Class<?>, Map<String, Object>> cacheIntances = new ConcurrentHashMap<>();
两个Map的key都是扩展服务的接口类的Class对象
cacheClasses
的value也是一个Map,这个map的key是定义的扩展名,即META-INF/roadspi/
目录下文件中的key,value是具体的扩展类的Class对象。
cacheIntances
变量的value也是一个Map,该map的key是定义的扩展名,value是扩展类的具体实例化对象。
该机制的主要逻辑是先获取要实现扩展的接口类Class对象,然后从cacheIntances
变量中根据key查找是否有缓存的实例,如果有直接返回。如果没有,然后根据接口类Class对象和key在cacheClasses
变量中进行查找具体扩展类的Class对象,如果存在,就直接获取对用的Class对象,然后利用BeanDefinitionBuilder
生成bean,并注册到Spring容器中;如果找不到对应的Class对象,则到META-INF/roadspi/扩展接口类全称
文件下进行资源加载。
支持自定义的RoadSpi
注解,来定义默认的具体服务类实现。
最主要部分实现
private void createService(Map<String, Object> extensionInstanceMap, Map<String, Class<?>> serviceClass, String serviceName, Class<?> type) {
Class<?> obj = serviceClass.get(serviceName);
if (obj == null) {
log.error("serviceClass is null!");
}
String beanName = obj.getSimpleName().concat(serviceName);
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(obj);
GenericBeanDefinition definition = (GenericBeanDefinition)builder.getRawBeanDefinition();
definition.setAutowireMode(GenericBeanDefinition.AUTOWIRE_BY_NAME);
ConfigurableApplicationContext configurableApplicationContext = (ConfigurableApplicationContext)context;
DefaultListableBeanFactory register = (DefaultListableBeanFactory)configurableApplicationContext.getBeanFactory();
register.registerBeanDefinition(beanName, definition);
extensionInstanceMap.put(serviceName, context.getBean(beanName));
cacheIntances.put(type, extensionInstanceMap);
}
具体详细代码地址:RoadSPI
轻量级RPC设计与实现第四版的更多相关文章
- 轻量级RPC设计与实现第三版
在前两个版本中,每次发起请求一次就新建一个netty的channel连接,如果在高并发情况下就会造成资源的浪费,这时实现异步请求就十分重要,当有多个请求线程时,需要设计一个线程池来进行管理.除此之外, ...
- 轻量级RPC设计与实现第五版(最终版)
在最近一段时间里,通过搜集有关资料加上自己的理解,设计了一款轻量级RPC,起了一个名字lightWeightRPC.它拥有一个RPC常见的基本功能.主要功能和特点如下: 利用Spring实现依赖注入与 ...
- 轻量级RPC设计与实现第二版
在上一个版本中利用netty实现了简单的一对一的RPC,需要手动设置服务地址,限制性较大. 在本文中,利用zookeeper作为服务注册中心,在服务端启动时将本地的服务信息注册到zookeeper中, ...
- 轻量级RPC设计与实现第一版
什么是RPC RPC (Remote Procedure Call Protocol), 远程过程调用,通俗的解释就是:客户端在不知道调用细节的情况下,调用存在于远程计算机上的某个对象,就像调用本地应 ...
- 【文章内容来自《Android 应用程序开发权威指南》(第四版)】如何设计兼容的用户界面的一些建议(有删改)
最近一直在看的一本书是<Android 应用程序开发权威指南>(第四版),十分推荐.书中讲到了一些用户界面设计的规范,对于初学者我认为十分有必要,在这里码给大家,希望对我们都有用. 在我们 ...
- 微博轻量级RPC框架Motan
Motan 是微博技术团队研发的基于 Java 的轻量级 RPC 框架,已在微博内部大规模应用多年,每天稳定支撑微博上亿次的内部调用.Motan 基于微博的高并发和高负载场景优化,成为一套简单.易用. ...
- 微博轻量级RPC框架Motan正式开源:支撑千亿调用
支撑微博千亿调用的轻量级 RPC 框架 Motan 正式开源了,项目地址为https://github.com/weibocom/motan. 微博轻量级RPC框架Motan正式开源 Motan 是微 ...
- C# 的轻量级 RPC 框架
Redola.Rpc 的一个小目标 Redola.Rpc 的一个小目标 Redola.Rpc 的一个小目标:20000 tps. Concurrency level: 8 threads Comple ...
- 《Thinking In Java第四版》拾遗
<Thinking In Java第四版>拾遗 转自我的github(http://katsurakkkk.github.io/2016/05/Thinking-In-Java%E7%AC ...
随机推荐
- 一个支持 CodeFirst/DbFirst/ModelFirst 的数据库小工具
一个支持 CodeFirst/DbFirst/ModelFirst 的数据库小工具 Intro DbTool 是一个支持 CodeFirst/DbFirst/ModelFirst 的数据库小工具,原本 ...
- ARTS Week 11
Jan 6, 2020 ~ Jan 12, 2020 Algorithm Problem 108 Convert Sorted Array to Binary Search Tree (将有序数组转化 ...
- Hexo部署到Gitee/Coding常见的错误
全网最全小白搭建Hexo+Gitee/Coding 全网最全小白搭建Hexo+Gitee/Coding 本站内容已全部转移到https://www.myyuns.ltd,具体请移步到www.myyun ...
- Python趣味入门02: 妥妥地安装配置Python(Windows版)
< 上一篇:Python趣味入门01:你真的了解Python么? 本篇内容手把手教您如何去网上下载安装Python的运行环境,本文写于2020年Python稳定的版本是3.8,Windows流行 ...
- k8s系列---dns部署
1:首先创建kube-dns和dnsmasq这两个yaml,然后生成相应的pod.svc等. 2:然后在去创建其他的验证pod和svc 3:验证nslookup解析的是其他pod的svc的name,而 ...
- 设置允许外网访问MySQL
1:设置mysql的配置文件 /etc/mysql/my.cnf 找到 bind-address =127.0.0.1 将其注释掉://作用是使得不再只允许本地访问: 重启my ...
- SpringCloud微服务:阿里开源组件Nacos,服务和配置管理
源码地址:GitHub·点这里||GitEE·点这里 一.阿里微服务简介 1.基础描述 Alibaba-Cloud致力于提供微服务开发的一站式解决方案.此项目包含开发分布式应用微服务的必需组件,方便开 ...
- python安装模块速度慢的解决方法
1.Win+R,cmd 2.pip install pqi 3.pqi use aliyun
- Win环境下安装vue及运行vue开发的前端项目
vue安装及配置 首先下载node.js要求版本在8.9以上 官网:https://nodejs.org/zh-cn/ 下载完可检查在windows任务命令行里输入node -v 使用淘 ...
- Zookeeper机制
顾名思义 zookeeper 就是动物园管理员,他是用来管 hadoop(大象).Hive(蜜蜂).pig(小 猪)的管理员, Apache Hbase 和 Apache Solr 的分布式集群都用到 ...