Sofrpc service publishing principle
Sofrpc is the RPC framework in ant soffastack. The rise of each middleware is worth learning its design concept to expand our knowledge reserve. It should not be shown here. For those students who have not understood, please refer to: https://www.sofastack.tech/projects/sofa-rpc/overview/
This is based on zookeeper as the registration analysis.
Exposure services
After the interface is configured, it is as follows:
@SofaService(interfaceType = HelloSofaV2.class, bindings = {@SofaServiceBinding(bindingType = "bolt"),@SofaServiceBinding(bindingType = "rest")}) @Service public class HelloSofav2Impl implements HelloSofaV2 { private Logger logger = LoggerFactory.getLogger(HelloSofav2Impl.class); @Override public String sayHello(String sofa) { logger.info("hello sofa..."); return "hellosofa"; } }
The @ SofaService annotation will be identified on the interface implementation. Servicebeanfactoryprocessor is an extension of the spring beanfactoryprocessor extension point. Beans are defined programmatically
ServiceBeanFactoryPostProcessor#postProcessBeanFactory->transformSofaBeanDefinition->generateSofaServiceDefinitionOnClass->generateSofaServiceDefinition
private void generateSofaServiceDefinition(String beanId, SofaService sofaServiceAnnotation, Class<?> beanClass, BeanDefinition beanDefinition, ConfigurableListableBeanFactory beanFactory) { if (sofaServiceAnnotation == null) { return; } AnnotationWrapperBuilder<SofaService> wrapperBuilder = AnnotationWrapperBuilder.wrap( sofaServiceAnnotation).withBinder(binder); //Placeholder resolution through proxy sofaServiceAnnotation = wrapperBuilder.build(); Class<?> interfaceType = sofaServiceAnnotation.interfaceType(); if (interfaceType.equals(void.class)) { Class<?> interfaces[] = beanClass.getInterfaces(); if (beanClass.isInterface() || interfaces == null || interfaces.length == 0) { interfaceType = beanClass; } else if (interfaces.length == 1) { interfaceType = interfaces[0]; } else { throw new FatalBeanException("Bean " + beanId + " has more than one interface."); } } BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(); String serviceId = SofaBeanNameGenerator.generateSofaServiceBeanName(interfaceType, sofaServiceAnnotation.uniqueId()); if (!beanFactory.containsBeanDefinition(serviceId)) { builder.getRawBeanDefinition().setScope(beanDefinition.getScope()); builder.setLazyInit(beanDefinition.isLazyInit()); builder.getRawBeanDefinition().setBeanClass(ServiceFactoryBean.class); builder.addPropertyValue(AbstractContractDefinitionParser.INTERFACE_CLASS_PROPERTY, interfaceType); builder.addPropertyValue(AbstractContractDefinitionParser.UNIQUE_ID_PROPERTY, sofaServiceAnnotation.uniqueId()); builder.addPropertyValue(AbstractContractDefinitionParser.BINDINGS, getSofaServiceBinding(sofaServiceAnnotation, sofaServiceAnnotation.bindings())); builder.addPropertyReference(ServiceDefinitionParser.REF, beanId); builder.addPropertyValue(ServiceDefinitionParser.BEAN_ID, beanId); builder.addPropertyValue(AbstractContractDefinitionParser.DEFINITION_BUILDING_API_TYPE, true); builder.addDependsOn(beanId); ((BeanDefinitionRegistry) beanFactory).registerBeanDefinition(serviceId, builder.getBeanDefinition()); } else { SofaLogger.error("SofaService was already registered: {}", serviceId); } }
Create a ServiceFactoryBean through BeanDefinitionBuilder. By looking at the brief introduction of sofrpc, we know that @ SofaService annotation supports placeholders. Placeholders are parsed through the class PlaceHolderAnnotationInvocationHandler. The above annotation is about the implementation principle. In this regard, we need to turn our attention to ServiceFactoryBean, which inherits AbstractContractFactoryBean and implements InitializingBean and the design pattern of template through inheritance relationship
ServiceFactoryBean#doAfterPropertiesSet
@Override protected void doAfterPropertiesSet() { if (!apiType && hasSofaServiceAnnotation()) { throw new ServiceRuntimeException( "Bean " + beanId + " of type " + ref.getClass() + " has already annotated by @SofaService," + " can not be registered using xml. Please check it."); } Implementation implementation = new DefaultImplementation(); implementation.setTarget(ref); service = buildService(); // default add jvm binding and service jvm binding should set serialize as true if (bindings.size() == 0) { JvmBindingParam jvmBindingParam = new JvmBindingParam().setSerialize(true); bindings.add(new JvmBinding().setJvmBindingParam(jvmBindingParam)); } for (Binding binding : bindings) { service.addBinding(binding); } ComponentInfo componentInfo = new ServiceComponent(implementation, service, bindingAdapterFactory, sofaRuntimeContext); sofaRuntimeContext.getComponentManager().register(componentInfo); }
Focus on * * sofaruntimecontext getComponentManager(). register(componentInfo);** By calling, you will get to ComponentManagerImpl#doRegister
private ComponentInfo doRegister(ComponentInfo ci) { ComponentName name = ci.getName(); if (isRegistered(name)) { SofaLogger.error("Component was already registered: {}", name); if (ci.canBeDuplicate()) { return getComponentInfo(name); } throw new ServiceRuntimeException("Component can not be registered duplicated: " + name); } try { ci.register(); } catch (Throwable t) { SofaLogger.error("Failed to register component: {}", ci.getName(), t); return null; } SofaLogger.info("Registering component: {}", ci.getName()); try { ComponentInfo old = registry.putIfAbsent(ci.getName(), ci); if (old != null) { SofaLogger.error("Component was already registered: {}", name); if (ci.canBeDuplicate()) { return old; } throw new ServiceRuntimeException("Component can not be registered duplicated: " + name); } if (ci.resolve()) { typeRegistry(ci); //Exposure services ci.activate(); } } catch (Throwable t) { ci.exception(new Exception(t)); SofaLogger.error("Failed to create the component {}", ci.getName(), t); } return ci; }
ci.activate() will rely on COM alipay. sofa. runtime. spi. binding. Binding violence service. Binding is an extension point in sofrpc. Package bindingadapter <? > through BindingAdapterFactory, adopt
Object outBinding(Object contract, T binding, Object target, SofaRuntimeContext sofaRuntimeContext);
Exposure services. Take RpcBindingAdapter as an example
@Override public Object outBinding(Object contract, RpcBinding binding, Object target, SofaRuntimeContext sofaRuntimeContext) { ApplicationContext applicationContext = sofaRuntimeContext.getSofaRuntimeManager() .getRootApplicationContext(); ProviderConfigContainer providerConfigContainer = applicationContext .getBean(ProviderConfigContainer.class); ProcessorContainer processorContainer = applicationContext .getBean(ProcessorContainer.class); String uniqueName = providerConfigContainer.createUniqueName((Contract) contract, binding); ProviderConfig providerConfig = providerConfigContainer.getProviderConfig(uniqueName); processorContainer.processorProvider(providerConfig); if (providerConfig == null) { throw new ServiceRuntimeException(LogCodes.getLog( LogCodes.INFO_SERVICE_METADATA_IS_NULL, uniqueName)); } try { //Exposure services providerConfig.export(); } catch (Exception e) { throw new ServiceRuntimeException(LogCodes.getLog(LogCodes.ERROR_PROXY_PUBLISH_FAIL), e); } if (providerConfigContainer.isAllowPublish()) { providerConfig.setRegister(true); List<RegistryConfig> registrys = providerConfig.getRegistry(); for (RegistryConfig registryConfig : registrys) { Registry registry = RegistryFactory.getRegistry(registryConfig); registry.init(); registry.start(); registry.register(providerConfig); } } return Boolean.TRUE; }
providerConfig#export
@Override public void export() { if (providerConfig.getDelay() > 0) { // Delayed loading in milliseconds Thread thread = factory.newThread(new Runnable() { @Override public void run() { try { Thread.sleep(providerConfig.getDelay()); } catch (Throwable ignore) { // NOPMD } doExport(); } }); thread.start(); } else { doExport(); } }
private void doExport() { if (exported) { return; } // Check parameters checkParameters(); String appName = providerConfig.getAppName(); //key is the protocol of server,for concurrent safe Map<String, Boolean> hasExportedInCurrent = new ConcurrentHashMap<String, Boolean>(); // Register the processor with the server List<ServerConfig> serverConfigs = providerConfig.getServer(); for (ServerConfig serverConfig : serverConfigs) { String protocol = serverConfig.getProtocol(); String key = providerConfig.buildKey() + ":" + protocol; if (LOGGER.isInfoEnabled(appName)) { LOGGER.infoWithApp(appName, "Export provider config : {} with bean id {}", key, providerConfig.getId()); } // Note the same interface, the same uniqueId and different server s AtomicInteger cnt = EXPORTED_KEYS.get(key); // Counter if (cnt == null) { // Not published cnt = CommonUtils.putToConcurrentMap(EXPORTED_KEYS, key, new AtomicInteger(0)); } int c = cnt.incrementAndGet(); hasExportedInCurrent.put(serverConfig.getProtocol(), true); int maxProxyCount = providerConfig.getRepeatedExportLimit(); if (maxProxyCount > 0) { if (c > maxProxyCount) { decrementCounter(hasExportedInCurrent); // If the maximum number is exceeded, an exception is thrown directly throw new SofaRpcRuntimeException(LogCodes.getLog(LogCodes.ERROR_DUPLICATE_PROVIDER_CONFIG, key, maxProxyCount)); } else if (c > 1) { if (LOGGER.isInfoEnabled(appName)) { LOGGER.infoWithApp(appName, LogCodes.getLog(LogCodes.WARN_DUPLICATE_PROVIDER_CONFIG, key, c)); } } } } try { // Construct request caller providerProxyInvoker = new ProviderProxyInvoker(providerConfig); preProcessProviderTarget(providerConfig, (ProviderProxyInvoker) providerProxyInvoker); // Initialize registry if (providerConfig.isRegister()) { List<RegistryConfig> registryConfigs = providerConfig.getRegistry(); if (CommonUtils.isNotEmpty(registryConfigs)) { for (RegistryConfig registryConfig : registryConfigs) { RegistryFactory.getRegistry(registryConfig); // Initialize Registry in advance } } } // Register the processor with the server for (ServerConfig serverConfig : serverConfigs) { try { Server server = serverConfig.buildIfAbsent(); // Register request caller server.registerProcessor(providerConfig, providerProxyInvoker); if (serverConfig.isAutoStart()) { //Start service server.start(); } } catch (SofaRpcRuntimeException e) { throw e; } catch (Exception e) { LOGGER.errorWithApp(appName, LogCodes.getLog(LogCodes.ERROR_REGISTER_PROCESSOR_TO_SERVER, serverConfig.getId()), e); } } // Register with the registry providerConfig.setConfigListener(new ProviderAttributeListener()); register(); } catch (Exception e) { decrementCounter(hasExportedInCurrent); if (e instanceof SofaRpcRuntimeException) { throw e; } throw new SofaRpcRuntimeException(LogCodes.getLog(LogCodes.ERROR_BUILD_PROVIDER_PROXY), e); } // Record some cached data RpcRuntimeContext.cacheProviderConfig(this); exported = true; }
Finally, start the service through BoltServer. The service will be released here.
Registration service
Through sofaboot. spring. You can see a sofarpcoautoconfiguration in the factories. A softbootrpcstartlistener is configured inside, and this listener listens to the softbootrpcstartevent. Softbootrpcstartevent is published by ApplicationContextRefreshedListener. ApplicationContextRefreshedListener listens to the ContextRefreshedEvent. That is to say, a softbootrpcstartevent will be published after spring boot is completed, and this event will be listened to by softbootrpcstartelistener
@Override public void onApplicationEvent(SofaBootRpcStartEvent event) { //choose disable metrics lookout disableLookout(); //extra info processExtra(event); //start fault tolerance faultToleranceConfigurator.startFaultTolerance(); Collection<ProviderConfig> allProviderConfig = providerConfigContainer .getAllProviderConfig(); if (!CollectionUtils.isEmpty(allProviderConfig)) { //start server serverConfigContainer.startServers(); } //set allow all publish providerConfigContainer.setAllowPublish(true); //register registry providerConfigContainer.publishAllProviderConfig(); //export dubbo providerConfigContainer.exportAllDubboProvideConfig(); }
You can see the providerconfigcontainer publishAllProviderConfig(); Register the service to the Registry through Registry
public void register(ProviderConfig config) { String appName = config.getAppName(); if (!registryConfig.isRegister()) { if (LOGGER.isInfoEnabled(appName)) { LOGGER.infoWithApp(appName, LogCodes.getLog(LogCodes.INFO_REGISTRY_IGNORE)); } return; } //release if (config.isRegister()) { registerProviderUrls(config); } if (config.isSubscribe()) { // Subscription configuration node if (!INTERFACE_CONFIG_CACHE.containsKey(buildConfigPath(rootPath, config))) { //Subscription interface level configuration subscribeConfig(config, config.getConfigListener()); } } }
Through registerProviderUrls(config); We can see the specific publishing process and complete the service registration.
SPI
Next, we will focus on the of sofarpc spi
spi is a tool class in jdk, which is widely referenced by other frameworks, and sofarpc is no exception. Many designs of sofarpc should be based on plug-in microkernel designed by imitating dubbo, because jdk spi is relatively single, and many frameworks are extended based on jdk spi
ExtensionLoader is the toolbar of spi in sofarpc. ExtensionLoader can be obtained through ExtensionLoaderFactory
ConcurrentMap<Class, ExtensionLoader> LOADER_MAP = new ConcurrentHashMap<Class, ExtensionLoader>(); 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<T>(clazz, listener); LOADER_MAP.put(clazz, loader); } } } return loader; }
A class will bind to an ExtensionLoader, and will cache and build an ExtensionLoader
protected ExtensionLoader(Class<T> interfaceClass, boolean autoLoad, ExtensionLoaderListener<T> listener) { if (RpcRunningState.isShuttingDown()) { this.interfaceClass = null; this.interfaceName = null; this.listeners = null; this.factory = null; this.extensible = null; this.all = null; return; } // The interface is empty and is neither an interface nor an abstract class 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); } } }
public @interface Extension { /** * Extension point name * * @return Extension point name */ String value(); /** * Extension point coding is not required by default. It is required when the interface needs coding * * @return Extension point coding * @see Extensible#coded() */ byte code() default -1; /** * Priority sorting is not required by default * * @return sort */ int order() default 0; /** * Overwrite other extensions with the same name with {@ link #order()} * * @return Overwrite other low sorted extensions with the same name * @since 5.2.0 */ boolean override() default false; /** * Exclude other extensions. You can exclude other low {@ link #order()} extensions * * @return Exclude other extensions * @since 5.2.0 */ String[] rejection() default {}; }
According to the priority, whether the same extension point exists, whether to overwrite, etc.
Extension points in sofarpc exist in META-INF/services / sof RPC / and META-INF/services / paths,
[the external chain image transfer fails. The source station may have an anti-theft chain mechanism. It is recommended to save the image and upload it directly (img-tdkd8cgw-1626429409833) (C: \ users \ subay \ appdata \ roaming \ typora user images \ image-20210716165212395. PNG)]
All extension points in sofarpc
load balancing
- consistentHash
- localPref
- random
- roundRobin
- weightRoundRobin
- weightConsistentHash
- auto
The default load balancing algorithm is random
Cluster fault tolerance
- failfast
- failover
Default fault tolerance policy failover
sofarpc global configuration
The default configuration of sofarpc is RPC config default JSON
If you want to override the default configuration, you need to go to sofa RPC / RPC config JSON or
META-INF/sofa-rpc/rpc-config. The configuration in JSON is overridden through RPC config. Order defines the priority
The basic process and key technical points of service release have been completed. In the next section, we will analyze the service reference process
The official account will also publish some spring stack,sofa stack source analysis articles.
spot
load balancing
- consistentHash
- localPref
- random
- roundRobin
- weightRoundRobin
- weightConsistentHash
- auto
The default load balancing algorithm is random
Cluster fault tolerance
- failfast
- failover
Default fault tolerance policy failover
sofarpc global configuration
The default configuration of sofarpc is RPC config default JSON
If you want to override the default configuration, you need to go to sofa RPC / RPC config JSON or
META-INF/sofa-rpc/rpc-config. The configuration in JSON is overridden through RPC config. Order defines the priority
The basic process and key technical points of service release have been completed. In the next section, we will analyze the service reference process
IT entrepreneurs who advance with official account numbers