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机制的。

原创粉丝点击