Dubbo入门学习--Dubbo服务提供接口SPI机制
来源:互联网 发布:matlab编程与工程应用 编辑:程序博客网 时间:2024/06/04 18:26
Dubbo采用微内核+插件体系,使得设计优雅,扩展性强。那所谓的微内核+插件体系是如何实现的呢!大家是否熟悉spi(service providerinterface)机制,即我们定义了服务接口标准,让厂商去实现(如果不了解spi的请谷歌百度下), jdk通过ServiceLoader类实现spi机制的服务查找功能。可以参考博客 Java spi机制浅谈 接下来我们来了解一下Dubbo是如何实现SPI机制,首先SPI机制出现的需求场景应该是对同一个接口会有不同的实现类,我们可以根据应用场景通过配置来选择使用不同的实现类。
1、@SPI注解
Dubbo提供了一个@SPI注解public @interface SPI { String value() default ""; //指定默认的扩展点}注解@SPI的值为接口实现类的标识
用法为注解到接口类上,表示默认使用接口的一个实现类
通过注解@SPI("dubbo"),默认使用RegistryFactory的实现类DubboRegistryFactory
@SPI("dubbo")public interface Protocol { /** * 获取缺省端口,当用户没有配置端口时使用。 * * @return 缺省端口 */ int getDefaultPort(); /** * 暴露远程服务:<br> * 1. 协议在接收请求时,应记录请求来源方地址信息:RpcContext.getContext().setRemoteAddress();<br> * 2. export()必须是幂等的,也就是暴露同一个URL的Invoker两次,和暴露一次没有区别。<br> * 3. export()传入的Invoker由框架实现并传入,协议不需要关心。<br> * * @param <T> 服务的类型 * @param invoker 服务的执行体 * @return exporter 暴露服务的引用,用于取消暴露 * @throws RpcException 当暴露服务出错时抛出,比如端口已占用 */ @Adaptive <T> Exporter<T> export(Invoker<T> invoker) throws RpcException; /** * 引用远程服务:<br> * 1. 当用户调用refer()所返回的Invoker对象的invoke()方法时,协议需相应执行同URL远端export()传入的Invoker对象的invoke()方法。<br> * 2. refer()返回的Invoker由协议实现,协议通常需要在此Invoker中发送远程请求。<br> * 3. 当url中有设置check=false时,连接失败不能抛出异常,并内部自动恢复。<br> * * @param <T> 服务的类型 * @param type 服务的类型 * @param url 远程服务的URL地址 * @return invoker 服务的本地代理 * @throws RpcException 当连接服务提供方失败时抛出 */ @Adaptive <T> Invoker<T> refer(Class<T> type, URL url) throws RpcException; /** * 释放协议:<br> * 1. 取消该协议所有已经暴露和引用的服务。<br> * 2. 释放协议所占用的所有资源,比如连接和端口。<br> * 3. 协议在释放后,依然能暴露和引用新的服务。<br> */ void destroy();}
2、接口实现类配置
通过注解@SPI("dubbo"),默认使用Protocol的实现类DubboProtocol,其实dubbo和实现类DubboProtocol关系类似Spring配置文件中的id和class的关系,不同的是Dubbo的关系是配置在目录/META_INF/dubbo/internal/com.alibaba.dubbo.rpc.Protocol文件中,文件内容为:
dubbo=com.alibaba.dubbo.rpc.protocol.dubbo.DubboProtocol
http=com.alibaba.dubbo.rpc.protocol.http.HttpProtocol
hessian=com.alibaba.dubbo.rpc.protocol.hessian.HessianProtocol
简单来说其实现机制就类似Spring的bean注入,通过key(dubbo、http、hessian)来找到其实现类。
3、SPI配置文件读取
我们已经知道Dubbo将Protocol的实现类配置到/META_INF/dubbo/internal/com.alibaba.dubbo.rpc.Protocol配置文件中,接下来我们要看的就是将配置文件中的对应关系解析出来了。其处理操作是在ExtensionLoder的loadFile方法中类来实现的,简单来说读取dubbo=com.alibaba.dubbo.registry.dubbo.DubboRegistryFactory,获取到键dubbo,初始化值com.alibaba.dubbo.registry.dubbo.DubboRegistryFactory然后将对应关系保存到一个Map中,这样就可以根据key找到实现类了。
private void loadFile(Map<String, Class<?>> extensionClasses, String dir) {//获取配置文件路径/META_INF/dubbo/internal/com.alibaba.dubbo.registry.RegistryFactory String fileName = dir + type.getName(); try { Enumeration<java.net.URL> urls; ClassLoader classLoader = findClassLoader(); if (classLoader != null) { urls = classLoader.getResources(fileName); } else { urls = ClassLoader.getSystemResources(fileName); } if (urls != null) { while (urls.hasMoreElements()) { java.net.URL url = urls.nextElement(); try { BufferedReader reader = new BufferedReader(new InputStreamReader(url.openStream(), "utf-8")); try { String line = null; while ((line = reader.readLine()) != null) { final int ci = line.indexOf('#'); if (ci >= 0) line = line.substring(0, ci); line = line.trim(); if (line.length() > 0) { try { String name = null; int i = line.indexOf('='); if (i > 0) {//获取key name = line.substring(0, i).trim();//获取value为类的完整路径 line = line.substring(i + 1).trim(); } if (line.length() > 0) {//初始化类 Class<?> clazz = Class.forName(line, true, classLoader); if (! type.isAssignableFrom(clazz)) { throw new IllegalStateException("Error when load extension class(interface: " + type + ", class line: " + clazz.getName() + "), class " + clazz.getName() + "is not subtype of interface."); } if (clazz.isAnnotationPresent(Adaptive.class)) { if(cachedAdaptiveClass == null) { cachedAdaptiveClass = clazz; } else if (! cachedAdaptiveClass.equals(clazz)) { throw new IllegalStateException("More than 1 adaptive class found: " + cachedAdaptiveClass.getClass().getName() + ", " + clazz.getClass().getName()); } } else { try { clazz.getConstructor(type); Set<Class<?>> wrappers = cachedWrapperClasses; if (wrappers == null) { cachedWrapperClasses = new ConcurrentHashSet<Class<?>>(); wrappers = cachedWrapperClasses; } wrappers.add(clazz); } catch (NoSuchMethodException e) { clazz.getConstructor(); if (name == null || name.length() == 0) { name = findAnnotationName(clazz); if (name == null || name.length() == 0) { if (clazz.getSimpleName().length() > type.getSimpleName().length() && clazz.getSimpleName().endsWith(type.getSimpleName())) { name = clazz.getSimpleName().substring(0, clazz.getSimpleName().length() - type.getSimpleName().length()).toLowerCase(); } else { throw new IllegalStateException("No such extension name for the class " + clazz.getName() + " in the config " + url); } } } String[] names = NAME_SEPARATOR.split(name); if (names != null && names.length > 0) { Activate activate = clazz.getAnnotation(Activate.class); if (activate != null) { cachedActivates.put(names[0], activate); } for (String n : names) { if (! cachedNames.containsKey(clazz)) { cachedNames.put(clazz, n); } Class<?> c = extensionClasses.get(n); if (c == null) {//保存name和实现类的关系 extensionClasses.put(n, clazz); } else if (c != clazz) { throw new IllegalStateException("Duplicate extension " + type.getName() + " name " + n + " on " + c.getName() + " and " + clazz.getName()); } } } } } } } catch (Throwable t) { IllegalStateException e = new IllegalStateException("Failed to load extension class(interface: " + type + ", class line: " + line + ") in " + url + ", cause: " + t.getMessage(), t); exceptions.put(line, e); } } } // end of while read lines } finally { reader.close(); } } catch (Throwable t) { logger.error("Exception when load extension class(interface: " + type + ", class file: " + url + ") in " + url, t); } } // end of while urls } } catch (Throwable t) { logger.error("Exception when load extension class(interface: " + type + ", description file: " + fileName + ").", t); } }
这样SPI机制的数据都准备好了,接下来我们会用一篇博客来分析Dubbo是如何使用SPI机制的。
- Dubbo入门学习--Dubbo服务提供接口SPI机制
- SPI 服务提供接口机制
- Java和dubbo中的SPI机制学习
- dubbo SPI机制
- Dubbo SPI机制简介
- Dubbo入门学习--SPI实现@SPI和@Adaptive
- Dubbo 源码学习笔记 —— SPI的机制体现
- ubuntu安装dubbo服务提供
- Dubbo-SPI
- Dubbo中SPI扩展机制解析
- dubbo学习(三)-dubbo的服务发布
- dubbo 服务提供者 心跳机制
- Dubbo学习入门(一)
- dubbo入门学习笔记
- Dubbo入门学习记录
- Dubbo入门学习笔记
- Dubbo学习(一)入门
- Dubbo入门学习
- PCB设计注意事项
- nodejs+layui+laytpl实现分页的例子
- activity活动的四种启动模式
- swift Data 扩展
- Java-abstract(抽象)、final、static
- Dubbo入门学习--Dubbo服务提供接口SPI机制
- iOS之《Effective Objective-C 2.0》读书笔记(14)
- spark的transformation和action算子(基本操作)
- POI
- FFmpeg基本介绍
- intellij struts2.x 部署tomcat 手机访问项目(已修复)
- My understand of Euclidean distance in digital image processing
- Material Design风格之Snackbar
- apache