background

The previous article looked at some of the logic on the Dubbo server side. This article begins by looking at the knowledge on the consumer side, which is a bit more complicated than the production side, but is easier to understand.

Focus on

  • Creation and wrapping of consumer side Invoker objects
  • How do I combine an Invoker object with a bean object referenced by the client

Analysis of the

Spring entrance

The consumer’s initialization entry is still Spring’s bean post-processing, so let’s review its use on the consumer side

<dubbo:reference id="helloService" interface="com.xx.HelloService" />

public class HelloController {

    @Resource
    private HelloService helloService;
Copy the code

The Dubbo client’s references to the server interface are also hosted in the Spring container and registered as beans through the ReferenceBean class.

public class ReferenceBean<T> extends ReferenceConfig<T> implements FactoryBean.ApplicationContextAware.InitializingBean {
    @Override
    public Object getObject(a) { returnget(); }public synchronized T get(a) {
        checkAndUpdateSubConfigs();
        if (ref == null) {
            init();
        }
        returnref; }}Copy the code

The modified class implements FactoryBeans with personalized bean-creation logic, as well as spring lifecycle capabilities.

Invoker object creation

Into the init ()

org.apache.dubbo.config.ReferenceConfig#init
private void init(a) {
		/ /...
        ref = createProxy(map);
        //....
}
org.apache.dubbo.config.ReferenceConfig#createProxy
private T createProxy(Map<String, String> map) {
	/ /...
   invoker = REF_PROTOCOL.refer(interfaceClass, urls.get(0));
   return (T) PROXY_FACTORY.getProxy(invoker);
}  
private static Protocol REF_PROTOCOL = ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension();
Copy the code

The invoker gets it from the Protocol adaptive extension point, urls.get(0) gets it from the registry, and we move on.

org.apache.dubbo.registry.integration.RegistryProtocol#refer
public <T> Invoker<T> refer(Class<T> type, URL url) throws RpcException {
        url = URLBuilder.from(url)
                .setProtocol(url.getParameter(REGISTRY_KEY, DEFAULT_REGISTRY))
                .removeParameter(REGISTRY_KEY)
                .build();
        Registry registry = registryFactory.getRegistry(url);/ / 1
        return doRefer(cluster, registry, type, url);
    }
Copy the code

The RegistryFactory will find a matching Registry based on the configuration type. Here we use ZooKeeper (ZookeeperRegistry). Look at doRefer().

private <T> Invoker<T> doRefer(Cluster cluster, Registry registry, Class<T> type, URL url) {
        RegistryDirectory<T> directory = new RegistryDirectory<T>(type, url);
        directory.setRegistry(registry);
        directory.setProtocol(protocol);
        // all attributes of REFER_KEY
        Map<String, String> parameters = new HashMap<String, String>(directory.getUrl().getParameters());
        URL subscribeUrl = new URL(CONSUMER_PROTOCOL, parameters.remove(REGISTER_IP_KEY), 0, type.getName(), parameters);
        if(! ANY_VALUE.equals(url.getServiceInterface()) && url.getParameter(REGISTER_KEY,true)) {
            directory.setRegisteredConsumerUrl(getRegisteredConsumerUrl(subscribeUrl, url));
            registry.register(directory.getRegisteredConsumerUrl());
        }
        directory.buildRouterChain(subscribeUrl);
        directory.subscribe(subscribeUrl.addParameter(CATEGORY_KEY,
                PROVIDERS_CATEGORY + "," + CONFIGURATORS_CATEGORY + "," + ROUTERS_CATEGORY));

        Invoker invoker = cluster.join(directory);
        ProviderConsumerRegTable.registerConsumer(invoker, url, subscribeUrl, directory);
        return invoker;
    }
Copy the code

Finally appeared invoker, but there are new concepts “directory” directory, “cluster” cluster, directory function is from a number of producers to select the most appropriate as the call target address, cluster is mainly as a call failure fault tolerance strategy, Cluster is also an adaptive extension point, with the default “FailoverCluster fail retry “extension (including wrapper extensions, of course). Let’s go to cluster.join().

org.apache.dubbo.rpc.cluster.support.wrapper.MockClusterWrapper#join
public class MockClusterWrapper implements Cluster {
@Override
public <T> Invoker<T> join(Directory<T> directory) {
return new MockClusterInvoker<T>(directory,this.cluster.join(directory));
}
org.apache.dubbo.rpc.cluster.support.FailoverCluster#join
public class FailoverCluster implements Cluster {
    public final static String NAME = "failover";
    @Override
    public <T> Invoker<T> join(Directory<T> directory) {
        return new FailoverClusterInvoker<T>(directory);
}
Copy the code

So invoker was eventually packaged as

new MockClusterInvoker(directory, new FailoverClusterInvoker(directory));

Proxy object creation

We continue to go back to the org. Apache. Dubbo. Config. ReferenceConfig# createProxy method invoker value we have analysis results, For those of you who have seen my other service exposure article PROXY_FACTORY is also an adaptive extension point for ProxyFactory. The default implementation is Javassist

private static ProxyFactory PROXY_FACTORY = ExtensionLoader.getExtensionLoader(ProxyFactory.class).getAdaptiveExtension();

public class JavassistProxyFactory extends AbstractProxyFactory {
    @Override
    public <T> T getProxy(Invoker
       
         invoker, Class
        [] interfaces)
        {
        return (T) Proxy.getProxy(interfaces).newInstance(new InvokerInvocationHandler(invoker));
    }
Copy the code

The Proxy to pay more attention to, and not in the JDK and but org.apache.dubbo.com mon. The bytecode. Proxy, we take a look at the debug generated Proxy class.

org.apache.dubbo.common.bytecode.Proxy#getProxy
public static Proxy getProxy(Class
       ... ics) {
    return getProxy(ClassUtils.getClassLoader(Proxy.class), ics);
}
Copy the code

As you can see, the getProxy method generates a new method body for all the methods in the API interface, which is then called by reflection.Notice that unlike the producer, the producer is called by if else

// The regenerated method
Object[] args = new Object[1]; 
args[0] = ($w)$1; 
Object ret = handler.invoke(this, methods[0], args); / / InvokerInvocationHandler direct call
return ret;
Copy the code

The class that reflects the callback is passed in InvokerInvocationHandler from the getProxy method and this class implements Java’s InvocationHandler New InvokerInvocationHandler(Invoker). When we create the object, we pass in the Invoker object for future remote calls. Such PROXY_FACTORY. GetProxy (invoker); The method is parsed and returns a bytecode object generated by JavAssist. This object holds the Invoker remote call object.