rpc模块是远程调用模块,抽象各种协议,以及动态代理,只包含一对一的调用,不关心集群的管理。这个模块的学习将使我们对服务的发布和调用更加清晰。
Invoker
这是一个可执行的对象,能够根据方法,参数得到执行结果,代码如下:
public interface Invoker<T> extends Node {
/**
* get service interface.
*
* @return service interface.
*/
Class<T> getInterface();
/**
* invoke.
*
* @param invocation
* @return result
* @throws RpcException
*/
Result invoke(Invocation invocation) throws RpcException;
}
里面的Invocation包含了要执行的方法和参数
public interface Invocation {
/**
* get method name.
*
* @return method name.
* @serial
*/
String getMethodName();
/**
* get parameter types.
*
* @return parameter types.
* @serial
*/
Class<?>[] getParameterTypes();
/**
* get arguments.
*
* @return arguments.
* @serial
*/
Object[] getArguments();
/**
* get attachments.
*
* @return attachments.
* @serial
*/
Map<String, String> getAttachments();
/**
* get attachment by key.
*
* @return attachment value.
* @serial
*/
String getAttachment(String key);
/**
* get attachment by key with default value.
*
* @return attachment value.
* @serial
*/
String getAttachment(String key, String defaultValue);
/**
* get the invoker in current context.
*
* @return invoker.
* @transient
*/
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 Map<String, String> attachments;
private transient Invoker<?> invoker;
}
其实也只是提供了Invocation所需的参数而已。我们把目光再放回Invoker。
Invoker的执行过程分为三种类型
(1)本地执行类的Invoker
(2)远程通信执行类的Invoker
(3)多个(2)的Invoker聚合成的集群版的Invoker(需要设计到负载均衡)
AbstractClusterInvoker:客户端使用的集群Invoker,还是依靠下面AbstractInvoker来完成具体的Invoker任务
AbstractInvoker:客户端使用的Invoker,是需要进行远程通信的
AbstractProxyInvoker:服务端使用的Invoker,仅仅需要反射执行相应的方法即可
ProxyFactory
对于server端,主要负责将服务统一进行包装成一个Invoker,这些Invoker通过反射来执行具体的对象的方法。
@SPI("javassist")
public interface ProxyFactory {
/**
* create proxy.
*
* @param invoker
* @return proxy
*/
// 针对client端,创建出代理对象
@Adaptive({Constants.PROXY_KEY})
<T> T getProxy(Invoker<T> invoker) throws RpcException;
/**
* create invoker.
*
* @param <T>
* @param proxy
* @param type
* @param url
* @return invoker
*/
// 针对server端,将服务对象包装成一个Invoker对象
@Adaptive({Constants.PROXY_KEY})
<T> Invoker<T> getInvoker(T proxy, Class<T> type, URL url) throws RpcException;
}
Protocol
我们发布服务的第一个过程是将服务封装成一个本地执行的invoker,执行服务就是执行这个invoker(调用invoker的invoke方法),通过反射执行。但是我们看到invoke方法的参数invocation,这个参数是如何得来的?
服务端的Protocol需要根据指定的协议对外公布服务,当客户端根据协议调用这个服务时候,将用户传递的invocation参数交给invoker来执行,所以Protocol加入了远程通信协议这一块,根据用户的请求获取参数Invocation。
@SPI("dubbo")
public interface Protocol {
/**
* 获取缺省端口,当用户没有配置端口时使用。
*
* @return 缺省端口
*/
int getDefaultPort();
/**
* 暴露远程服务:<br>
* 1. 协议在接收请求时,应记录请求来源方地址信息:RpcContext.getContext().setRemoteAddress();<br>
* 2. export()必须是幂等的,也就是暴露同一个URL的Invoker两次,和暴露一次没有区别。<br>
* 3. export()传入的Invoker由框架实现并传入,协议不需要关心。<br>
*
* @param <T> 服务的类型
* @param invoker 服务的执行体
* @return exporter 暴露服务的引用,用于取消暴露
* @throws RpcException 当暴露服务出错时抛出,比如端口已占用
*/
// 针对server端来说,将本地执行类的Invoker通过协议暴漏给外部。这样外部就可以通过协议发送执行参数Invocation,然后交给本地Invoker来执行
@Adaptive
<T> Exporter<T> export(Invoker<T> invoker) throws RpcException;
/**
* 引用远程服务:<br>
* 1. 当用户调用refer()所返回的Invoker对象的invoke()方法时,协议需相应执行同URL远端export()传入的Invoker对象的invoke()方法。<br>
* 2. refer()返回的Invoker由协议实现,协议通常需要在此Invoker中发送远程请求。<br>
* 3. 当url中有设置check=false时,连接失败不能抛出异常,并内部自动恢复。<br>
*
* @param <T> 服务的类型
* @param type 服务的类型
* @param url 远程服务的URL地址
* @return invoker 服务的本地代理
* @throws RpcException 当连接服务提供方失败时抛出
*/
// 这个是针对客户端的,客户端从注册中心获取服务器端发布的服务信息
// 通过服务信息得知服务器端使用的协议,然后客户端仍然使用该协议构造一个Invoker。这个Invoker是远程通信类的Invoker。
// 执行时,需要将执行信息通过指定协议发送给服务器端,服务器端接收到参数Invocation,然后交给服务器端的本地Invoker来执行
@Adaptive
<T> Invoker<T> refer(Class<T> type, URL url) throws RpcException;
/**
* 释放协议:<br>
* 1. 取消该协议所有已经暴露和引用的服务。<br>
* 2. 释放协议所占用的所有资源,比如连接和端口。<br>
* 3. 协议在释放后,依然能暴露和引用新的服务。<br>
*/
void destroy();
}
服务发布的第二步是暴露invoker。
Invoker<?> invoker = proxyFactory.getInvoker(ref, (Class) interfaceClass,
registryURL.addParameterAndEncoded(Constants.EXPORT_KEY, url.toFullString()));
DelegateProviderMetaDataInvoker wrapperInvoker = new DelegateProviderMetaDataInvoker(invoker, this);
Exporter<?> exporter = protocol.export(wrapperInvoker);
暴露过程即根据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 Exporter<T> export(final Invoker<T> invoker) {
//... 一些操作
impl .export(invoker);
// ... 一些操作
}
// ...
}
当服务发布时候会先经过RegistryProtocol,这个类的主要功能如下:
1,利用内部的Protocol即DubboProtocol,将服务进行导出,
2,根据注册中心的registryUrl获取注册服务Registry,然后将serviceUrl注册到注册中心上,供客户端订阅
接下来就需要看到DubboProtocol类了,注重看它的服务导出功能。
public <T> Exporter<T> export(Invoker<T> invoker) throws RpcException {
URL url = invoker.getUrl();
// export service.
String key = serviceKey(url);
DubboExporter<T> exporter = new DubboExporter<T>(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) {
// find server.
String key = url.getAddress();
//client 也可以暴露一个只有server可以调用的服务。
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,配合override功能使用
server.reset(url);
}
}
}
首先根据Invoker的url获取ExchangeServer通信对象(负责与客户端的通信模块),以url中的host和port作为key存至Map,没有则创建server
private ExchangeServer createServer(URL url) {
//默认开启server关闭时发送readonly事件
url = url.addParameterIfAbsent(Constants.CHANNEL_READONLYEVENT_SENT_KEY, Boolean.TRUE.toString());
//默认开启heartbeat
url = url.addParameterIfAbsent(Constants.HEARTBEAT_KEY, String.valueOf(Constants.DEFAULT_HEARTBEAT));
String str = url.getParameter(Constants.SERVER_KEY, Constants.DEFAULT_REMOTING_SERVER);
if (str != null && str.length() > 0 && !ExtensionLoader.getExtensionLoader(Transporter.class).hasExtension(str))
throw new RpcException("Unsupported server type: " + str + ", url: " + url);
url = url.addParameter(Constants.CODEC_KEY, DubboCodec.NAME);
ExchangeServer server;
try {
server = Exchangers.bind(url, requestHandler);
} catch (RemotingException e) {
throw new RpcException("Fail to start server(url: " + url + ") " + e.getMessage(), e);
}
str = url.getParameter(Constants.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 server;
}
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);
//如果是callback 需要处理高版本调用低版本的问题
if (Boolean.TRUE.toString().equals(inv.getAttachments().get(IS_CALLBACK_SERVICE_INVOKE))) {
String methodsStr = invoker.getUrl().getParameters().get("methods");
boolean hasMethod = false;
if (methodsStr == null || methodsStr.indexOf(",") == -1) {
hasMethod = inv.getMethodName().equals(methodsStr);
} else {
String[] methods = methodsStr.split(",");
for (String method : methods) {
if (inv.getMethodName().equals(method)) {
hasMethod = true;
break;
}
}
}
if (!hasMethod) {
logger.warn(new IllegalStateException("The methodName " + inv.getMethodName() + " not found in callback service interface ,invoke will be ignored. please update the api interface. url is:" + invoker.getUrl()) + " ,invocation is :" + inv);
return null;
}
}
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());
}
@Override
public void received(Channel channel, Object message) throws RemotingException {
if (message instanceof Invocation) {
reply((ExchangeChannel) channel, message);
} else {
super.received(channel, message);
}
}
@Override
public void connected(Channel channel) throws RemotingException {
invoke(channel, Constants.ON_CONNECT_KEY);
}
@Override
public void disconnected(Channel channel) throws RemotingException {
if (logger.isInfoEnabled()) {
logger.info("disconected from " + channel.getRemoteAddress() + ",url:" + channel.getUrl());
}
invoke(channel, Constants.ON_DISCONNECT_KEY);
}
private void invoke(Channel channel, String methodKey) {
Invocation invocation = createInvocation(channel, channel.getUrl(), methodKey);
if (invocation != null) {
try {
received(channel, invocation);
} catch (Throwable t) {
logger.warn("Failed to invoke event method " + invocation.getMethodName() + "(), cause: " + t.getMessage(), t);
}
}
}
private Invocation createInvocation(Channel channel, URL url, String methodKey) {
String method = url.getParameter(methodKey);
if (method == null || method.length() == 0) {
return null;
}
RpcInvocation invocation = new RpcInvocation(method, new Class<?>[0], new Object[0]);
invocation.setAttachment(Constants.PATH_KEY, url.getPath());
invocation.setAttachment(Constants.GROUP_KEY, url.getParameter(Constants.GROUP_KEY));
invocation.setAttachment(Constants.INTERFACE_KEY, url.getParameter(Constants.INTERFACE_KEY));
invocation.setAttachment(Constants.VERSION_KEY, url.getParameter(Constants.VERSION_KEY));
if (url.getParameter(Constants.STUB_EVENT_KEY, false)) {
invocation.setAttachment(Constants.STUB_EVENT_KEY, Boolean.TRUE.toString());
}
return invocation;
}
};
Invoker<?> getInvoker(Channel channel, Invocation inv) throws RemotingException {
boolean isCallBackServiceInvoke = false;
boolean isStubServiceInvoke = false;
int port = channel.getLocalAddress().getPort();
String path = inv.getAttachments().get(Constants.PATH_KEY);
//如果是客户端的回调服务.
isStubServiceInvoke = Boolean.TRUE.toString().equals(inv.getAttachments().get(Constants.STUB_EVENT_KEY));
if (isStubServiceInvoke) {
port = channel.getRemoteAddress().getPort();
}
//callback
isCallBackServiceInvoke = isClientSide(channel) && !isStubServiceInvoke;
if (isCallBackServiceInvoke) {
path = inv.getAttachments().get(Constants.PATH_KEY) + "." + inv.getAttachments().get(Constants.CALLBACK_SERVICE_KEY);
inv.getAttachments().put(IS_CALLBACK_SERVICE_INVOKE, Boolean.TRUE.toString());
}
String serviceKey = serviceKey(port, path, inv.getAttachments().get(Constants.VERSION_KEY), inv.getAttachments().get(Constants.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:" + inv);
return exporter.getInvoker();
}
可以看到在获取到Invocation参数后,调用getInvoker(channel, inv)来获取本地Invoker。获取过程就是根据channel获取port,根据Invocation inv信息获取要调用的服务接口、版本号、分组号等,以此组装成key,从上述Map中获取DubboExporter,然后再返回该exporter的invoker
public interface Exporter<T> {
/**
* get invoker.
*
* @return invoker
*/
Invoker<T> getInvoker();
/**
* unexport.
* <p>
* <code>
* getInvoker().destroy();
* </code>
*/
void unexport();
}
export包含了一个Invoker对象。一旦想撤销该服务,就会调用Invoker的destroy()方法,同时清理上述exporterMap中的数据。对于RegistryProtocol来说就需要向注册中心撤销该服务