Today I’m going to talk about Dubbo service remote calls. When I started to read the source code related to Dubbo remote service, I was a little confused. Later it became clear that the essence of the Dubbo remote service invocation was an implementation of the dynamic proxy pattern. The e local consumer does not need to know the implementation of the remote service, the consumer and provider interact with each other through proxy classes!
JAVA dynamic proxy
A quick code review of dynamic proxies:
public class MyInvocationHandler implements InvocationHandler{
private Object object;
public MyInvocationHandler(Object object){
this.object = object;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
Object result = method.invoke(object, args);
returnresult; }}public static void main(String[] args) {
MyInvocationHandler handler = new MyInvocationHandler(stu);
// Generate the proxy class
Student proxy = (Student)Proxy.newProxyInstance(loader, interfaces, handler);
// Call the sayHello method through the proxy class
proxy.sayHello(message);
}
Copy the code
The core steps to implement dynamic Proxy are: 1. Customize the Handler class that implements the InvocationHandler interface and rewrite the invoke() method; 2. Create Proxy class by calling proxy.newProxyInstance
When we finally call proxy.sayHello(), the proxy class invokes the Invoke () method of MyInvocationHandler, which eventually invokes sayHello() via reflection. Now that we know the core components of dynamic proxies, let’s look at the implementation of Dubbo remote service invocation in combination with these two points.
Create a remote service proxy class
Create time
The < Dubbo :service /> tag is parsed as a ServiceBean object. The < Dubbo :service /> tag is parsed as a ServiceBean object. Similarly, the
- Create the Invoker object
invoker = refprotocol.refer(interfaceClass, urls.get(0)); Copy the code
The object returned here is an Invoker object. The Invoke class is an interface class that defines an Invoke () method.
- Call the factory class to create the proxy object
return (T) proxyFactory.getProxy(invoker); Copy the code
In this section, we only need to get a general idea of the proxy class creation process, which we will explore in detail later.
Create the invoker
invoker = refprotocol.refer(interfaceClass, urls.get(0));
Copy the code
Name: registryprotocol.refer () name: registryprotocol.refer () name: RegistryProtocol. Refprotocol. refer() process is relatively long, first put a sequence diagram to let you have a basic impression:
Here’s a quick summary of the main points from the above:
- RefProtocolProtocol. Refer () we have made here refer above () method is the final call is RegistryProtocol refer () method. * * in refer () method will be called first registryFactory. GetRegistry (url) to obtain the Registry objects (Dubbo SPI mechanism); ** then calls the doRefer() method.
- doRefer()
- Registry.register () is introduced in step3
- Directory. Subscribe () is introduced in step4
- Registry. Register () the registry used by the author for debugging is ZooKeeper (actually officially recommended), so a node will be created on ZooKeeper. Nodes are similar: dubbo/org. Apache. Dubbo. Service. DemoService/consumers/url, if not set the dynamic parameter, the default for temporary node;
- RegistryDirectory.subscribe(url)
- The category of the current url parameter value is set: will, consumers, and routers. Subscribe (url, this) is called, and the FailbackRegistry class subscribe() is called.
- Note also that the RegistryDirectory class implements the notify() method in the NotifyListener interface:
- FailbackRegistry.subscribe()
- Call the doSubscribe() method, as described in Step6
- ZookeeperRegistry.doSubscribe()
- Url will be toCategoriesPath convert () method is similar to the following the path in the form of collection/dubbo/org. Apache. Dubbo. Service. DemoService/will /dubbo/org.apache.dubbo.service.DemoService/consumers /dubbo/org.apache.dubbo.service.DemoService/routers
- Create the corresponding path node on ZooKeeper and add a listener that executes the notify() method of the ZookeeperRegistry class when it detects changes
- If the path node is empty, set the protocol value of the URL to empty. Child nodes are not empty, and qualifying urls are added to the collection of urls. PS: if the provider service has successfully launched, dubbo/org. Apache. Dubbo. Service. DemoService/will path should be longer nodes.
- Executing notify
- notify(url, listener, urls); To execute the doNotify() method, see step8
- DoNotify (URL, Listener, urls) calls notify() of the parent class
- AbstractRegistry.notify(URL url, NotifyListener listener, List urls)
- The urls you get here were generated in step6. We classify them by the category value in the URL, and finally put them in a map set (key is category, value is URL set)
- Iterate over the map collection generated above and execute the listener.notify(categoryList) method. The listener is a RegistryDirecotry object that is passed as a parameter in Step4;
- RegistryDirectory.notify(categoryList)
- Iterate through the categoryList and add urls with category values of providers to the invokerUrls collection
- Implement the refreshInvoker(invokerUrls) method
- RefreshInvoker (invokerUrls) refreshInvoker is very important for converting invokerUrls into Invoker objects.
- If invokerUrls has only one record and the protocol parameter is empty, access is prohibited
- Call the toInvokers() method
- toInvokers(List urls)
A local cache, urlInvokerMap, is maintained with a url string key;- Traverse the urls. If the value of the URL in the urlInvokerMap collection is not empty, execute the following code:
invoker = new InvokerDelegate<T>(protocol.refer(serviceType, url), url, providerUrl); Copy the code
This creates a new DubboInvoker object and returns it, which we’ll look at in more detail later;
- Take the value directly from the cache without reconstructing the invoker object;
- Traverse the urls. If the value of the URL in the urlInvokerMap collection is not empty, execute the following code:
- ToMethodInvokers step12 will eventually generate a set with key url and value invoker. The final collection returned after processing here is method key and value invoker collection
- cluster.join(directory); Finally, Dubbo SPI mechanism is used, and the actual call flow is as follows: Cluster$Adaptive. Join () == “mockClusterWrapper.join () ==> FailoverCluster().join() ==> FailoverClusterInvoker() ==> AbstractClusterInvoker() finally returns a MockClusterInvoker object
DubboInvoker object creation process
We left the creation of invoker a little unfinished in the previous section. The code is as follows:
invoker = new InvokerDelegate<T>(protocol.refer(serviceType, url), url, providerUrl);
Copy the code
- protocol.refer()The Dubbo SPI mechanism is used here again, and the simple call flow is given as usual:$the Adaptive Protocol. Refer () = = “ProtocolListenerWrapper. Refer () = =” ProtocolFilterWrapper. Refer () = =” DubboProtocol.refer()
This is built in the dubboprotocol.refer () methodDubboInvokerObject.DubboInvoker<T> invoker = new DubboInvoker<T>(serviceType, url, getClients(url), invokers); Copy the code
The overall process is relatively simple, but notice that there is an important method here: getClient(URL). What is it used for? Remember when we started the Netty server in the remote exposure section of service exposure? There was a hole about where the Netty client started. GetClients () is used to start the Netty client.
- GetClients (URL) If connections is not set in the URL, the link is shared by default, and getSharedClient() is called to get an ExchangeClient object.
- GetSharedClient (URL URL) getSharedClient() as the name implies is used to obtain the share client. The referenceClientMap collection is used to cache clients, and the key value is the URL address parameter. If the cached value is null, the initClient(URL) method is called to create ExchangeClient
- InitClient (URL) calls exchangers.connect () to build the client. The returned client is assigned to the Clients member variable of the DubboInvoker class via the constructor.
- Exchangers.connect()
return getExchanger(url).connect(url, handler); Copy the code
- Call getExchanger(URL) to retrieve the HeaderRecovery class.
- Call the headersanoserver. connet() method to establish the connection
- HeaderExchanger.connect()
public ExchangeClient connect(URL url, ExchangeHandler handler) throws RemotingException { return new HeaderExchangeClient(Transporters.connect(url, new DecodeHandler(new HeaderExchangeHandler(handler))), true); } Copy the code
The core method is transporters.connect ()
- Transporters.connect()
return getTransporter().connect(url, handler); Copy the code
- Call getTransporter() to get the NettyTransporter class (Dubbo SPI mechanism)
- Call the nettyTransporter.connet () method to establish the connection
- NettyTransporter.connet()
public Client connect(URL url, ChannelHandler listener) throws RemotingException { return new NettyClient(url, listener); } Copy the code
The NettyClient class constructor calls the AbstractClient constructor of the parent class. There are two core methods:
- DoOpen () initializes bootstrap
- Summary: In this section, we introduce the process of creating a DubboInvoker object and when to create a Netty client connection. So far the Invoker creation process has been pretty much covered!
4. Create a proxy class
To create a proxy class, refer to the last sentence of the createProxy() method in ReferenceConfig:
return (T) proxyFactory.getProxy(invoker);
Copy the code
The creation of the Invoker object is examined in detail in section 2. So what does proxyFactory’s getProxy() do? In fact, the Dubbo SPI mechanism is used to implement this. The execution process is roughly as follows: ProxyFactory. GetProxy (invoker) = = “StubProxyFactoryWrapper. GetProxy (invoker) = =” AbstractProxyFactory. GetProxy (invoker) = = > AbstractProxyFactory. GetProxy (invoker, generic) = = > JavassistProxyFactory. GetProxy (invoker, Interfaces) focus on JavassistProxyFactory. GetProxy () method:
public <T> T getProxy(Invoker
invoker, Class
[] interfaces)
{
return (T) Proxy.getProxy(interfaces).newInstance(new InvokerInvocationHandler(invoker));
}
Copy the code
There are two points worth mentioning here:
- The InvokerInvocationHandler class implements the InvocationHandler interface.
- Proxy.getproxy (interfaces) uses Javassist bytecode technology to generate dynamic proxies. The generated proxy class looks like this:
package org.apache.dubbo.common.bytecode; import com.alibaba.dubbo.rpc.service.EchoService; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import org.apache.dubbo.common.bytecode.ClassGenerator.DC; import org.apache.dubbo.demo.DemoService; public class proxy0 implements DC.EchoService.DemoService { public static Method[] methods; private InvocationHandler handler; public proxy0(InvocationHandler var1) { this.handler = var1; } public proxy0(a) {}public String sayHello(String var1) { Object[] var2 = new Object[]{var1}; Object var3 = this.handler.invoke(this, methods[0], var2); return (String)var3; } public Object $echo(Object var1) { Object[] var2 = new Object[]{var1}; Object var3 = this.handler.invoke(this, methods[1], var2); return(Object)var3; }}Copy the code
Finally, how to view proxy classes generated using Javassist bytecode technology on Windows!!
- Go to the current JDK directory, for example: C:\Program Files\Java\jdk1.8.0\
- A dialog box will pop up after executing the command Java -cp lib/sa-jdi.jar sun.jjvm. Hotspot.hsdb
- Click File == “Attach to HotSpot Process and enter the current process PID. Task Manager…)
- If sawindbg.dll is not found, don’t panic… C: Program Files\Java\jdk1.8.0\jre\bin
- Finally click on Tools ==> Class Browser and you can see a lot of classes…
- Select a calss and click Create. Class File to Create a.class File in the current directory
Summary: this section of the water ~ but it doesn’t matter, the meaning has arrived!!