[dubbo source code] 1. How to publish services by service providers

Keywords: Java Dubbo Apache Zookeeper

Service publishing

Startup process

1.ServiceConfig#export

When the service provider starts the deployment, dubbo will call serviceconfig ා export to activate the service publishing process, as shown below:

  • Java API:
// 1. Create ServiceConfig instance
            ServiceConfig<IGreetingService> serviceConfig = new ServiceConfig<>();
            // 2. Set application configuration
            serviceConfig.setApplication(new ApplicationConfig("deep-in-dubbo-first-provider"));
            // 3. Set up a registration center
            RegistryConfig registryConfig = new RegistryConfig("zookeeper://127.0.0.1:2181/");
            serviceConfig.setRegistry(registryConfig);
            // 4. Set interface and implementation class
            // 5. Set service group and version
            // In dubbo, the service interface + service grouping + service version uniquely determine a service. The same interface can have different versions, which is convenient for maintenance and upgrading
            serviceConfig.setInterface(IGreetingService.class);
            serviceConfig.setRef(new GreetingServiceImpl());
            serviceConfig.setVersion("1.0.0");
            serviceConfig.setGroup("dubbo-sxzhongf-group");
            RpcContext.getContext().setAttachment("age","18");
    
            // 7. Export the service, start Netty to listen to the link request, and register the service in the registry
            serviceConfig.export();
    
            // 8. Suspend thread to avoid service stop
            System.out.println("api provider service is started...");
            System.in.read();
  • XML
  <beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xmlns:dubbo="http://dubbo.apache.org/schema/dubbo"
         xmlns="http://www.springframework.org/schema/beans"
         xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
         http://dubbo.apache.org/schema/dubbo http://dubbo.apache.org/schema/dubbo/dubbo.xsd">
  
      <!-- provider's application name, used for tracing dependency relationship -->
      <dubbo:application name="first-xml-provider"/>
      <!-- use multicast registry center to export service -->
      <dubbo:registry address="zookeeper://127.0.0.1:2181/"/>
      <!-- use dubbo protocol to export service on port 20880 -->
      <dubbo:protocol name="dubbo" port="20880"/>
      <!-- service implementation, as same as regular local bean -->
      <bean id="demoService" class="com.sxzhongf.deep.in.dubbo.provider.service.impl.GreetingServiceImpl"/>
      <!-- declare the service interface to be exported -->
      <dubbo:service interface="com.sxzhongf.deep.in.dubbo.api.service.IGreetingService"
                     ref="demoService" version="1.0.0" group="dubbo-sxzhongf-group">
          <dubbo:method name="sayHello" async="false" timeout="0" retries="3"></dubbo:method>
          <dubbo:method name="testGeneric" async="false" timeout="10000" retries="3"></dubbo:method>
      </dubbo:service>
  </beans>

You can see from the export source code that there are three service export options:
Java public synchronized void export() {/ / 1. Export if (! Shouldexport()) {return;}... / / 2. Delay export if (shoulddelay()) {delay ﹣ export ﹣ exporter. Schedule (this:: doexport, getdelay(), timeunit. Milliseconds); }Else {/ / 3. Export doExport();}} now

2.ServiceConfig#doExport

This method mainly checks the validity according to the set property, mainly including whether it has been exported, doExportUrls();

3.doExportUrls
4.ConfigValidationUtils#loadRegistries

This method is used to load all service registry objects. In dubbo, a service can be registered to multiple registries.

Through do export URLs for 1 protocol (protocol config, registry URLs);

5.doExportUrlsFor1Protocol

In this method, all parameters are encapsulated as org.apache.dubbo.common.URL object, and then specific service export is performed.

The specific process is divided into:

  • 1. Parse MethodConfig configuration (separate method call parameter settings)

  • 2. Generic call type setting

  • 3. Splicing URL parameters

  • 4. Export specific services

    The export is divided into four scopes:

    • SCOPE_NONE = "none". If it is set to none, the service will not be exported.

    • SCOPE_LOCAL = "local", if it is set to local, it means that the service is exported to local (injvm -- pseudo protocol, implementation class: org.apache.dubbo.rpc.protocol.injvm.InjvmProtocol)

      • SCOPE_REMOTE = "remote", if set to remote, it means that the service is exported to remote.
    • If there is a registry, publish to the registry

    • If there is no registration center, the service is direct connection

    • Starting from dubbo-2.7.0, a WritableMetadataService has been added to store the metadata of Dubbo service. The metadata can be stored in the remote configuration center and local. By default, it is stored locally. By setting: METADATA_KEY = "metadata"

      • DEFAULT_METADATA_STORAGE_TYPE = "local"
      • REMOTE_METADATA_STORAGE_TYPE = "remote"
        java /** * @since 2.7.0 * ServiceData Store */ WritableMetadataService metadataService = WritableMetadataService.getExtension(url.getParameter(METADATA_KEY, DEFAULT_METADATA_STORAGE_TYPE)); if (metadataService != null) { metadataService.publishServiceDefinition(url); }

      • Do not set, export to local and remote

    • The final code to perform the export is as follows

      // Extended adapter class
      private static final Protocol protocol = ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension();
      
      /**
       * A {@link ProxyFactory} implementation that will generate a exported service proxy,the JavassistProxyFactory is its
       * default implementation
       */
      // Extended adapter class
      private static final ProxyFactory PROXY_FACTORY = ExtensionLoader.getExtensionLoader(ProxyFactory.class).getAdaptiveExtension();
      ...
      
      Invoker<?> invoker = PROXY_FACTORY.getInvoker(ref, (Class) interfaceClass, registryURL.addParameterAndEncoded(EXPORT_KEY, url.toFullString()));
      DelegateProviderMetaDataInvoker wrapperInvoker = new DelegateProviderMetaDataInvoker(invoker, this);
      
      Exporter<?> exporter = protocol.export(wrapperInvoker);
      exporters.add(exporter);

      Since both protocol and proxy? Factory are extension adaptation classes, we can find the following trace Codes:

      • When executing proxy? Factory.getInvoker, in fact, the first step is to execute the getInvoker method of ProxyFactory$Adaptive, the adaptation class of the extension interface ProxyFactory. Select a specific proxy factory according to the setting type of the parameter proxy in the URL. The default is javassist,, Therefore, we call org.apache.dubbo.rpc.proxy.javassist.javassistproxyfactory ා getInvoker to get the proxy implementation class. The code is as follows:

        /**
         * JavaassistRpcProxyFactory
         */
        public class JavassistProxyFactory extends AbstractProxyFactory {
            ...
            @Override
            public <T> Invoker<T> getInvoker(T proxy, Class<T> type, URL url) {
                // TODO Wrapper cannot handle this scenario correctly: the classname contains '$'
                // Here, we use javassist dynamic proxy to generate the wrapper class of serviceImpl implementation class ` wrapper`
                final Wrapper wrapper = Wrapper.getWrapper(proxy.getClass().getName().indexOf('$') < 0 ? proxy.getClass() : type);
                return new AbstractProxyInvoker<T>(proxy, type, url) {
                    @Override
                    protected Object doInvoke(T proxy, String methodName,
                                              Class<?>[] parameterTypes,
                                              Object[] arguments) throws Throwable {
                        return wrapper.invokeMethod(proxy, methodName, parameterTypes, arguments);
                    }
                };
            }
            ...
        }

        The above code has two purposes:

        1. inal Wrapper wrapper = Wrapper.getWrapper(...); used to generate specific serviceImpl wrapper classes to reduce reflection performance loss;
        2. Return new abstractproxyinvoker < T >... Returns an abstract proxy invoker and rewrites the doInvoker method. After rewriting, the invokeMethod in the wrapper class is used to call the method.

        After the above two steps, the service provider converts the specific implementation class to the Invoker agent.

      • Then, when executing protocol.export(), the protocol $adaptiveාexport() method is also called, and there are two cases

        • If it is a remote exposure, execute registryprotocol ා export
        • If it is a local exposure, only the injvmprotocol ා export is required

        Due to Dubbo's enhanced SPI feature support, injectExtension((T) wrapperClass.getConstructor(type).newInstance(instance)); will be called layer by layer before calling, protocolfilterwrapper - > protocollistenerwrapper - > qosprotocolwrapper, Finally, the export method will be called, which will convert the Invoker to the Exporter object in the org.apache.dubbo.registry.integration.registryprotocol ා export method, The org.apache.dubbo.registry.integration.registryprotocol ා dolocalexport method enables NettyServer to listen to services. Org.apache.dubbo.registry.integration.registryprotocol ා register the current services to the registry.

        • How does doLocalExport start NettyServer?

              private <T> ExporterChangeableWrapper<T> doLocalExport(final Invoker<T> originInvoker, URL providerUrl) {
                  String key = getCacheKey(originInvoker);
          
                  return (ExporterChangeableWrapper<T>) bounds.computeIfAbsent(key, s -> {
                      Invoker<?> invokerDelegate = new InvokerDelegate<>(originInvoker, providerUrl);
                      return new ExporterChangeableWrapper<>((Exporter<T>) protocol.export(invokerDelegate), originInvoker);
                  });
              }

          At this time, the protocol type in the URL is the default dubbo, so dubboprotocol ා export will be executed for conversion, as follows:

          @Override
              public <T> Exporter<T> export(Invoker<T> invoker) throws RpcException {
                  URL url = invoker.getUrl();
          
                  // export service.
                  String key = serviceKey(url);
                  // invoker->exporter
                  DubboExporter<T> exporter = new DubboExporter<T>(invoker, key, exporterMap);
                  exporterMap.put(key, exporter);
          
                  //export an stub service for dispatching event
                  Boolean isStubSupportEvent = url.getParameter(STUB_EVENT_KEY, DEFAULT_STUB_EVENT);
                  Boolean isCallbackservice = url.getParameter(IS_CALLBACK_SERVICE, false);
                  if (isStubSupportEvent && !isCallbackservice) {
                      String stubServiceMethods = url.getParameter(STUB_EVENT_METHODS_KEY);
                      if (stubServiceMethods == null || stubServiceMethods.length() == 0) {
                          if (logger.isWarnEnabled()) {
                              logger.warn(new IllegalStateException("consumer [" + url.getParameter(INTERFACE_KEY) +
                                      "], has set stubproxy support event ,but no stub methods founded."));
                          }
          
                      } else {
                          stubServiceMethodsMap.put(url.getServiceKey(), stubServiceMethods);
                      }
                  }
                  //Create server
                  openServer(url);
                  //Serialization prompt
                  optimizeSerialization(url);
          
                  return exporter;
              }

          You can see that the code is executed to openServer. Because key=getAddress()=ip+port, only one NettyServer will be opened on the same machine

              private void openServer(URL url) {
                  // find server.
                  String key = url.getAddress();
                  //client can export a service which's only for server to invoke
                  boolean isServer = url.getParameter(IS_SERVER_KEY, true);
                  if (isServer) {
                      ProtocolServer server = serverMap.get(key);
                      if (server == null) {
                          synchronized (this) {
                              server = serverMap.get(key);
                              if (server == null) {
                                  serverMap.put(key, createServer(url));
                              }
                          }
                      } else {
                          // server supports reset, use together with override
                          server.reset(url);
                      }
                  }
              }

          There are three types of adaptation classes for org.apache.dubbo.remoting.Transporter: MinaTransporter, NettyTransporter and GrizzlyTransporter. For JavaNIO, compare Apache Mina, JBoss Netty and Sun Grizzly frameworks: Portal

        • After starting NettyServer, go back to org.apache.dubbo.registry.integration.registryprotocol ා export method and continue to register the service to the registry. Take Zookeeper as an example:

          • 1. First find all registration centers

            final Registry registry = getRegistry(originInvoker);
            ...
            protected Registry getRegistry(final Invoker<?> originInvoker) {
                URL registryUrl = getRegistryUrl(originInvoker);
                return registryFactory.getRegistry(registryUrl);
            }

            Because RegistryFactory is a SPI extension interface, the code is set to zookeeper, so here is called ZookeeperRegistryFactory, inherited from: org.apache.dubbo.registry.support.AbstractRegistryFactory#getRegistry(org.apache.dubbo.common.URL), createRegistry is called in this method, but ZookeeperRegistryFactory rewrites createRegistry. Therefore, the specific call is zookeeperregistryfactory ා createregistry, which returns a new ZookeeperRegistry(url, zookeeperTransporter) instance object.

          • 2. Start registration. The registryprotocol ා register method performs the registration action. First, get the registration center zookeeperregistry we found in the previous step. Zookeeperregistry executes the parent class org. Apache. Dubbo. Registry. Support. Failbackregistry ා register. In this method, we call the abstract method: doregister, If zookeeperregistry overrides the modified method, execute zookeeperregistry ා doregister, as follows:

            @Override
            public void doRegister(URL url) {
                try {
                    zkClient.create(toUrlPath(url), url.getParameter(DYNAMIC_KEY, true));
                } catch (Throwable e) {
                    throw new RpcException("Failed to register " + url + " to zookeeper " + getUrl() + ", cause: " + e.getMessage(), e);
                }
            }
          • 3. The tourlpath method will convert org.apache.dubbo.common.URL into a format and store it in zookeeper, as follows:

            dubbo://172.16.44.21:20880/com.sxzhongf.deep.in.dubbo.api.service.IGreetingService?anyhost=true&application=deep-in-dubbo-first-provider&default=true&deprecated=false&dubbo=2.0.2&dynamic=true&generic=false&group=dubbo-sxzhongf-group&interface=com.sxzhongf.deep.in.dubbo.api.service.IGreetingService&methods=sayHello,testGeneric&pid=8480&release=2.7.5&revision=1.0.0&side=provider&timestamp=1582872610313&version=1.0.0
            
            -----------------------Transformation------------------------
            
            /dubbo/com.sxzhongf.deep.in.dubbo.api.service.IGreetingService/providers/dubbo%3A%2F%2F172.16.44.21%3A20880%2Fcom.sxzhongf.deep.in.dubbo.api.service.IGreetingService%3Fanyhost%3Dtrue%26application%3Ddeep-in-dubbo-first-provider%26default%3Dtrue%26deprecated%3Dfalse%26dubbo%3D2.0.2%26dynamic%3Dtrue%26generic%3Dfalse%26group%3Ddubbo-sxzhongf-group%26interface%3Dcom.sxzhongf.deep.in.dubbo.api.service.IGreetingService%26methods%3DsayHello%2CtestGeneric%26pid%3D8480%26release%3D2.7.5%26revision%3D1.0.0%26side%3Dprovider%26timestamp%3D1582872610313%26version%3D1.0.0

            The format after conversion is actually the same as we see in zookeeper, but there are several directories:

            • dubbo
            • com.sxzhongf.deep.in.dubbo.api.service.IGreetingService
            • providers
            [zk: localhost:2181(CONNECTED) 2] ls  /dubbo/com.sxzhongf.deep.in.dubbo.api.service.IGreetingService/providers
            [dubbo%3A%2F%2F172.16.44.21%3A20880%2Fcom.sxzhongf.deep.in.dubbo.api.service.IGreetingService%3Fanyhost%3Dtrue%26application%3Ddeep-in-dubbo-first-provider%26default%3Dtrue%26deprecated%3Dfalse%26dubbo%3D2.0.2%26dynamic%3Dtrue%26generic%3Dfalse%26group%3Ddubbo-sxzhongf-group%26interface%3Dcom.sxzhongf.deep.in.dubbo.api.service.IGreetingService%26methods%3DsayHello%2CtestGeneric%26pid%3D15716%26release%3D2.7.5%26revision%3D1.0.0%26side%3Dprovider%26timestamp%3D1582872850187%26version%3D1.0.0]

At this point, the service consumer can obtain the service provider service from the registry for calling. In the next section, we will continue to analyze how the consumer pulls the service from the registry for processing.

Posted by [-_-] on Thu, 27 Feb 2020 23:53:21 -0800