Registration and discovery of Pigeon service

Keywords: Netty Spring

Last article: What happened to a call to the server of Pigeon
In the previous section, we introduced the process of the request calling of Pigeon, so how is the service of Pigeon perceived by the caller? We know that RPC usually has a registry to store the information of service testing (ip, port, etc.), so that the client can get the information of the service through the registry to complete the request calling. Let's see how the registration and discovery mechanism of the Pigeon service is implemented.

Registration of services

First, let's look at the service registration mechanism.
The @ Reference and @ Service annotations are mainly used in Pigeon to mark the Service caller interface and the Service provider implementation class. The class that intercepts these two annotations is AnnotationBean class. Here, the processing is completed by implementing Spring's BeanPostProcessor interface (Pigeon related @ Service class scanning and its BeanDefinition encapsulation are implemented by implementing beanf The implementation of the postProcessAfterInitialization method is as follows:

public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
		// First, determine whether the Bean to be injected with related attributes is under the specified annotation scanning package
		Class<?> beanClass = AopUtils.getTargetClass(bean);
		if (beanClass == null || !isMatchPackage(beanClass.getName())) {
			return bean;
		}

		// Determine whether @ Service annotation exists in class definition
		Service service = beanClass.getAnnotation(Service.class);
		if (service != null) {
			// If the interface is not customized, the current beanClass is used
			Class serviceInterface = service.interfaceClass();
			if (void.class.equals(service.interfaceClass())) {
				serviceInterface = ServiceConfigUtils.getServiceInterface(beanClass);
			}
			if (serviceInterface == null) {
				serviceInterface = beanClass;
			}

			// Initialize ProviderConfig and ServerConfig to complete the initialization of service provider configuration and server configuration
			ProviderConfig<Object> providerConfig = new ProviderConfig<Object>(serviceInterface, bean);
			providerConfig.setService(bean);
			providerConfig.setUrl(service.url());
			providerConfig.setVersion(service.version());
			providerConfig.setSharedPool(service.useSharedPool());
			providerConfig.setActives(service.actives());

			ServerConfig serverConfig = new ServerConfig();
			serverConfig.setPort(getDefaultPort(service.port()));
			serverConfig.setSuffix(service.group());
			serverConfig.setAutoSelectPort(service.autoSelectPort());
			providerConfig.setServerConfig(serverConfig);

			// Register the service provider, start the server, publish the service, and complete the initialization of the pigeon provider call
			ServiceFactory.addService(providerConfig);
		}

		// Resolve whether the methods and properties in the bean contain Reference, and complete the dependency injection of the bean as the service caller.
		postProcessBeforeInitialization(bean, beanName);
		return bean;
	}

Here, we are concerned with the registration process, namely ServiceFactory.addService(), and look at the implementation of its final trigger

public void doAddService(ProviderConfig providerConfig) {
        try {
            // Check service name
            checkServiceName(providerConfig);
            // Publish the specified version of the service and resolve the service method at the same time
            ServicePublisher.addService(providerConfig);
            // Start the netty RPC server as the service provider for the caller to call the service
            ServerConfig serverConfig = ProviderBootStrap.startup(providerConfig);
            // Update serverConfig
            providerConfig.setServerConfig(serverConfig);
            // When the service is actually published, it will be registered in the registry for the caller to discover and call
            ServicePublisher.publishService(providerConfig, false);
        } catch (Throwable t) {
            throw new RpcException("error while adding service:" + providerConfig, t);
        }
    }

In addition to the resolution service, the following processes are mainly carried out:
Determine whether the version is configured. If it is configured, generate the urlWithVersion with version and update the service with key=urlWithVersion. If it is greater than the corresponding service version of key=url, the default url version will be overwritten with the new version
If the service implements the InitializingService interface, call the implemented initialize method
Call ServiceMethodFactory.init(url) method to initialize ServiceMethodCache of ServiceMethodFactory: traverse all service prov id ing classes of ServicePublisher, establish ServiceMethodCache, and store mapping relationships of all methods and method IDs that meet the requirements under this class
First, all methods of Object and Class will be ignored
After filtering the method, judge whether compression is needed, and hash according to the way of url + "#" + method name.
The code is as follows

public static <T> void addService(ProviderConfig<T> providerConfig) throws Exception {
    if (logger.isInfoEnabled()) {
        logger.info("add service:" + providerConfig);
    }
    String version = providerConfig.getVersion();
    String url = providerConfig.getUrl();
    // The default version, directly with url as the key
    if (StringUtils.isBlank(version)) {
        serviceCache.put(url, providerConfig);
    } else {
        // urlWithVersion = url + "_" + version
        String urlWithVersion = getServiceUrlWithVersion(url, version);
        if (serviceCache.containsKey(url)) {
            // Overwrite service if it already exists
            serviceCache.put(urlWithVersion, providerConfig);
            ProviderConfig<?> providerConfigDefault = serviceCache.get(url);
            String defaultVersion = providerConfigDefault.getVersion();
            // If the default service has a default version and is smaller than the current version, update the default service version with the current version service
            if (!StringUtils.isBlank(defaultVersion)) {
                if (VersionUtils.compareVersion(defaultVersion, providerConfig.getVersion()) < 0) {
                    serviceCache.put(url, providerConfig);
                }
            }
        } else {
            // Set current version as specified version service and default version service
            serviceCache.put(urlWithVersion, providerConfig);
            // use this service as the default provider
            serviceCache.put(url, providerConfig);
        }
    }
    // If the service implements the InitializingService interface, call the implemented initialize method
    T service = providerConfig.getService();
    if (service instanceof InitializingService) {
        ((InitializingService) service).initialize();
    }
    // The user-defined method of the resolution interface is recorded according to the method name, parameters and other relevant information
    ServiceMethodFactory.init(url);
}

// ServiceMethodFactory.init(url); the method is implemented as follows:
public static void init(String url) {
    getServiceMethodCache(url);
}
// Specifically called
public static ServiceMethodCache getServiceMethodCache(String url) {
    // Is there a ServiceMethodCache for the specified url
    ServiceMethodCache serviceMethodCache = methods.get(url);
    if (serviceMethodCache == null) {
        // Get providerConfig for the specified url
        Map<String, ProviderConfig<?>> services = ServicePublisher.getAllServiceProviders();
        ProviderConfig<?> providerConfig = services.get(url);
        if (providerConfig != null) {
            Object service = providerConfig.getService();
            Method[] methodArray = service.getClass().getMethods();
            serviceMethodCache = new ServiceMethodCache(url, service);
            // All service methods traversing the specified url
            for (Method method : methodArray) {
                // Ignore all methods of Object and Class
                if (!ingoreMethods.contains(method.getName())) {
                    method.setAccessible(true);
                    serviceMethodCache.addMethod(method.getName(), new ServiceMethod(service, method));

                    if (isCompact) {
                        // Compress the information needed for calling url, method name, etc
                        int id = LangUtils.hash(url + "#" + method.getName(), 0, Integer.MAX_VALUE);
                        ServiceId serviceId = new ServiceId(url, method.getName());
                        ServiceId lastId = CompactRequest.PROVIDER_ID_MAP.putIfAbsent(id, serviceId);
                        // Check if the same id service method exists, throw an exception
                        if (lastId != null && !serviceId.equals(lastId)) {
                            throw new IllegalArgumentException("same id for service:" + url + ", method:"
                                    + method.getName());
                        }
                    }

                }
            }
            // Update cache
            methods.put(url, serviceMethodCache);
        }
    }
    return serviceMethodCache;
}

The start of the service mainly refers to the start of the netty server interface listening, the initialization and start of the request processing thread pool (the thread pool processing the request call chain). The code is as follows:

Here we focus on the main logic of the service publishing method ServicePublisher.publishService

If the service needs to be registered, the publishServiceToRegistry method is called here to register the service, complete the registration by creating a persistent node on the ZK, and then notify the service of changes.

To be supplemented

Published 19 original articles, won praise 1, visited 5273
Private letter follow

Posted by hmiller73 on Sun, 19 Jan 2020 00:59:17 -0800