接上一篇的内容,继续Dubbo服务提供端模块的分解,先来回顾下RPC的图:
Consumer调用走到网络部分就结束了,下面来分解下图右半部分Dubbo的实现。
Dubbo RPC 服务端组件
还是按照图的顺序自左至右,一个请求跨越了网络,数据走到Provider这一侧。首先自然是反序列化和协议解析,这个跟Consumer端正好是反的,但是代码是一起的,分别是Serialization中的deserialize()方法和Codec2中的decode()方法。
请求传输
Provider要提供服务,必须在某一端口上监听来接收数据,所以跟Client端对应要有个Server。
Server
为了支持多协议,Dubbo将Server抽象为ExchangeServer接口:
public interface ExchangeServer extends RemotingServer { /** * 获取所有和client的Channel */ Collection<ExchangeChannel> getExchangeChannels(); /** * 获取指定Client的Channel */ ExchangeChannel getExchangeChannel(InetSocketAddress remoteAddress); }
public interface ExchangeChannel extends Channel { /** * 发送请求 */ CompletableFuture<Object> request(Object request, int timeout, ExecutorService executor) throws RemotingException; /** * 接收数据的handler */ ExchangeHandler getExchangeHandler(); /** * 关闭Channel */ @Override void close(int timeout); }
跟上一篇的ExchangeClient一样,ExchangeServer自然也是从Exchanger接口里来的。
@SPI(HeaderExchanger.NAME) public interface Exchanger { /** * 开启一个服务端Server */ @Adaptive({Constants.EXCHANGER_KEY}) ExchangeServer bind(URL url, ExchangeHandler handler) throws RemotingException; /** * 获取Client */ @Adaptive({Constants.EXCHANGER_KEY}) ExchangeClient connect(URL url, ExchangeHandler handler) throws RemotingException; }
当然底层还是依赖的Transporter,这里就不再重复说了。Handler 回到数据流上,请求到达后,经过协议解析和对象的反序列化,以Request对象的形式到达ExchangeHandler, 现在看下该接口:
public interface ExchangeHandler extends ChannelHandler, TelnetHandler { /** * reply. */ CompletableFuture<Object> reply(ExchangeChannel channel, Object request) throws RemotingException; }
从接口可以看到,在收到请求后,handler负责回复Response,就是说handler负责调用接口的实现类,然后将结果通过网络返回。
服务暴露
请求到达服务端后,需要调用本地的接口实现类。还记得上一篇中讲到的Invoker吗?它是对调用的封装接口,在服务提供方,同样是用的它,只是这次执行invoke()方法的时候是调用的本地实现类。
Exporter
Invoker调用本地方法,首要的自然是找到本地实现类在什么地方,这时候就需要引入另外一个接口Exporter。
public interface Exporter<T> { /** * 获取Invoker */ Invoker<T> getInvoker(); /** * 取消接口暴露 */ void unexport(); }
Exporter代表本地接口服务的暴露。可以这么理解,本地有个接口的实现,我想让其它服务能够远程调用到,那就需要按照一定的协议注册成一个Exporter,否则即使远程发送请求过来要求访问该实现,Dubbo也是不允许的。 可以看到,通过Exporter是可以拿到Invoker的,对这个invoker的执行调用的就是本地接口实现。Dubbo中一个接口实现可以以多种协议暴露,调用的Protocol接口的export方法,默认自然是DubboProtocol中的DubboExporter。
@SPI("dubbo") public interface Protocol { //暴露服务接口 @Adaptive <T> Exporter<T> export(Invoker<T> invoker) throws RpcException; //获取接口调用的Invoker @Adaptive <T> Invoker<T> refer(Class<T> type, URL url) throws RpcException; }
看完上面几个接口,我们把关系捋一下。在服务启动的后,根据服务的配置,Dubbo会将一个接口的实现在指定的协议上暴露成一个Exporter,Exporter中封装了一个本地调用的Invoker。 ExchangeHandler收到请求后,从请求中拿到协议,接口名、方法名以及反序列化后的参数,所有这些基本上都封装在一个RpcInvocation中。Handler到指定协议中找接口的Exporter,找到后就可以获取到Invoker,invoker执行时会调用本地实现类的方法。
本地调用
上面讲到发起调用本地方法的Invoker,Dubbo也有两种实现方式,一种是用的java的反射调用,一种是用javaassist生成一个Invoker的实现类,在生成的类中直接引用接口实现。显然后一种性能更好,Dubbo默认也是用的后一种方式。 获取Invoker的方法也是在ProxyFactory接口中。
@SPI("javassist") public interface ProxyFactory { /** * 创建远程接口的代理,Consumer用 */ @Adaptive({PROXY_KEY}) <T> T getProxy(Invoker<T> invoker) throws RpcException; /** * 创建本地调用Invoker的代理,Provider用 */ @Adaptive({PROXY_KEY}) <T> Invoker<T> getInvoker(T proxy, Class<T> type, URL url) throws RpcException; }
Provider端总结
一图胜千言,对应RPC的图画下Dubbo Provider这一端的模块关系。