前言:rpc模块是远程调用模块,抽象各种协议,以及动态代理,只包含一对一的调用,不关心集群的管理。这个模块的学习将使我们对服务的发布和调用更加清晰。
重点学习这几个类的作用和它们的实现类:ProxyFactory、Invoker、Protocol、Exporter
Invoker
这是一个可执行的对象,能够根据方法,参数得到执行结果,代码如下:
public interface Invokerextends Node { Class getInterface(); Result invoke(Invocation invocation) throws RpcException;}
里面的Invocation包含了要执行的方法和参数
public interface Invocation { String getMethodName(); Class>[] getParameterTypes(); Object[] getArguments(); MapgetAttachments(); String getAttachment(String key); String getAttachment(String key, String defaultValue); Invoker> getInvoker();}
看一下Invocation的实现类:RpcInvocation。简略代码如下:
public class RpcInvocation implements Invocation, Serializable { private static final long serialVersionUID = -4355285085441097045L; private String methodName; private Class>[] parameterTypes; private Object[] arguments; private Mapattachments; private transient Invoker> invoker;}
其实也只是提供了Invocation所需的参数而已。我们把目光又放回Invoker。
Invoker的执行过程分为三种类型
(1)本地执行类的Invoker
(2)远程通信执行类的Invoker
(3)多个(2)的Invoker聚合成的集群版的Invoker(需要设计到负载均衡)
ProxyFactory
对于server端,主要负责将服务统一进行包装成一个Invoker,这些Invoker通过反射来执行具体的对象的方法。
@SPI("javassist")public interface ProxyFactory { @Adaptive({Constants.PROXY_KEY})T getProxy(Invoker invoker) throws RpcException; @Adaptive({Constants.PROXY_KEY}) Invoker getInvoker(T proxy, Class type, URL url) throws RpcException;}
实现类主要有JdkProxyFactory、JavassistProxyFactory。默认是JavassistProxyFactory
Protocol
我们发布服务的第一个过程是将服务封装成一个本地执行的invoker,执行服务这个就是执行这个invoker(调用这个invoker的invoke方法),通过反射执行。但是我们看到invoke方法的参数-invocation,这个参数是如何得来的?
服务端的Protocol需要根据指定的协议对外公布服务,当客户端根据协议调用这个服务时候,将用户传递的invocation参数交给invoker来执行,所以Protocol加入了远程通信协议这一块,根据用户的请求获取参数Invocation。
@Extension("dubbo")public interface Protocol { int getDefaultPort(); @AdaptiveExporter export(Invoker invoker) throws RpcException; @Adaptive Invoker refer(Class type, URL url) throws RpcException; void destroy();}
服务发布的第二步是暴露invoker。
Exporter> exporter = protocol.export(invoker);
暴露过程即根据invoker的URL的配置信息来最终选择Protocol实现,默认是dubbo,扩展实现即DubboProtocol,然后再对DubboProtocol进行依赖注入,进行wrap包装。先来看看Protocol的实现情况:
可以看到在返回DubboProtocol之前,经过了ProtocolFilterWrapper(核心方法是buildInvokerChain,构建Chain)、ProtocolListenerWrapper、RegistryProtocol的包装。
包装时候的是装饰模式,类似AOP功能
package com.alibaba.xxx;import com.alibaba.dubbo.rpc.Protocol;public class XxxProtocolWrapper implemenets Protocol { Protocol impl; public XxxProtocol(Protocol protocol) { impl = protocol; } // 接口方法做一个操作后,再调用extension的方法 public Exporterexport(final Invoker invoker) { //... 一些操作 impl .export(invoker); // ... 一些操作 } // ...}
当服务发布时候会先经过RegistryProtocol,这个类的主要功能如下:
1,利用内部的Protocol即DubboProtocol,将服务进行导出,
2,根据注册中心的registryUrl获取注册服务Registry,然后将serviceUrl注册到注册中心上,供客户端订阅
接下来就需要看到DubboProtocol类了,注重看它的服务导出功能。
publicExporter export(Invoker invoker) throws RpcException { URL url = invoker.getUrl() // export service. String key = serviceKey(url) DubboExporter exporter = new DubboExporter (invoker, key, exporterMap) exporterMap.put(key, exporter) //export an stub service for dispaching event Boolean isStubSupportEvent = url.getParameter(Constants.STUB_EVENT_KEY,Constants.DEFAULT_STUB_EVENT) Boolean isCallbackservice = url.getParameter(Constants.IS_CALLBACK_SERVICE, false) if (isStubSupportEvent && !isCallbackservice){ String stubServiceMethods = url.getParameter(Constants.STUB_EVENT_METHODS_KEY) if (stubServiceMethods == null || stubServiceMethods.length() == 0 ){ if (logger.isWarnEnabled()){ logger.warn(new IllegalStateException("consumer [" +url.getParameter(Constants.INTERFACE_KEY) + "], has set stubproxy support event ,but no stub methods founded.")) } } else { stubServiceMethodsMap.put(url.getServiceKey(), stubServiceMethods) } } openServer(url) return exporter }
创建一个DubboExporter,封装invoker。然后根据url的port、path(接口的名称)、版本号、分组号作为key,将DubboExporter存至Map
private void openServer(URL url) { String key = url.getAddress(); boolean isServer = url.getParameter(Constants.IS_SERVER_KEY,true); if (isServer) { ExchangeServer server = serverMap.get(key); if (server == null) { serverMap.put(key, createServer(url)); } else { server.reset(url); } } }
首先根据Invoker的url获取ExchangeServer通信对象(负责与客户端的通信模块),以url中的host和port作为key存至Map
private ExchangeHandler requestHandler = new ExchangeHandlerAdapter() { public Object reply(ExchangeChannel channel, Object message) throws RemotingException { if (message instanceof Invocation) { Invocation inv = (Invocation) message; Invoker> invoker = getInvoker(channel, inv); RpcContext.getContext().setRemoteAddress(channel.getRemoteAddress()); return invoker.invoke(inv); } throw new RemotingException(channel, "Unsupported request: " + message == null ? null : (message.getClass().getName() + ": " + message) + ", channel: consumer: " + channel.getRemoteAddress() + " --> provider: " + channel.getLocalAddress()); }};
可以看到在获取到Invocation参数后,调用getInvoker(channel, inv)来获取本地Invoker。获取过程就是根据channel获取port,根据Invocation inv信息获取要调用的服务接口、版本号、分组号等,以此组装成key,从上述Map
public interface Exporter{ Invoker getInvoker(); void unexport();}
包含了一个Invoker对象。一旦想撤销该服务,就会调用Invoker的destroy()方法,同时清理上述exporterMap中的数据。对于RegistryProtocol来说就需要向注册中心撤销该服务
DubboCodec
这个类是上一篇遗留的,当初涉及到编解码问题,接下来就来解读dubbo传输的底层协议组成以及它的编码解码过程。
1,传输协议
协议格式
2,header
3,bodydata
是消息传递的真正内容,body的占用的字节大小由协议头后四位保存。
4,序列化Request和Response
@Override protected void encodeRequestData(Channel channel, ObjectOutput out, Object data) throws IOException { RpcInvocation inv = (RpcInvocation) data out.writeUTF(inv.getAttachment(Constants.DUBBO_VERSION_KEY, DUBBO_VERSION)) out.writeUTF(inv.getAttachment(Constants.PATH_KEY)) out.writeUTF(inv.getAttachment(Constants.VERSION_KEY)) out.writeUTF(inv.getMethodName()) out.writeUTF(ReflectUtils.getDesc(inv.getParameterTypes())) Object[] args = inv.getArguments() if (args != null) for (int i = 0 out.writeObject(encodeInvocationArgument(channel, inv, i)) } out.writeObject(inv.getAttachments()) }
5,编解码流程
(1)编码
(2)解码