preface:
Followed by the previous article. The above analyzes the whole process of the consumer sending the request, and finally sends the request to the Provider through NettyChannel. Of course, a series of processing has been carried out in the middle.
From the perspective of Provider, this paper discusses how the service Provider handles the request after receiving the request.
1. Code example
The Provider example is shown above
public class ProviderApplication { public static void main(String[] args) throws Exception { ServiceConfig<DemoServiceImpl> service = new ServiceConfig<>(); service.setInterface(DemoService.class); service.setRef(new DemoServiceImpl()); service.setApplication(new ApplicationConfig("dubbo-demo-api-provider")); service.setRegistry(new RegistryConfig("zookeeper://127.0.0.1:2181")); service.export(); System.out.println("dubbo service started"); new CountDownLatch(1).await(); } }
2. The whole process of service provider processing request
2.1 DecodeHandler
The service provider exposes a Netty server, and all requests will be processed by its Handler. Therefore, the service provider first parses the received messages through the DecodeHandler
public class DecodeHandler extends AbstractChannelHandlerDelegate { public void received(Channel channel, Object message) throws RemotingException { if (message instanceof Decodeable) { decode(message); } // Parse the Request message into a Request object if (message instanceof Request) { decode(((Request) message).getData()); } if (message instanceof Response) { decode(((Response) message).getResult()); } // Continue to be handled by HeaderExchangeHandler handler.received(channel, message); } }
The encapsulation relationship between DecodeHandler and HeaderExchangeHandler has been determined in HeaderExchanger.
public class HeaderExchanger implements Exchanger { public static final String NAME = "header"; @Override public ExchangeClient connect(URL url, ExchangeHandler handler) throws RemotingException { return new HeaderExchangeClient(Transporters.connect(url, new DecodeHandler(new HeaderExchangeHandler(handler))), true); } @Override public ExchangeServer bind(URL url, ExchangeHandler handler) throws RemotingException { return new HeaderExchangeServer(Transporters.bind(url, new DecodeHandler(new HeaderExchangeHandler(handler)))); } }
2.2 HeaderExchangeHandler
public class HeaderExchangeHandler implements ChannelHandlerDelegate { public void received(Channel channel, Object message) throws RemotingException { final ExchangeChannel exchangeChannel = HeaderExchangeChannel.getOrAddChannel(channel); if (message instanceof Request) { // handle request. Request request = (Request) message; if (request.isEvent()) { handlerEvent(channel, request); } else { if (request.isTwoWay()) { // Continue processing handleRequest(exchangeChannel, request); } else { handler.received(exchangeChannel, request.getData()); } } } ... } void handleRequest(final ExchangeChannel channel, Request req) throws RemotingException { Response res = new Response(req.getId(), req.getVersion()); ... Object msg = req.getData(); try { // What is this Handler? Refer to 2.2.1 for details CompletionStage<Object> future = handler.reply(channel, msg); // After execution, add the result to the Response future.whenComplete((appResult, t) -> { try { if (t == null) { res.setStatus(Response.OK); res.setResult(appResult); } else { res.setStatus(Response.SERVICE_ERROR); res.setErrorMessage(StringUtils.toString(t)); } channel.send(res); } catch (RemotingException e) { logger.warn("Send result to consumer failed, channel is " + channel + ", msg is " + e); } }); } catch (Throwable e) { res.setStatus(Response.SERVICE_ERROR); res.setErrorMessage(StringUtils.toString(e)); channel.send(res); } } }
2.2.1 adding headerexchangehandler.handler property
There is nothing unexplained in the world of code. All attributes are added somewhere. So what is the HeaderExchangeHandler.handler property?
In the DubboProtocol.createServer() method:
public class DubboProtocol extends AbstractProtocol { private ExchangeHandler requestHandler = new ExchangeHandlerAdapter() {...} private ProtocolServer createServer(URL url) { ... ExchangeServer server; try { // Bind the current requestHandler to the url server = Exchangers.bind(url, requestHandler); } catch (RemotingException e) { throw new RpcException("Fail to start server(url: " + url + ") " + e.getMessage(), e); } str = url.getParameter(CLIENT_KEY); if (str != null && str.length() > 0) { Set<String> supportedTypes = ExtensionLoader.getExtensionLoader(Transporter.class).getSupportedExtensions(); if (!supportedTypes.contains(str)) { throw new RpcException("Unsupported client type: " + str); } } return new DubboProtocolServer(server); } }
requestHandler is subsequently bound to HeaderExchanger.bind() or connect() methods.
Therefore, DubboProtocol.requestHandler is used to process the request body
2.3 DubboProtocol.requestHandler.reply()
private ExchangeHandler requestHandler = new ExchangeHandlerAdapter() { @Override public CompletableFuture<Object> reply(ExchangeChannel channel, Object message) throws RemotingException { ... Invocation inv = (Invocation) message; // The key point here is to obtain the specific Invoker in DubboExporter through the request parameters in Invocation // See 2.3.1 for details Invoker<?> invoker = getInvoker(channel, inv); ... RpcContext.getContext().setRemoteAddress(channel.getRemoteAddress()); Result result = invoker.invoke(inv); return result.thenApply(Function.identity()); } }
2.3.1 obtain specific DubboInvoker processing information through the request body
public class DubboProtocol extends AbstractProtocol { Invoker<?> getInvoker(Channel channel, Invocation inv) throws RemotingException { boolean isCallBackServiceInvoke = false; boolean isStubServiceInvoke = false; int port = channel.getLocalAddress().getPort(); String path = (String) inv.getObjectAttachments().get(PATH_KEY); // if it's callback service on client side isStubServiceInvoke = Boolean.TRUE.toString().equals(inv.getObjectAttachments().get(STUB_EVENT_KEY)); if (isStubServiceInvoke) { port = channel.getRemoteAddress().getPort(); } //callback isCallBackServiceInvoke = isClientSide(channel) && !isStubServiceInvoke; if (isCallBackServiceInvoke) { path += "." + inv.getObjectAttachments().get(CALLBACK_SERVICE_KEY); inv.getObjectAttachments().put(IS_CALLBACK_SERVICE_INVOKE, Boolean.TRUE.toString()); } String serviceKey = serviceKey( port, path, (String) inv.getObjectAttachments().get(VERSION_KEY), (String) inv.getObjectAttachments().get(GROUP_KEY) ); DubboExporter<?> exporter = (DubboExporter<?>) exporterMap.get(serviceKey); if (exporter == null) { throw new RemotingException(channel, "Not found exported service: " + serviceKey + " in " + exporterMap.keySet() + ", may be version or group mismatch " + ", channel: consumer: " + channel.getRemoteAddress() + " --> provider: " + channel.getLocalAddress() + ", message:" + getInvocationWithoutData(inv)); } return exporter.getInvoker(); } }
So far, we have returned to the main battlefield of the provider. The processing of requests starts from Filter, then proxy (AbstractProxyInvoker), and finally to the specific implementation class
2.4 various Filter processing
EchoFilter, ClassLoaderFilter, GenericFilter, etc. are not the focus of this article. The details of Filter processing will be introduced later
2.5 AbstractProxyInvoker.doInvoke()
It is an abstract class. When the Provider is started, we will create an Invoker corresponding to the implementation class, as shown below
public class JavassistProxyFactory extends AbstractProxyFactory { @Override public <T> Invoker<T> getInvoker(T proxy, Class<T> type, URL url) { // Wrapper is also a dynamically generated implementation class, 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); } }; } }
After the request enters, the obtained Invoker is the current AbstractProxyInvoker, which is used to process the client request.
Therefore, we finally enter the Wrapper.invokeMethod() method, and the Wrapper is a dynamically generated proxy class. We can recall the specific contents of the Wrapper before
2.6 Wrapper0.invokeMethod() process request
public class Wrapper0 extends Wrapper implements ClassGenerator.DC { public Object invokeMethod(Object paramObject, String paramString, Class[] paramArrayOfClass, Object[] paramArrayOfObject) throws InvocationTargetException { DemoServiceImpl localDemoServiceImpl; try { localDemoServiceImpl = (DemoServiceImpl)paramObject; } catch (Throwable localThrowable1) { throw new IllegalArgumentException(localThrowable1); } try { // Here, we call localDemoServiceImpl, which is the class referenced by setRef() method when creating ServiceConfig. In this case, it is new DemoServiceImpl() if ((!"sayHello".equals(paramString)) || (paramArrayOfClass.length == 1)) { return localDemoServiceImpl.sayHello((String)paramArrayOfObject[0]); } if ((!"sayHelloAsync".equals(paramString)) || (paramArrayOfClass.length == 1)) { return localDemoServiceImpl.sayHelloAsync((String)paramArrayOfObject[0]); } } catch (Throwable localThrowable2) { throw new InvocationTargetException(localThrowable2); } throw new NoSuchMethodException("Not found method \"" + paramString + "\" in class org.apache.dubbo.demo.provider.DemoServiceImpl."); } }
Therefore, it is clear that the final call to the specific implementation class through the Wrapper
Similarly, the sequence diagram is used to show the whole process