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