SOFA RPC SPI机制原理

SOFA RPC(Scalable Open Financial Architecture Remote Procedure Call)是一个高可扩展性、高性能、生产级的 Java RPC 框架。其 SPI(Service Provider Interface)机制为框架提供了强大的扩展能力,允许开发者在不修改框架核心代码的情况下,对框架的各个功能组件进行定制和扩展。以下将详细介绍 SOFA RPC 的 SPI 机制原理。

1. Java SPI 机制回顾

在了解 SOFA RPC 的 SPI 机制之前,有必要先回顾一下 Java 原生的 SPI 机制。Java SPI 是一种服务发现机制,它允许第三方为某个接口提供实现。其核心思想是将接口的定义和实现分离,通过在 META-INF/services 目录下创建以接口全限定名命名的文件,并在文件中列出实现类的全限定名,JVM 可以在运行时动态加载这些实现类。

例如,假设有一个接口 com.example.MyService,其实现类为 com.example.impl.MyServiceImpl,则需要在 META-INF/services/com.example.MyService 文件中添加以下内容:

com.example.impl.MyServiceImpl

Java 代码中可以通过 ServiceLoader 来加载这些实现类:

import java.util.ServiceLoader;

public class Main {
    public static void main(String[] args) {
        ServiceLoader<com.example.MyService> serviceLoader = ServiceLoader.load(com.example.MyService.class);
        for (com.example.MyService service : serviceLoader) {
            service.doSomething();
        }
    }
}

Java SPI 机制虽然提供了一定的扩展性,但也存在一些缺点,如无法指定加载特定的实现类、加载效率较低等。

2. SOFA RPC 对 SPI 的改进

SOFARPC 的 SPI 机制可以借助上下文类加载器(Context ClassLoader)来实现。上下文类加载器是 Java 里的一个特性,它允许线程在运行时指定一个类加载器,以此来打破双亲委派机制的限制,达成动态加载类的目的。
在 Java 里,能够通过 Thread.currentThread().getContextClassLoader() 方法获取当前线程的上下文类加载器。SOFARPC 里有专门的工具类 ClassLoaderUtils 来获取当前的类加载器:

public class ClassLoaderUtils {
    public static ClassLoader getCurrentClassLoader() {
        ClassLoader cl = Thread.currentThread().getContextClassLoader();
        if (cl == null) {
            cl = ClassLoaderUtils.class.getClassLoader();
        }
        return cl == null ? ClassLoader.getSystemClassLoader() : cl;
    }
}

上述代码中,getCurrentClassLoader 方法先尝试获取当前线程的上下文类加载器,若为空,则使用 ClassLoaderUtils 类的类加载器,若还为空,则使用系统类加载器。
SOFA RPC 在 Java SPI 的基础上进行了改进和扩展,主要体现在以下几个方面:

2.1 拓展文件管理和加载优化

SOFA RPC 引入了自定义的拓展文件管理机制,不再局限于 META-INF/services 目录。它使用 META-INF/sofa-rpc 目录来存放拓展文件,通过自定义的加载器来提高加载效率。同时,SOFA RPC 支持在拓展文件中为每个实现类指定一个唯一的名称,方便在代码中通过名称来获取特定的实现类。

例如,在 META-INF/sofa-rpc/com.alipay.sofa.rpc.protocol.Protocol 文件中,可以定义多个 Protocol 接口的实现类:

bolt=com.alipay.sofa.rpc.protocol.bolt.BoltProtocol
rest=com.alipay.sofa.rpc.protocol.rest.RestProtocol
2.2 扩展点的编码和排序机制

SOFA RPC 为每个扩展点提供了编码和排序机制。通过 @Extensible 注解可以为扩展点指定一个唯一的编码,用于在配置文件中引用。同时,可以通过 @Extension 注解为实现类指定一个排序值,SOFA RPC 会根据排序值对实现类进行排序,确保在加载时按照指定的顺序进行。

以下是 Protocol 接口的定义,使用了 @Extensible 注解:

@Extensible(coded = true)
@Unstable
public interface Protocol {
    // 接口方法定义
    ProtocolInfo protocolInfo();
    ProtocolEncoder encoder();
    ProtocolDecoder decoder();
    ProtocolNegotiator negotiator();
}

以下是 BoltProtocol 实现类的定义,使用了 @Extension 注解:

@Extension("bolt")
public class BoltProtocol implements Protocol {
    // 实现接口方法
    @Override
    public ProtocolInfo protocolInfo() {
        // 实现逻辑
        return null;
    }

    @Override
    public ProtocolEncoder encoder() {
        // 实现逻辑
        return null;
    }

    @Override
    public ProtocolDecoder decoder() {
        // 实现逻辑
        return null;
    }

    @Override
    public ProtocolNegotiator negotiator() {
        // 实现逻辑
        return null;
    }
}
2.3 覆盖和排斥策略

SOFA RPC 支持覆盖和排斥策略,允许开发者在不同的模块中对同一个扩展点的实现进行覆盖或排斥。例如,在某些情况下,开发者可能希望使用自定义的实现类来替换框架默认的实现类,或者禁止某些实现类的加载。

3. 核心源码解析

SOFA RPC 的 SPI 机制中使用了两个核心注解:@Extensible@Extension

3.1 @Extensible 注解

@Extensible 注解用于标记一个接口为可扩展的接口,即该接口可以有多个实现类。该注解有以下几个重要的参数:

  • singleton:指定该扩展点的实现类是否为单例模式,默认为 true
  • file:指定自定义扩展文件名称,默认是接口的全类名。
  • coded:指定该扩展点是否支持编码,即是否可以通过编码来引用实现类,默认为 false

例如,Cluster 接口使用了 @Extensible 注解:


@Extensible(singleton = false)
@ThreadSafe
public abstract class Cluster implements Invoker, ProviderInfoListener, Initializable, Destroyable {
    // 类的定义
}
3.2 @Extension 注解

@Extension 注解用于标记一个类为某个扩展点的实现类。该注解有以下几个重要的参数:

  • value:扩展点的名称。
  • code:扩展点的编码,默认 -1,当接口需要编码时使用。
  • order:优先级排序,默认 0。
  • override:是否覆盖其他低 order 的同名扩展,默认 false
  • rejection:排斥其他扩展,可以排斥掉其他低 order 的扩展。

例如,SofaTracerModule 类使用了 @Extension 注解:


@Extension("sofaTracer")
public class SofaTracerModule implements Module {
    // 类的定义
}
3.3 扩展加载器工厂 ExtensionLoaderFactory

ExtensionLoaderFactory 是一个工厂类,用于获取 ExtensionLoader 实例。它使用一个 ConcurrentMap 来缓存 ExtensionLoader 实例,确保每个扩展接口只有一个 ExtensionLoader 实例。

示例代码如下:

public class ExtensionLoaderFactory {
    private static final ConcurrentMap<Class, ExtensionLoader> LOADER_MAP = new ConcurrentHashMap<>();

    public static <T> ExtensionLoader<T> getExtensionLoader(Class<T> clazz) {
        return getExtensionLoader(clazz, null);
    }

    public static <T> ExtensionLoader<T> getExtensionLoader(Class<T> clazz, ExtensionLoaderListener<T> listener) {
        ExtensionLoader<T> loader = LOADER_MAP.get(clazz);
        if (loader == null) {
            synchronized (ExtensionLoaderFactory.class) {
                loader = LOADER_MAP.get(clazz);
                if (loader == null) {
                    loader = new ExtensionLoader<>(clazz, listener);
                    LOADER_MAP.put(clazz, loader);
                }
            }
        }
        return loader;
    }
}
3. 5扩展加载器 ExtensionLoader

ExtensionLoader 是核心类,负责加载扩展点的实现类。以下是其主要功能的详细解读:

3.5.1构造函数

构造函数负责初始化扩展加载器的各种属性,并根据 autoLoad 参数决定是否自动加载扩展。

public ExtensionLoader(Class<T> interfaceClass, boolean autoLoad, ExtensionLoaderListener<T> listener) {
    if (RpcRunningState.isShuttingDown()) {
        // 省略部分代码
        return;
    }
    if (interfaceClass == null || !(interfaceClass.isInterface() || Modifier.isAbstract(interfaceClass.getModifiers()))) {
        throw new IllegalArgumentException("Extensible class must be interface or abstract class!");
    }
    this.interfaceClass = interfaceClass;
    this.interfaceName = ClassTypeUtils.getTypeStr(interfaceClass);
    this.listeners = new ArrayList<>();
    if (listener != null) {
        listeners.add(listener);
    }
    Extensible extensible = interfaceClass.getAnnotation(Extensible.class);
    if (extensible == null) {
        throw new IllegalArgumentException(
                "Error when load extensible interface " + interfaceName + ", must add annotation @Extensible.");
    } else {
        this.extensible = extensible;
    }
    this.factory = extensible.singleton() ? new ConcurrentHashMap<String, T>() : null;
    this.all = new ConcurrentHashMap<String, ExtensionClass<T>>();
    if (autoLoad) {
        List<String> paths = RpcConfigs.getListValue(RpcOptions.EXTENSION_LOAD_PATH);
        for (String path : paths) {
            loadFromFile(path);
        }
    }
}
3.5.2 从文件加载扩展

loadFromFile 方法负责从指定路径加载扩展文件。

protected synchronized void loadFromFile(String path) {
    if (LOGGER.isDebugEnabled()) {
        LOGGER.debug("Loading extension of extensible {} from path: {}", interfaceName, path);
    }
    String file = StringUtils.isBlank(extensible.file()) ? interfaceName : extensible.file().trim();
    String fullFileName = path + file;
    try {
        ClassLoader classLoader = ClassLoaderUtils.getClassLoader(getClass());
        loadFromClassLoader(classLoader, fullFileName);
    } catch (Throwable t) {
        if (LOGGER.isDebugEnabled()) {
            LOGGER.debug("Failed to load extension of extensible " + interfaceName + " from path:" + fullFileName, t);
        }
    }
}
3.3 从类加载器加载扩展

loadFromClassLoader 方法负责从类加载器中读取扩展文件的内容,并逐行解析。

protected void loadFromClassLoader(ClassLoader classLoader, String fullFileName) throws Throwable {
    Enumeration<URL> urls = classLoader != null ? classLoader.getResources(fullFileName)
            : ClassLoader.getSystemResources(fullFileName);
    if (urls != null) {
        while (urls.hasMoreElements()) {
            URL url = urls.nextElement();
            if (LOGGER.isDebugEnabled()) {
                LOGGER.debug("Loading extension of extensible {} from classloader: {} and file: {}",
                        interfaceName, classLoader, url);
            }
            BufferedReader reader = null;
            try {
                reader = new BufferedReader(new InputStreamReader(url.openStream(), StandardCharsets.UTF_8));
                String line;
                while ((line = reader.readLine()) != null) {
                    readLine(url, line);
                }
            } catch (Throwable t) {
                if (LOGGER.isDebugEnabled()) {
                    LOGGER.debug("Failed to load extension of extensible " + interfaceName
                            + " from classloader: " + classLoader + " and file:" + url, t);
                }
            } finally {
                if (reader != null) {
                    reader.close();
                }
            }
        }
    }
}
3.5.3 解析扩展行

readLine 方法负责解析扩展文件中的每一行,提取别名和类名,并加载扩展。

protected void readLine(URL url, String line) {
    String[] aliasAndClassName = parseAliasAndClassName(line);
    if (aliasAndClassName == null || aliasAndClassName.length != 2) {
        return;
    }
    String alias = aliasAndClassName[0];
    String className = aliasAndClassName[1];
    Class tmp;
    try {
        tmp = ClassUtils.forName(className, false);
    } catch (Throwable e) {
        if (LOGGER.isWarnEnabled()) {
            LOGGER.warn("Extension {} of extensible {} is disabled, cause by: {}",
                    className, interfaceName, ExceptionUtils.toShortString(e, 2));
        }
        if (LOGGER.isDebugEnabled()) {
            LOGGER.debug("Extension " + className + " of extensible " + interfaceName + " is disabled.", e);
        }
        return;
    }
    loadExtension(alias, tmp, StringUtils.toString(url), className);
}
3.5.4加载扩展

loadExtension 方法负责检查扩展类是否符合要求,并将其添加到 all 映射中。

private void loadExtension(String alias, Class loadedClazz, String location, String className) {
    if (!interfaceClass.isAssignableFrom(loadedClazz)) {
        throw new IllegalArgumentException("Error when load extension of extensible " + interfaceName +
                " from file:" + location + ", " + className + " is not subtype of interface.");
    }
    Class<? extends T> implClass = (Class<? extends T>) loadedClazz;
    Extension extension = implClass.getAnnotation(Extension.class);
    if (extension == null) {
        throw new IllegalArgumentException("Error when load extension of extensible " + interfaceName +
                " from file:" + location + ", " + className + " must add annotation @Extension.");
    }
    // 省略部分代码
    ExtensionClass old = all.get(alias);
    ExtensionClass<T> extensionClass = null;
    if (old != null) {
        if (extension.override()) {
            if (extension.order() < old.getOrder()) {
                if (LOGGER.isDebugEnabled()) {
                    LOGGER.debug("Extension of extensible {} with alias {} override from {} to {} failure, ",
                            interfaceName, alias, old.getClazz().getName(), implClass.getName());
                }
            } else {
                all.put(alias, extensionClass);
                if (factory != null) {
                    factory.remove(alias);
                }
            }
        }
    } else {
        all.put(alias, extensionClass);
    }
    for (ExtensionLoaderListener<T> listener : listeners) {
        listener.onLoad(extensionClass);
    }
}
3.6 扩展类封装 ExtensionClass

ExtensionClass 用于封装扩展类的信息,包括类名、别名、编码、是否单例、排序等。它还提供了获取扩展实例的方法。

public class ExtensionClass<T> implements Sortable {
    protected final Class<? extends T> clazz;
    protected final String alias;
    protected byte code;
    protected boolean singleton;
    protected int order;
    protected boolean override;
    protected String[] rejection;
    private volatile transient T instance;

    public ExtensionClass(Class<? extends T> clazz, String alias) {
        this.clazz = clazz;
        this.alias = alias;
    }

    public T getExtInstance() {
        return getExtInstance(null, null);
    }
   // 该方法通过单例模式或非单例模式创建并返回扩展点对象的实例。在单例模式下,使用双重检查锁定确保只有一个实例被创建。
   public T getExtInstance(Class[] argTypes, Object[] args) {
    if (clazz != null) {
        try {
            if (singleton) { // 如果是单例
                if (instance == null) {
                    synchronized (this) {
                        if (instance == null) {
                            instance = ClassUtils.newInstanceWithArgs(clazz, argTypes, args);
                        }
                    }
                }
                return instance; // 保留单例
            } else {
                return ClassUtils.newInstanceWithArgs(clazz, argTypes, args);
            }
        } catch (SofaRpcRuntimeException e) {
            throw e;
        } catch (Exception e) {
            throw new SofaRpcRuntimeException(LogCodes.getLog(LogCodes.ERROR_CREATE_EXT_INSTANCE,
                clazz.getCanonicalName()), e);
        }
    }
    throw new SofaRpcRuntimeException(LogCodes.getLog(LogCodes.ERROR_EXTENSION_CLASS_NULL));
}
    // 省略 getter 和 setter 方法
}

4 缓存和管理机制

SOFARPC 定义了多个缓存类,用于存储不同类型的数据,这些缓存类主要使用 ConcurrentMap 来实现线程安全的存储。以下是几个主要的缓存类及其用途:

  • ReflectCache:用于缓存反射相关的信息,如类加载器、类、方法等。
public final class ReflectCache {
    // 应用对应的ClassLoader
    @VisibleForTesting
    static final ConcurrentMap<String, ClassLoader> APPNAME_CLASSLOADER_MAP = new ConcurrentHashMap<String, ClassLoader>();
    // 服务对应的ClassLoader
    @VisibleForTesting
    static final ConcurrentMap<String, ClassLoader> SERVICE_CLASSLOADER_MAP = new ConcurrentHashMap<String, ClassLoader>();
    // String-->Class 缓存
    @VisibleForTesting
    static final ConcurrentMap<String, WeakHashMap<ClassLoader, Class>> CLASS_CACHE    = new ConcurrentHashMap<String, WeakHashMap<ClassLoader, Class>>();
    // 其他缓存...
}
  • RpcCacheManager:提供清理所有缓存的静态方法。
public class RpcCacheManager {
    /**
     * 清理缓存
     */
    public static void clearAll() {
        ReflectCache.clearAll();
    }
}

4.1. 缓存的注册和获取

ReflectCache 类中,提供了一系列方法用于注册和获取缓存数据。

  • 注册类加载器缓存
// 注册应用级别的ClassLoader
public static void registerAppClassLoader(String appName, ClassLoader classloader) {
    APPNAME_CLASSLOADER_MAP.put(appName, classloader);
}
// 注册服务级别的ClassLoader
public static void registerServiceClassLoader(String serviceUniqueName, ClassLoader classloader) {
    SERVICE_CLASSLOADER_MAP.put(serviceUniqueName, classloader);
}
  • 获取类加载器缓存
// 获取应用级别的ClassLoader
public static ClassLoader getAppClassLoader(String appName) {
    ClassLoader appClassLoader = APPNAME_CLASSLOADER_MAP.get(appName);
    if (appClassLoader == null) {
        return ClassLoaderUtils.getCurrentClassLoader();
    } else {
        return appClassLoader;
    }
}
// 获取服务级别的ClassLoader
public static ClassLoader getServiceClassLoader(String serviceUniqueName) {
    ClassLoader appClassLoader = SERVICE_CLASSLOADER_MAP.get(serviceUniqueName);
    if (appClassLoader == null) {
        return ClassLoaderUtils.getCurrentClassLoader();
    } else {
        return appClassLoader;
    }
}
  • 注册和获取类缓存
// 放入Class缓存
public static void putClassCache(String typeStr, Class clazz) {
    CLASS_CACHE.putIfAbsent(typeStr, new WeakHashMap<ClassLoader, Class>());
    CLASS_CACHE.get(typeStr).put(clazz.getClassLoader(), clazz);
}
// 得到Class缓存
public static Class getClassCache(String typeStr) {
    ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
    if (classLoader == null) {
        return null;
    } else {
        Map<ClassLoader, Class> temp = CLASS_CACHE.get(typeStr);
        return temp == null ? null : temp.get(classLoader);
    }
}

4.2. 缓存的清理

ReflectCache 类提供了 clearAll 方法,用于清理所有的缓存数据。

static void clearAll() {
    CLASS_CACHE.clear();
    TYPE_STR_CACHE.clear();
    APPNAME_CLASSLOADER_MAP.clear();
    SERVICE_CLASSLOADER_MAP.clear();
    NOT_OVERLOAD_METHOD_CACHE.clear();
    // 其他缓存清理...
}

4.3 SPI 机制与缓存管理的结合

在 SOFARPC 中,SPI 机制用于动态加载不同的服务实现,而缓存管理则用于提高这些服务实现的访问效率。例如,在 Cluster 类中,通过 SPI 机制可以动态加载不同的集群实现,而在实际调用过程中,可以使用缓存来存储一些常用的信息,如方法签名、类加载器等,以减少反射调用的开销。

@Extensible(singleton = false)
@ThreadSafe
public abstract class Cluster implements Invoker, ProviderInfoListener, Initializable, Destroyable {
    // 服务端消费者启动器
    protected final ConsumerBootstrap consumerBootstrap;
    // 配置
    protected final ConsumerConfig    consumerConfig;

    // 其他方法...
}

5. 总结

SOFA RPC 的 SPI 机制通过对 Java SPI 的改进和扩展,提供了强大的扩展性和灵活性。它允许开发者在不修改框架核心代码的情况下,对框架的各个功能组件进行定制和扩展,如服务注册与发现、远程调用、负载均衡、过滤器等。通过核心注解和拓展点加载流程,SOFA RPC 能够高效地管理和加载扩展点的实现类,为分布式系统的开发提供了便利。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值