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('\t') >= 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
机制。
来源 https://www.cnblogs.com/switchvov/p/15363105.html