प्रौद्योगिकी साझेदारी

खतरावायु-आरपीसी-ढाँचा---4

2024-07-12

한어Русский языкEnglishFrançaisIndonesianSanskrit日本語DeutschPortuguêsΕλληνικάespañolItalianoSuomalainenLatina

SPI इत्यस्य अर्थः सेवाप्रदाता अन्तरफलकं भवति, यत् विशेषतया सेवाप्रदातृभ्यः अथवा विकासकेभ्यः प्रदत्तं अन्तरफलकं इति अवगन्तुं शक्यते ये रूपरेखाकार्यं विस्तारयन्ति । SPI सेवा-अन्तरफलकं विशिष्टसेवा-कार्यन्वयनात् पृथक् करोति, सेवा-आह्वानं सेवा-कार्यन्वयकं च वियुग्मयति, तथा च कार्यक्रमस्य मापनीयतां, परिपालनक्षमतां च सुधारयितुं शक्नोति सेवाकार्यन्वयनस्य परिवर्तनं वा प्रतिस्थापनं वा आह्वानकर्तुः परिवर्तनस्य आवश्यकता नास्ति । अनेकाः ढाञ्चाः जावा इत्यस्य SPI तन्त्रस्य उपयोगं कुर्वन्ति, यथा: Spring framework, database loading driver, log interface, and Dubbo extension implementation इत्यादयः । अस्य RPC-रूपरेखायाः SPI भागं कार्यान्वितुं Dubbo इत्यस्य SPI तन्त्रं पश्यन्तु ।

यथा, यदा क्लायन्ट् सर्वरेण सह संवादं करोति तदा तस्य सन्देशस्य क्रमाङ्कनं करणीयम् । क्रमाङ्कनस्य समये अनेके क्रमाङ्कन-अल्गोरिदम्-इत्येतत् उपयोक्तुं शक्यन्ते, यथा Hessian, Kryo, ProtoStuff च । प्रणाल्याः आवश्यकता अस्ति यत् क्रमाङ्कनं विक्रमणं च कर्तुं सन्देशे क्रमाङ्कन-एल्गोरिदम् इत्यस्य नाम आधारितं वर्गे विधिं आह्वयितुं तदतिरिक्तं विस्तारस्य सुविधायै SPI इत्यस्य उपयोगः आवश्यकः अस्ति वियुग्मनार्थम् ।

एसपीआई इत्यस्य उपयोगः निम्नलिखितरूपेण भवति ।

  1. Serializer serializer = ExtensionLoader.getExtensionLoader(Serializer.class)
  2. .getExtension(codecName);

codecName इति क्रमाङ्कन-अल्गोरिदम् इत्यस्य नाम, तदनुरूपं वर्गं च अस्य नामस्य अनुसारं लोड् करणीयम् ।

  1. private final Class<?> type;
  2. private ExtensionLoader(Class<?> type) {
  3. this.type = type;
  4. }
  5. // 每个SPI接口都有自身的ExtensionLoader
  6. public static <S> ExtensionLoader<S> getExtensionLoader(Class<S> type) {
  7. if (type == null) {
  8. throw new IllegalArgumentException("Extension type should not be null.");
  9. }
  10. if (!type.isInterface()) {
  11. throw new IllegalArgumentException("Extension type must be an interface.");
  12. }
  13. if (type.getAnnotation(SPI.class) == null) {
  14. throw new IllegalArgumentException("Extension type must be annotated by @SPI");
  15. }
  16. // firstly get from cache, if not hit, create one
  17. ExtensionLoader<S> extensionLoader = (ExtensionLoader<S>) EXTENSION_LOADERS.get(type);
  18. if (extensionLoader == null) {
  19. EXTENSION_LOADERS.putIfAbsent(type, new ExtensionLoader<S>(type));
  20. extensionLoader = (ExtensionLoader<S>) EXTENSION_LOADERS.get(type);
  21. }
  22. return extensionLoader;
  23. }

प्रत्येकस्य SPI अन्तरफलकस्य स्वकीयः ExtensionLoader भवति यदा getExtensionLoader इति आह्वानं करोति तदा प्रथमं अन्तरफलकस्य ExtensionLoader प्राप्तुं प्रयतते यदि एतत् न भवितुम् अर्हति प्राप्तं, Loader वस्तु रचयन्तु ।

तदनन्तरं getExtension इत्यस्य माध्यमेन दृष्टान्तः प्राप्यते, तथा च दृष्टान्तः स्थानीयरूपेण अपि संग्रहीतः भवति यदि सः संग्रहे नास्ति तर्हि पुनः दृष्टान्तं रचयन्तु ।

  1. private final Map<String, Holder<Object>> cachedInstances = new ConcurrentHashMap<>();
  2. public T getExtension(String name) {
  3. if (StringUtil.isBlank(name)) {
  4. throw new IllegalArgumentException("Extension name should not be null or empty.");
  5. }
  6. // firstly get from cache, if not hit, create one
  7. // 缓存holder
  8. Holder<Object> holder = cachedInstances.get(name);
  9. if (holder == null) {
  10. cachedInstances.putIfAbsent(name, new Holder<>());
  11. holder = cachedInstances.get(name);
  12. }
  13. // create a singleton if no instance exists
  14. // holder为空,双重检查锁创建示例
  15. Object instance = holder.get();
  16. if (instance == null) {
  17. synchronized (holder) {
  18. instance = holder.get();
  19. if (instance == null) {
  20. instance = createExtension(name);
  21. holder.set(instance);
  22. }
  23. }
  24. }
  25. return (T) instance;
  26. }

क्लास् इत्यस्य Class ऑब्जेक्ट् प्राप्त्वा भवन्तः reflection इत्यनेन एतत् ऑब्जेक्ट् निर्मातुम् अर्हन्ति ।

  1. // 缓存
  2. private static final Map<Class<?>, Object> EXTENSION_INSTANCES = new ConcurrentHashMap<>();
  3. private T createExtension(String name) {
  4. // load all extension classes of type T from file and get specific one by name
  5. // SPI接口对应的实现类,其标识名与class文件的映射,根据标识名获取class
  6. Class<?> clazz = getExtensionClasses().get(name);
  7. if (clazz == null) {
  8. throw new RuntimeException("No such extension of name " + name);
  9. }
  10. T instance = (T) EXTENSION_INSTANCES.get(clazz);
  11. if (instance == null) {
  12. try {
  13. // 缓存中不存在,则创建实例
  14. EXTENSION_INSTANCES.putIfAbsent(clazz, clazz.newInstance());
  15. instance = (T) EXTENSION_INSTANCES.get(clazz);
  16. } catch (Exception e) {
  17. log.error(e.getMessage());
  18. }
  19. }
  20. return instance;
  21. }

कुञ्जी Class ऑब्जेक्ट् प्राप्तुं प्रक्रिया अस्ति, अर्थात् getExtensionCalsses मेथड्:

  1. // 该SPI接口所有实现类的标识与其Class对象的缓存
  2. private final Holder<Map<String, Class<?>>> cachedClasses = new Holder<>();
  3. private static final String SERVICE_DIRECTORY = "META-INF/extensions/";
  4. private Map<String, Class<?>> getExtensionClasses() {
  5. // get the loaded extension class from the cache
  6. // 根据Interface实现类的类名获取对应类的缓存
  7. Map<String, Class<?>> classes = cachedClasses.get();
  8. // double check
  9. if (classes == null) {
  10. synchronized (cachedClasses) {
  11. classes = cachedClasses.get();
  12. if (classes == null) {
  13. classes = new HashMap<>();
  14. // load all extensions from our extensions directory
  15. loadDirectory(classes);
  16. // 将Map集合存储在Holder中进行缓存
  17. cachedClasses.set(classes);
  18. }
  19. }
  20. }
  21. return classes;
  22. }
  23. private void loadDirectory(Map<String, Class<?>> extensionClasses) {
  24. // 固定路径下的文件,SPI接口的类名作为文件名,在此文件中规定需要加载的实现类
  25. String fileName = ExtensionLoader.SERVICE_DIRECTORY + type.getName();
  26. try {
  27. Enumeration<URL> urls;
  28. // 系统类加载器,它能够加载用户类路径(ClassPath)上的类和资源。对于SPI机制尤为重要,因为SPI的实现类通常是由应用程序提供并放置在应用程序的类路径下的
  29. ClassLoader classLoader = ExtensionLoader.class.getClassLoader();
  30. // 获取当前类加载器加载的URL资源,文件名确定一般urls是唯一的
  31. urls = classLoader.getResources(fileName);
  32. if (urls != null) {
  33. while (urls.hasMoreElements()) {
  34. URL resourceUrl = urls.nextElement();
  35. // 使用classLoader加载资源,资源目标在resourceUrl下,加载后的class存储在extensionClasses Map集合当中
  36. loadResource(extensionClasses, classLoader, resourceUrl);
  37. }
  38. }
  39. } catch (IOException e) {
  40. log.error(e.getMessage());
  41. }
  42. }
  43. private void loadResource(Map<String, Class<?>> extensionClasses, ClassLoader classLoader, URL resourceUrl) {
  44. try (BufferedReader reader = new BufferedReader(new InputStreamReader(resourceUrl.openStream(), UTF_8))) {
  45. String line;
  46. // read every line
  47. // #是注释,截取注释之前的部分
  48. while ((line = reader.readLine()) != null) {
  49. // get index of comment
  50. final int ci = line.indexOf('#');
  51. if (ci >= 0) {
  52. // string after # is comment so we ignore it
  53. line = line.substring(0, ci);
  54. }
  55. line = line.trim();
  56. if (line.length() > 0) {
  57. try {
  58. final int ei = line.indexOf('=');
  59. // 标识与类名
  60. String name = line.substring(0, ei).trim();
  61. String clazzName = line.substring(ei + 1).trim();
  62. // our SPI use key-value pair so both of them must not be empty
  63. if (name.length() > 0 && clazzName.length() > 0) {
  64. // 加载类
  65. Class<?> clazz = classLoader.loadClass(clazzName);
  66. // 在map中保存
  67. extensionClasses.put(name, clazz);
  68. }
  69. } catch (ClassNotFoundException e) {
  70. log.error(e.getMessage());
  71. }
  72. }
  73. }
  74. } catch (IOException e) {
  75. log.error(e.getMessage());
  76. }
  77. }
  1. kyro=github.javaguide.serialize.kyro.KryoSerializer
  2. protostuff=github.javaguide.serialize.protostuff.ProtostuffSerializer
  3. hessian=github.javaguide.serialize.hessian.HessianSerializer

स्तर-दर-स्तर-विधि-आह्वानं Class-वस्तुं लोड् कर्तुं तथा च उदाहरणं प्राप्तुं META-INF/extensions/ मार्गस्य अन्तर्गतं तत्सम्बद्धां SPI विन्याससञ्चिकां लोड् करणस्य प्रक्रियां साक्षात्करोति

ज्ञातव्यं यत् META-INF/extensions/ इत्यस्य अन्तर्गतं सञ्चिकानाम कोड् मध्ये निर्दिष्टं सञ्चिकानाम SPI अन्तरफलकवर्गस्य पूर्णं वर्गनाम अस्ति । सञ्चिकायां सामग्री अपि (कार्यन्वयनवर्गपरिचयः = कार्यान्वयनवर्गस्य पूर्णवर्गनाम) इत्यस्य अनुसारं लिखितुं आवश्यकं भवति, येन सः कोडेन सह सङ्गतः भवितुम् अर्हति, तथा च कार्यक्रमः सञ्चिकां सम्यक् विश्लेषणं कर्तुं शक्नोति तथा च वर्गलोडरस्य उपयोगं कर्तुं शक्नोति तत्सम्बद्धं Class लोडं कुर्वन्तु। अन्ते &lt;implementation class identifier, implementation class Class object&gt; इत्यस्य अनुसारं cache कुर्वन्तु ।

त्रयाणां सञ्चयानां (class identifier, Class object), (Class object, object instance), तथा (class identifier, object instance) इत्यस्य अस्तित्वस्य कारणात्, भवान् प्रत्यक्षतया identifier मध्ये pass कृत्वा तत्सम्बद्धस्य class इत्यस्य instance प्राप्तुं शक्नोति, यत् आरपीसी-रूपरेखायाः अनुकूलनं अपि करोति ।