接上一篇的内容,继续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);
}
ExchangeServer启动后,每个客户端连接对应着一个channel。Channel代表是两个端点之间的连接通道,所以是没有Client和Server之分的,接口自然也是同一个。发送数据用request方法,接收数据用ExchangeHandler。只是大部分情况下Server端不会主动发送request,而是在Handler接收到请求后,回复response。
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这一端的模块关系
Provider
用了两篇文章把RPC部分简单分解一下,目的是为了有个大体的轮廓,不至于读代码的时候被其中复杂的关系搞乱。这里的结构不需要记住,知道大概关系就好,后面到细节分解的时候可以随时回查。
网友评论