三Dubbo服务暴露源码分析--3远程暴露-上
三Dubbo服务暴露源码分析--3远程暴露-上
3.2 远程暴露
在Dubbo开发者手册中,给出了服务调用的流程图如上。其中,圈住部分为provider端的远程服务暴露部分的主流程。后续的分析,可以对照流程图,把握执行方向。
ServiceConfig
private void doExportUrlsFor1Protocol(ProtocolConfig protocolConfig, List<URL> registryURLs) {
/**……………………………………………………………………………………………………根据scope暴露服务(本地或远程)……………………………………………………………………………………………………………………… */
//TAG3.1.2.2. 暴露服务--scope
//scope为null,需要暴露本地和远程服务(none:不暴露;remote:只远程;local:本地;null:本地+远程)
String scope = url.getParameter(Constants.SCOPE_KEY);
//配置为none不暴露(scope为none,不暴露服务;为null,或者其他,此处暴露)
if (! Constants.SCOPE_NONE.toString().equalsIgnoreCase(scope)) { //1
//配置不是remote的情况下做本地暴露 (配置为remote,则表示只暴露远程服务)
if (!Constants.SCOPE_REMOTE.toString().equalsIgnoreCase(scope)) { //2
//TAG1 本地暴露
exportLocal(url);
}//2
/**……………………………………………………………………………远程暴露………………………………………………………………………………………………… */
//如果配置不是local则暴露为远程服务.(配置为local,则表示只暴露远程服务)
if (! Constants.SCOPE_LOCAL.toString().equalsIgnoreCase(scope) ){ //2
if (logger.isInfoEnabled()) {
logger.info("Export dubbo service " + interfaceClass.getName() + " to url " + url);
}
//registry://localhost:2181/com.alibaba.dubbo.registry.RegistryService?application=demotest-provider&dubbo=2.5.3&organization=dubbox&owner=programmer&pid=25677®istry=zookeeper×tamp=1671174564785
//url为 dubbo://192.168.0.101:20880/com.alibaba.dubbo.demo.DemoService?anyhost=true&application=demotest-provider&dubbo=2.5.3&interface=com.alibaba.dubbo.demo.DemoService&methods=getPermissions&organization=dubbox&owner=programmer&pid=25677&side=provider×tamp=1671174564948
if (registryURLs != null && registryURLs.size() > 0
&& url.getParameter("register", true)) { //3
for (URL registryURL : registryURLs) { //4
//从registryURL中获取参数,加入url中
url = url.addParameterIfAbsent("dynamic", registryURL.getParameter("dynamic"));
URL monitorUrl = loadMonitor(registryURL);
if (monitorUrl != null) {
url = url.addParameterAndEncoded(Constants.MONITOR_KEY, monitorUrl.toFullString());
}
if (logger.isInfoEnabled()) {
logger.info("Register dubbo service " + interfaceClass.getName() + " url " + url + " to registry " + registryURL);
}
/** ……………………………………………………………………………………………………………………远程暴露……………………………………………………………………………………………………………………………………………………*/
//TAG1 registryURL.addParameterAndEncoded
//TAG2 proxyFactory@Adpative.getInvoker(ref)--ref转化为invoker
Invoker<?> invoker = proxyFactory.getInvoker(ref, (Class) interfaceClass, registryURL.addParameterAndEncoded(Constants.EXPORT_KEY, url.toFullString()));
//TAG3 protocol.export(invoker)--invoker转换为exporter
Exporter<?> exporter = protocol.export(invoker);
//将暴露出的服务,保存在serviceConfig的本地缓存lis<exporters>上
exporters.add(exporter);
}//4
} //3
else {
Invoker<?> invoker = proxyFactory.getInvoker(ref, (Class) interfaceClass, url);
Exporter<?> exporter = protocol.export(invoker);
exporters.add(exporter);
}
}//2
}//1
this.urls.add(url);
}
前面,已经执行完本地暴露。
TAG1 registryURL.addParameterAndEncoded
registryURL.addParameterAndEncoded(Constants.EXPORT_KEY, url.toFullString());
TAG1/2主要会是向registry中注册暴露服务,因此,这里,把provider的服务" dubbo://192.168.0.101:20880/com.alibaba.dubbo.demo.DemoService?anyhost=true&application=demotest-provider&dubbo=2.5.3&interface=com.alibaba.dubbo.demo.DemoService&methods=getPermissions&organization=dubbox&owner=programmer&pid=25677&side=provider×tamp=1671174564948",编码后加入registryURL的参数对parameters中,如下图所示。
TAG2 proxyFactory@Adpative.getInvoker(ref)--ref转化为invoker
这里构造的过程,和本地暴露部分相同,详见3.1 本地暴露-TAG2部分。
在JavassistProxyFactory中构造abstractProxyInvoker匿名类,如下:
此时invoker内URL为registry://localhost:2181,表示这时候的invoker需要去注册中心进行服务注册。
TAG3 Protocol$Adpative.export---invoker转换为exporter
public class Protocol$Adpative implements com.alibaba.dubbo.rpc.Protocol {
public com.alibaba.dubbo.rpc.Exporter export(com.alibaba.dubbo.rpc.Invoker arg0) throws RpcException {
if (arg0 == null) throw new IllegalArgumentException("com.alibaba.dubbo.rpc.Invoker argument == null");
if (arg0.getUrl() == null)
throw new IllegalArgumentException("com.alibaba.dubbo.rpc.Invoker argument getUrl() == null");
//registry://localhost:2181/com.alibaba.dubbo.registry.RegistryService?application=demotest-provider&dubbo=2.5.3&export=dubbo%3A%2F%2F192.168.0.101%3A20880%2Fcom.alibaba.dubbo.demo.DemoService%3Fanyhost%3Dtrue%26application%3Ddemotest-provider%26dubbo%3D2.5.3%26interface%3Dcom.alibaba.dubbo.demo.DemoService%26methods%3DgetPermissions%26organization%3Ddubbox%26owner%3Dprogrammer%26pid%3D59337%26side%3Dprovider%26timestamp%3D1671361634513&organization=dubbox&owner=programmer&pid=59337®istry=zookeeper×tamp=1671361634364
com.alibaba.dubbo.common.URL url = arg0.getUrl();
//获取此时当前invoker.url的协议头为registry---表示这个invoker需要去注册中心执行注册
String extName = (url.getProtocol() == null ? "dubbo" : url.getProtocol());
if (extName == null)
throw new IllegalStateException("Fail to get extension(com.alibaba.dubbo.rpc.Protocol) name from url(" + url.toString() + ") use keys([protocol])");
//TAG3.1 protocol自适应
com.alibaba.dubbo.rpc.Protocol extension = (com.alibaba.dubbo.rpc.Protocol) ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.rpc.Protocol.class).getExtension(extName);
//TAG3.2 protocol.export(listener->filter->registry)
return extension.export(arg0);
}
TAG3.1 protocol自适应
TAG3.2 protocol.export(listener->filter->registry)
此时,获取Protocol.class的拓展类,是个包装类,由外到内分别是listener->filter->registry。
EXP1 ProtocolListenerWrapper.export--注册服务
ProtocolListenerWrapper
public <T> Exporter<T> export(Invoker<T> invoker) throws RpcException {
//如果invoker.url的协议为registry,表示当前invoker服务需要注册到registry注册中心,因此不会真正暴露,而是直接执行
if (Constants.REGISTRY_PROTOCOL.equals(invoker.getUrl().getProtocol())) {
return protocol.export(invoker);
}
/**………………………………………………………………如果暴露的服务为 */
return new ListenerExporterWrapper<T>(protocol.export(invoker),
Collections.unmodifiableList(ExtensionLoader.getExtensionLoader(ExporterListener.class)
.getActivateExtension(invoker.getUrl(), Constants.EXPORTER_LISTENER_KEY)));
}
EXP2 ProtocolFilterWrapper.export--
ProtocolFilterWrapper
public <T> Exporter<T> export(Invoker<T> invoker) throws RpcException {
//如果invoker.url的protocol协议为"registry",则表示是注册服务
if (Constants.REGISTRY_PROTOCOL.equals(invoker.getUrl().getProtocol())) {
//直接执行registryProtocol.export
return protocol.export(invoker);
}
return protocol.export(buildInvokerChain(invoker, Constants.SERVICE_FILTER_KEY, Constants.PROVIDER));
}
EXP3 RegistryProtocol.export
RegistryProtocol类的初始化,在TAG3.1过程,(com.alibaba.dubbo.rpc.Protocol) ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.rpc.Protocol.class).getExtension(extName);的方法中,通过extensionLoader的构造和依赖注入功能,实例化并传入其属性完成初始化。
参数:
originInvoker:
registry://localhost:2181/com.alibaba.dubbo.registry.RegistryService?application=demotest-provider&dubbo=2.5.3&export=dubbo%3A%2F%2F192.168.0.101%3A20880%2Fcom.alibaba.dubbo.demo.DemoService%3Fanyhost%3Dtrue%26application%3Ddemotest-provider%26dubbo%3D2.5.3%26interface%3Dcom.alibaba.dubbo.demo.DemoService%26methods%3DgetPermissions%26organization%3Ddubbox%26owner%3Dprogrammer%26pid%3D59337%26side%3Dprovider%26timestamp%3D1671361634513&organization=dubbox&owner=programmer&pid=59337®istry=zookeeper×tamp=1671361634364
RegistryProtocol
public <T> Exporter<T> export(final Invoker<T> originInvoker) throws RpcException {
//EXP3.1 doLocalExport
//对服务dubbo://的暴露,并打开端口等操作
final ExporterChangeableWrapper<T> exporter = doLocalExport(originInvoker);
//EXP3.2 getRegistry
//根据originInvoker的注册中心zookeeper://,加载registryService的实现类--zookeeperRegistry
final Registry registry = getRegistry(originInvoker);
//获取注册的服务提供者的URL,即dubbo://
final URL registedProviderUrl = getRegistedProviderUrl(originInvoker);
//EXP3.3 registry.register
//将provider信息,注册到注册中心
registry.register(registedProviderUrl);
/**…………………………………………………………………………………………………………override信息、overrideListener的订阅……………………………………………………………………………… */
// 订阅override数据
// FIXME 提供者订阅时,会影响同一JVM即暴露服务,又引用同一服务的的场景,因为subscribed以服务名为缓存的key,导致订阅信息覆盖。
//获取订阅的override数据
final URL overrideSubscribeUrl = getSubscribedOverrideUrl(registedProviderUrl);
//创建override监听器
final OverrideListener overrideSubscribeListener = new OverrideListener(overrideSubscribeUrl);
overrideListeners.put(overrideSubscribeUrl, overrideSubscribeListener);
//向注册中心订阅override
registry.subscribe(overrideSubscribeUrl, overrideSubscribeListener);
//保证每次export都返回一个新的exporter实例(其包裹的exporter是EXP3.1doLocalExport中暴露的服务exporter)
return new Exporter<T>() {
public Invoker<T> getInvoker() {
return exporter.getInvoker();
}
public void unexport() {
try {
exporter.unexport();
} catch (Throwable t) {
logger.warn(t.getMessage(), t);
}
try {
registry.unregister(registedProviderUrl);
} catch (Throwable t) {
logger.warn(t.getMessage(), t);
}
try {
overrideListeners.remove(overrideSubscribeUrl);
registry.unsubscribe(overrideSubscribeUrl, overrideSubscribeListener);
} catch (Throwable t) {
logger.warn(t.getMessage(), t);
}
}
};
}
/** …………暴露远程服务、打开端口……………*/
EXP3.1 doLocalExport--实际暴露远程服务、打开端口
originInvoker:
registry://localhost:2181/com.alibaba.dubbo.registry.RegistryService?application=demotest-provider&dubbo=2.5.3&export=dubbo%3A%2F%2F192.168.0.101%3A20880%2Fcom.alibaba.dubbo.demo.DemoService%3Fanyhost%3Dtrue%26application%3Ddemotest-provider%26dubbo%3D2.5.3%26interface%3Dcom.alibaba.dubbo.demo.DemoService%26methods%3DgetPermissions%26organization%3Ddubbox%26owner%3Dprogrammer%26pid%3D59337%26side%3Dprovider%26timestamp%3D1671361634513&organization=dubbox&owner=programmer&pid=59337®istry=zookeeper×tamp=1671361634364
RegistryProtocol
//本地缓存,记录providerURL暴露的exporter服务--避免暴露过的服务,不再重新暴露
private final Map<String, ExporterChangeableWrapper<?>> bounds = new ConcurrentHashMap<String, ExporterChangeableWrapper<?>>();
private <T> ExporterChangeableWrapper<T> doLocalExport(final Invoker<T> originInvoker){
//EXP3.1.1 getCacheKey(获取invoker在bounds中缓存的key)
String key = getCacheKey(originInvoker);
//然后获取bounds内缓存的exporter---避免重复暴露
ExporterChangeableWrapper<T> exporter = (ExporterChangeableWrapper<T>) bounds.get(key);
/**………………………………………………………………RegistryProtocol中bounds,记录是否暴露过服务………………………………………………………………………………………… */
if (exporter == null) {
synchronized (bounds) {//加锁
exporter = (ExporterChangeableWrapper<T>) bounds.get(key);
if (exporter == null) {
//EXP3.1.2 InvokerDelegete
final Invoker<?> invokerDelegete = new InvokerDelegete<T>(originInvoker, getProviderUrl(originInvoker));
//EXP3.1.3 protocol.export
//EXP3.1.4 ExporterChangeableWrapper
exporter = new ExporterChangeableWrapper<T>((Exporter<T>)protocol.export(invokerDelegete), originInvoker);
bounds.put(key, exporter);
}
}
}
return (ExporterChangeableWrapper<T>) exporter;
}
//EXP3.1.1 getCacheKey(获取invoker在bounds中缓存的key)
private String getCacheKey(final Invoker<?> originInvoker){
//获取provider服务的url
URL providerUrl = getProviderUrl(originInvoker);
//移除参数--"dynamic", "enabled"用于在注册过程中
String key = providerUrl.removeParameters("dynamic", "enabled").toFullString();
return key;
}
//通过invoker的url,获取provider的地址
private URL getProviderUrl(final Invoker<?> origininvoker){
//获取在registry://中添加入parameter的export:encode(providerURL),并解码
//dubbo://192.168.0.101:20880/com.alibaba.dubbo.demo.DemoService?anyhost=true&application=demotest-provider&dubbo=2.5.3&interface=com.alibaba.dubbo.demo.DemoService&methods=getPermissions&organization=dubbox&owner=programmer&pid=59337&side=provider×tamp=1671361634513
String export = origininvoker.getUrl().getParameterAndDecoded(Constants.EXPORT_KEY);
if (export == null || export.length() == 0) {
throw new IllegalArgumentException("The registry export url is null! registry: " + origininvoker.getUrl());
}
//dubbo://192.168.0.101:20880/com.alibaba.dubbo.demo.DemoService?anyhost=true&application=demotest-provider&dubbo=2.5.3&interface=com.alibaba.dubbo.demo.DemoService&methods=getPermissions&organization=dubbox&owner=programmer&pid=59337&side=provider×tamp=1671361634513
URL providerUrl = URL.valueOf(export); //将string转换为URL
return providerUrl;
}
originInvoker内的相关数据----当前为注册服务的invoker。
因此,getCacheKey,是获取originInvoker内参数providerURL(实际provider端的地址)。
//EXP3.1.2 InvokerDelegete->InvokerWrapper
//EXP3.1.2 InvokerDelegete
final Invoker<?> invokerDelegete = new InvokerDelegete<T>(originInvoker, getProviderUrl(originInvoker));
该过程,创建invoker与providerURL关联的委托类InvokerDelegete.
RegistryProtocol内部类
public static class InvokerDelegete<T> extends InvokerWrapper<T>{
//invokerdelegete委托类内保存真正的invoker
private final Invoker<T> invoker;
public InvokerDelegete(Invoker<T> invoker, URL url){
super(invoker, url);
this.invoker = invoker;
}
//getInvoker获取内部包裹的真正invoker
public Invoker<T> getInvoker(){
if (invoker instanceof InvokerDelegete){
return ((InvokerDelegete<T>)invoker).getInvoker();
} else {
return invoker;
}
}
}
public class InvokerWrapper<T> implements Invoker<T> {
private final Invoker<T> invoker;
private final URL url;
public InvokerWrapper(Invoker<T> invoker, URL url){
this.invoker = invoker;
this.url = url;
}
此时,构造成的invokerDelegete信息如下:
作用:
因为此时,首先进行EXP3.1 doLocalExport暴露服务,尚未进行服务注册,因此在暴露过程中,把服务注册的invoker(registry://)和服务提供者providerURL(dubbo://)进行关联保存。
//EXP3.1.3 protocol@Adpative.export(invokerDelegete-->exporter转换)
//EXP3.1.3 protocol.export
//EXP3.1.4 ExporterChangeableWrapper
exporter = new ExporterChangeableWrapper<T>((Exporter<T>)protocol.export(invokerDelegete), originInvoker);
继续跟进代码,执行invoker--》exporter操作
public class Protocol$Adpative implements com.alibaba.dubbo.rpc.Protocol {
public com.alibaba.dubbo.rpc.Exporter export(com.alibaba.dubbo.rpc.Invoker arg0) throws RpcException {
if (arg0 == null) throw new IllegalArgumentException("com.alibaba.dubbo.rpc.Invoker argument == null");
if (arg0.getUrl() == null)
throw new IllegalArgumentException("com.alibaba.dubbo.rpc.Invoker argument getUrl() == null");
//invokerDelegete的URL为providerURL
com.alibaba.dubbo.common.URL url = arg0.getUrl();
//providerurl协议为dubbo
String extName = (url.getProtocol() == null ? "dubbo" : url.getProtocol());
if (extName == null)
throw new IllegalStateException("Fail to get extension(com.alibaba.dubbo.rpc.Protocol) name from url(" + url.toString() + ") use keys([protocol])");
com.alibaba.dubbo.rpc.Protocol extension = (com.alibaba.dubbo.rpc.Protocol) ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.rpc.Protocol.class).getExtension(extName);
//PTC1 protocol.export(listener->filter->dubbo)
return extension.export(arg0);
}
这里,根据invokerDelegete内providerurl的protocol协议,获取Protocol.class的拓展类。根据wrapper机制,会依次包裹listener、filter的协议类。
这里的arg0,是invokerDelegete类。
PTC1 protocol.export(listener->filter->dubbo)--和本地暴露相同
这里protocol.export的暴露过程,和本地暴露基本相同,分别为暴露过程增加ExporterListener监听器、构建filter拦截器链。
ProtocolListenerWrapper
public <T> Exporter<T> export(Invoker<T> invoker) throws RpcException {
if (Constants.REGISTRY_PROTOCOL.equals(invoker.getUrl().getProtocol())) {
return protocol.export(invoker);
}
//invoker.getUrl()协议头尾dubbo://,进行实际的远程暴露过程
return new ListenerExporterWrapper<T>(protocol.export(invoker),
Collections.unmodifiableList(ExtensionLoader.getExtensionLoader(ExporterListener.class)
.getActivateExtension(invoker.getUrl(), Constants.EXPORTER_LISTENER_KEY)));
}
ProtocolFilterWrapper
public <T> Exporter<T> export(Invoker<T> invoker) throws RpcException {
if (Constants.REGISTRY_PROTOCOL.equals(invoker.getUrl().getProtocol())) {
return protocol.export(invoker);
}
//PTC2 dubboProtocol.export
//invoker.getUrl()协议头尾dubbo://,进行实际的远程暴露过程
return protocol.export(buildInvokerChain(invoker, Constants.SERVICE_FILTER_KEY, Constants.PROVIDER));
}
在ProtocolFilterWrapper执行中,由于协议头是dubbo,不是registry,所以首先构造filter拦截器链。
invoker链,为list
PTC2 dubboProtocol.export
DubboProtocol
public <T> Exporter<T> export(Invoker<T> invoker) throws RpcException {
//获取providerURL--dubbo://
URL url = invoker.getUrl();
//com.alibaba.dubbo.demo.DemoService:20880 暴露服务的path:port
String key = serviceKey(url);
//PTC2.1 DubboExporter
DubboExporter<T> exporter = new DubboExporter<T>(invoker, key, exporterMap);
//将dubbo://,DubboExporter的键值对存入map中
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);
}
}
//PTC2.2 openServer 打开服务端口
openServer(url);
return exporter;
}
PTC2.1 DubboExporter
public class DubboExporter<T> extends AbstractExporter<T> {
private final String key;
//以dubbo://为key,DubboExporter为value的map,做缓存
private final Map<String, Exporter<?>> exporterMap;
public DubboExporter(Invoker<T> invoker, String key, Map<String, Exporter<?>> exporterMap){
super(invoker);
this.key = key;
this.exporterMap = exporterMap;
}
存入map后,得到如下:
PTC2.2 openServer 打开服务端口
DubboProtocol
private final Map<String, ExchangeServer> serverMap = new ConcurrentHashMap<String, ExchangeServer>(); // <host:port,Exchanger>
private void openServer(URL url) {
//192.168.0.101:20880
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) {
//PTC2.2.1 createServer 创建ExchangeServer
//PTC2.2.2 DubboProtocol.serverMap.put(key, createServer(url))
serverMap.put(key, createServer(url));
} else {
//server支持reset,配合override功能使用
server.reset(url);
}
}
}
PTC2.2.1 createServer 创建ExchangeServer
DubboProtocol
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));
//获取远程服务端类型Netty
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, Version.isCompatibleVersion() ? COMPATIBLE_CODEC_NAME : DubboCodec.NAME);
ExchangeServer server;
try {
//PTC2.2.1.1 Exchangers.bind(url, requestHandler)-----返回HeaderExchangeServer
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;
}
PTC2.2.1.1 Exchangers.bind(url, requestHandler)-BIND
//PTC2.2.1.1 Exchangers.bind(url, requestHandler)
server = Exchangers.bind(url, requestHandler);
其中两个参数,
dubbo://192.168.0.101:20880/com.alibaba.dubbo.demo.DemoService?anyhost=true&application=demotest-provider&channel.readonly.sent=true&codec=dubbo&dubbo=2.5.3&heartbeat=60000&interface=com.alibaba.dubbo.demo.DemoService&methods=getPermissions&organization=dubbox&owner=programmer&pid=73930&side=provider×tamp=1671436924670
ExchangeHandler requestHandler,是服务端启动服务后,用于处理request请求的handler处理器。