zoukankan      html  css  js  c++  java
  • Skywalking-12:Skywalking SPI机制

    SPI机制

    基本概述

    SPI 全称 Service Provider Interface ,是一种服务发现机制。通过提供接口、预定义的加载器( Loader )以及约定俗称的配置(一般在 META-INF 目录下),可以实现动态加载服务实现类。

    类图

    通过类图可以分析出, ServiceLoader 实现了 Iterable 接口,提供了迭代的功能。

    ServiceLoader 将迭代的实现委托给 LazyIterator

    LazyIterator 提供了延时迭代的能力,当有需要的时候,才去加载。

    Skywalking 模块中的使用

    接口定义

    org.apache.skywalking.oap.server.library.module.ModuleDefine

    package org.apache.skywalking.oap.server.library.module;
    
    import java.lang.reflect.Field;
    import java.util.Enumeration;
    import java.util.Properties;
    import java.util.ServiceLoader;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    
    /**
     * A module definition.
     */
    public abstract class ModuleDefine implements ModuleProviderHolder {
    
        private static final Logger LOGGER = LoggerFactory.getLogger(ModuleDefine.class);
    
        private ModuleProvider loadedProvider = null;
    
        private final String name;
    
        public ModuleDefine(String name) {
            this.name = name;
        }
    
        /**
         * @return the module name
         *
         */
        public final String name() {
            return name;
        }
    
        /**
         * @return the {@link Service} provided by this module.
         */
        public abstract Class[] services();
    
        /**
         * Run the prepare stage for the module, including finding all potential providers, and asking them to prepare.
         *
         * @param moduleManager of this module
         * @param configuration of this module
         * @throws ProviderNotFoundException when even don't find a single one providers.
         */
        void prepare(ModuleManager moduleManager, ApplicationConfiguration.ModuleConfiguration configuration,
            ServiceLoader<ModuleProvider> moduleProviderLoader) throws ProviderNotFoundException, ServiceNotProvidedException, ModuleConfigException, ModuleStartException {
            // etc...
        }
        
        // etc...
    
        @Override
        public final ModuleProvider provider() throws DuplicateProviderException, ProviderNotFoundException {
            if (loadedProvider == null) {
                throw new ProviderNotFoundException("There is no module provider in " + this.name() + " module!");
            }
            return loadedProvider;
        }
    }
    

    接口实现

    org.apache.skywalking.oap.server.library.module.BaseModuleA

    package org.apache.skywalking.oap.server.library.module;
    
    public class BaseModuleA extends ModuleDefine {
    
        public BaseModuleA() {
            super("BaseA");
        }
    
        // 需要提供服务的接口
        @Override
        public Class<? extends Service>[] services() {
            return new Class[] {
                ServiceABusiness1.class,
                ServiceABusiness2.class
            };
        }
    
        public interface ServiceABusiness1 extends Service {
            void print();
        }
    
        public interface ServiceABusiness2 extends Service {
        }
    }
    

    META-INF 定义

    META-INF/services/org.apache.skywalking.oap.server.library.module.ModuleDefine

    org.apache.skywalking.oap.server.library.module.BaseModuleA
    

    使用方式

    org.apache.skywalking.oap.server.library.module.ModuleManager#init

        /**
         * Init the given modules
         */
        public void init(ApplicationConfiguration applicationConfiguration) /* etc... */ {
            // SPI机制加载
            ServiceLoaderModuleDefine> moduleServiceLoader = ServiceLoader.load(ModuleDefine.class);
            // 迭代器获取
            for (ModuleDefine module : moduleServiceLoader) {
                // do something
                // etc...
            }
            // etc...
        }
    

    源码解析

    package java.util;
    
    import java.io.BufferedReader;
    import java.io.IOException;
    import java.io.InputStream;
    import java.io.InputStreamReader;
    import java.net.URL;
    import java.security.AccessController;
    import java.security.AccessControlContext;
    import java.security.PrivilegedAction;
    import java.util.ArrayList;
    import java.util.Enumeration;
    import java.util.Iterator;
    import java.util.List;
    import java.util.NoSuchElementException;
    
    public final class ServiceLoader<S> implements Iterable<S> {
        // 目录前缀
        private static final String PREFIX = "META-INF/services/";
    
        // 需要被加载对象的Class对象
        private final Class<S> service;
    
        // 类加载器
        private final ClassLoader loader;
    
        // The access control context taken when the ServiceLoader is created
        private final AccessControlContext acc;
    
        // 加载对象缓存(按实例化顺序排序)
        private LinkedHashMap<String,S> providers = new LinkedHashMap<>();
    
        // 当前使用的懒加载迭代器
        private LazyIterator lookupIterator;
    
        // 重载
        public void reload() {
            // 清除加载对象缓存
            providers.clear();
            // 重置懒加载迭代器
            lookupIterator = new LazyIterator(service, loader);
        }
    
        // 不允许直接创建ServiceLoader对象,只能通过loadXXX获取ServiceLoader对象
        private ServiceLoader(Class<S> svc, ClassLoader cl) {
            service = Objects.requireNonNull(svc, "Service interface cannot be null");
            loader = (cl == null) ? ClassLoader.getSystemClassLoader() : cl;
            acc = (System.getSecurityManager() != null) ? AccessController.getContext() : null;
            reload();
        }
    
        private static void fail(Class<?> service, String msg, Throwable cause) throws ServiceConfigurationError {
            throw new ServiceConfigurationError(service.getName() + ": " + msg, cause);
        }
    
        private static void fail(Class<?> service, String msg) throws ServiceConfigurationError {
            throw new ServiceConfigurationError(service.getName() + ": " + msg);
        }
    
        private static void fail(Class<?> service, URL u, int line, String msg) throws ServiceConfigurationError {
            fail(service, u + ":" + line + ": " + msg);
        }
    
        // 解析配置文件中的一行,如果没有注释,则加入到类名列表中
        private int parseLine(Class<?> service, URL u, BufferedReader r, int lc, List<String> names) throws IOException, ServiceConfigurationError {
            String ln = r.readLine();
            if (ln == null) {
                return -1;
            }
            int ci = ln.indexOf('#');
            if (ci >= 0) ln = ln.substring(0, ci);
            ln = ln.trim();
            int n = ln.length();
            if (n != 0) {
                if ((ln.indexOf(' ') >= 0) || (ln.indexOf('	') >= 0))
                    fail(service, u, lc, "Illegal configuration-file syntax");
                int cp = ln.codePointAt(0);
                if (!Character.isJavaIdentifierStart(cp))
                    fail(service, u, lc, "Illegal provider-class name: " + ln);
                for (int i = Character.charCount(cp); i < n; i += Character.charCount(cp)) {
                    cp = ln.codePointAt(i);
                    if (!Character.isJavaIdentifierPart(cp) && (cp != '.'))
                        fail(service, u, lc, "Illegal provider-class name: " + ln);
                }
                if (!providers.containsKey(ln) && !names.contains(ln))
                    names.add(ln);
            }
            return lc + 1;
        }
    
        // 解析配置文件,返回实现类名列表
        private Iterator<String> parse(Class<?> service, URL u) throws ServiceConfigurationError {
            InputStream in = null;
            BufferedReader r = null;
            ArrayList<String> names = new ArrayList<>();
            try {
                in = u.openStream();
                r = new BufferedReader(new InputStreamReader(in, "utf-8"));
                int lc = 1;
                while ((lc = parseLine(service, u, r, lc, names)) >= 0);
            } catch (IOException x) {
                fail(service, "Error reading configuration file", x);
            } finally {
                try {
                    if (r != null) r.close();
                    if (in != null) in.close();
                } catch (IOException y) {
                    fail(service, "Error closing configuration file", y);
                }
            }
            return names.iterator();
        }
    
        // 懒加载迭代器,提供了延时迭代的能力,当有需要的时候,才去加载
        private class LazyIterator implements Iterator<S> {
            // 需要被加载对象的Class对象
            Class<S> service;
            // 类加载器
            ClassLoader loader;
            // 配置文件列表
            Enumeration<URL> configs = null;
            // 当前迭代的配置文件中类名列表的迭代器
            Iterator<String> pending = null;
            // 下一个实现类名
            String nextName = null;
    
            private LazyIterator(Class<S> service, ClassLoader loader) {
                this.service = service;
                this.loader = loader;
            }
    
            // 是否有下一个Service
            private boolean hasNextService() {
                if (nextName != null) {
                    return true;
                }
                // 加载所有配置文件
                if (configs == null) {
                    try {
                        String fullName = PREFIX + service.getName();
                        if (loader == null)
                            configs = ClassLoader.getSystemResources(fullName);
                        else
                            configs = loader.getResources(fullName);
                    } catch (IOException x) {
                        fail(service, "Error locating configuration files", x);
                    }
                }
                // 当当前类名列表迭代完之后,加载下一个配置文件
                while ((pending == null) || !pending.hasNext()) {
                    if (!configs.hasMoreElements()) {
                        return false;
                    }
                    pending = parse(service, configs.nextElement());
                }
                // 获取下一个类名
                nextName = pending.next();
                return true;
            }
    
            // 获取下一个Service
            private S nextService() {
                if (!hasNextService())
                    throw new NoSuchElementException();
                String cn = nextName;
                nextName = null;
                Class<?> c = null;
                try {
                    // 类名 -> 类的Class对象
                    c = Class.forName(cn, false, loader);
                } catch (ClassNotFoundException x) {
                    fail(service, "Provider " + cn + " not found");
                }
                if (!service.isAssignableFrom(c)) {
                    fail(service, "Provider " + cn  + " not a subtype");
                }
                try {
                    // 实例化
                    S p = service.cast(c.newInstance());
                    // 加入到缓存中
                    providers.put(cn, p);
                    return p;
                } catch (Throwable x) {
                    fail(service, "Provider " + cn + " could not be instantiated", x);
                }
                throw new Error();          // This cannot happen
            }
    
            // 迭代器,是否有下个元素
            public boolean hasNext() {
                if (acc == null) {
                    return hasNextService();
                } else {
                    // 授权资源
                    PrivilegedAction<Boolean> action = new PrivilegedAction<Boolean>() {
                        public Boolean run() { return hasNextService(); }
                    };
                    return AccessController.doPrivileged(action, acc);
                }
            }
    
            // 迭代器,获取下个元素
            public S next() {
                if (acc == null) {
                    return nextService();
                } else {
                    // 授权资源
                    PrivilegedAction<S> action = new PrivilegedAction<S>() {
                        public S run() { return nextService(); }
                    };
                    return AccessController.doPrivileged(action, acc);
                }
            }
    
            // 不支持删除
            public void remove() {
                throw new UnsupportedOperationException();
            }
    
        }
    
        // 迭代器实现,如果有缓存从缓存中获取,没有则从懒加载迭代器加载
        public Iterator<S> iterator() {
            return new Iterator<S>() {
                Iterator<Map.Entry<String,S>> knownProviders = providers.entrySet().iterator();
    
                public boolean hasNext() {
                    if (knownProviders.hasNext())
                        return true;
                    return lookupIterator.hasNext();
                }
    
                public S next() {
                    if (knownProviders.hasNext())
                        return knownProviders.next().getValue();
                    return lookupIterator.next();
                }
    
                public void remove() {
                    throw new UnsupportedOperationException();
                }
            };
        }
    
        // 通过类的Class对象及类加载,获取ServiceLoader
        public static <S> ServiceLoader<S> load(Class<S> service, ClassLoader loader) {
            return new ServiceLoader<>(service, loader);
        }
    
        // 通过类的Class对象,获取ServiceLoader
        public static <S> ServiceLoader<S> load(Class<S> service) {
            ClassLoader cl = Thread.currentThread().getContextClassLoader();
            return ServiceLoader.load(service, cl);
        }
    
        // 通过类的Class对象和扩展类加载器,获取ServiceLoader
        public static <S> ServiceLoader<S> loadInstalled(Class<S> service) {
            ClassLoader cl = ClassLoader.getSystemClassLoader();
            ClassLoader prev = null;
            while (cl != null) {
                prev = cl;
                cl = cl.getParent();
            }
            return ServiceLoader.load(service, prev);
        }
    
        public String toString() {
            return "java.util.ServiceLoader[" + service.getName() + "]";
        }
    
    }
    

    PS: JDK 提供的 SPI 机制,必须要使用迭代器遍历获取需要的实现,而 Dubbo SPI 可以通过 #getExtension 获取指定实现类。

    总结

    通过源码分析,可以了解到 Skywalking 没有定义自己的 SPI 机制,但深入阅读 Skywalking 的使用场景后,发现用 JDK 提供的 SPI 机制也没什么问题。

    个人认为,任何技术都应该根据场景选取,适合的才是最好的,如果没有那么复杂的需要,没必要像 dubbo 一样,定义自己的 SPI 机制。

    参考文档

    分享并记录所学所见

  • 相关阅读:
    Spring实现声明式事务
    Spring整合MyBatis
    Spring AOP
    代理模式
    Bean的作用域
    Spring的配置
    HQL题目记录以及解题思路--持续更新
    数仓学习之路一:数仓理论
    DBeaver连接Hive遇到的坑
    MySQL常见面试题
  • 原文地址:https://www.cnblogs.com/switchvov/p/15363105.html
Copyright © 2011-2022 走看看