Dubbo 服务暴露有两种方式
本地暴露,JVM 本地调用。配置如下:
远程暴露,网络远程通信。配置如下:
在不配置 scope 的情况下,默认两种方式都暴露
本地暴露服务的顺序图如下:
ServiceConfig#export() 方法中,会在配置初始化完成后,调用顺序图的起点 #doExportUrls() 方法,开始暴露服务。代码如下:
/**
* 暴露 Dubbo URL
*/
@SuppressWarnings({"unchecked", "rawtypes"})
private void doExportUrls() {
// 加载注册中心 URL 数组
List registryURLs = loadRegistries(true);
// 循环 `protocols` ,向逐个注册中心分组暴露服务。
for (ProtocolConfig protocolConfig : protocols) {
doExportUrlsFor1Protocol(protocolConfig, registryURLs);
}
}
loadRegistries(provider) 方法,加载注册中心 com.alibaba.dubbo.common.URL 数组
/**
* 加载注册中心 URL 数组
*
* @param provider 是否是服务提供者
* @return URL 数组
*/
protected List loadRegistries(boolean provider) {
// 校验 RegistryConfig 配置数组。
checkRegistry();
// 创建 注册中心 URL 数组
List registryList = new ArrayList();
if (registries != null && !registries.isEmpty()) {
for (RegistryConfig config : registries) {
// 获得注册中心的地址
String address = config.getAddress();
if (address == null || address.length() == 0) {
address = Constants.ANYHOST_VALUE;
}
String sysaddress = System.getProperty("dubbo.registry.address"); // 从启动参数读取
if (sysaddress != null && sysaddress.length() > 0) {
address = sysaddress;
}
// 有效的地址
if (address.length() > 0
&& !RegistryConfig.NO_AVAILABLE.equalsIgnoreCase(address)) {
Map map = new HashMap();
// 将各种配置对象,添加到 `map` 集合中。
appendParameters(map, application);
appendParameters(map, config);
// 添加 `path` `dubbo` `timestamp` `pid` 到 `map` 集合中。
map.put("path", RegistryService.class.getName());
map.put("dubbo", Version.getVersion());
map.put(Constants.TIMESTAMP_KEY, String.valueOf(System.currentTimeMillis()));
if (ConfigUtils.getPid() > 0) {
map.put(Constants.PID_KEY, String.valueOf(ConfigUtils.getPid()));
}
// 若不存在 `protocol` 参数,默认 "dubbo" 添加到 `map` 集合中。
if (!map.containsKey("protocol")) {
if (ExtensionLoader.getExtensionLoader(RegistryFactory.class).hasExtension("remote")) { // "remote"
map.put("protocol", "remote");
} else {
map.put("protocol", "dubbo");
}
}
// 解析地址,创建 Dubbo URL 数组。(数组大小可以为一)
List urls = UrlUtils.parseURLs(address, map);
// 循环 `url` ,设置 "registry" 和 "protocol" 属性。
for (URL url : urls) {
// 设置 `registry=${protocol}` 和 `protocol=registry` 到 URL
url = url.addParameter(Constants.REGISTRY_KEY, url.getProtocol());
url = url.setProtocol(Constants.REGISTRY_PROTOCOL);
// 添加到结果
if ((provider && url.getParameter(Constants.REGISTER_KEY, true)) // 服务提供者 && 注册 https://dubbo.gitbooks.io/dubbo-user-book/demos/subscribe-only.html
|| (!provider && url.getParameter(Constants.SUBSCRIBE_KEY, true))) { // 服务消费者 && 订阅 https://dubbo.gitbooks.io/dubbo-user-book/demos/registry-only.html
registryList.add(url);
}
}
}
}
}
return registryList;
}
#doExportUrlsFor1Protocol(protocolConfig, registryURLs) 方法,基于单个协议,暴露服务。简化代码如下:
/**
* 基于单个协议,暴露服务
*
* @param protocolConfig 协议配置对象
* @param registryURLs 注册中心链接对象数组
*/
private void doExportUrlsFor1Protocol(ProtocolConfig protocolConfig, List registryURLs) {
// ... 【省略】 创建服务 URL 对象
String scope = url.getParameter(Constants.SCOPE_KEY);
// don't export when none is configured
if (!Constants.SCOPE_NONE.toString().equalsIgnoreCase(scope)) {
// 服务本地暴露
// export to local if the config is not remote (export to remote only when config is remote)
if (!Constants.SCOPE_REMOTE.toString().equalsIgnoreCase(scope)) {
exportLocal(url);
}
// 服务远程暴露
// export to remote if the config is not local (export to local only when config is local)
if (!Constants.SCOPE_LOCAL.toString().equalsIgnoreCase(scope)) {
// ... 【省略】远程暴露
}
}
this.urls.add(url);
}
#exportLocal(url) 方法,本地暴露服务。代码如下:
/**
* 本地暴露服务
*
* @param url 注册中心 URL
*/
@SuppressWarnings({"unchecked", "rawtypes"})
private void exportLocal(URL url) {
if (!Constants.LOCAL_PROTOCOL.equalsIgnoreCase(url.getProtocol())) {
// 创建本地 Dubbo URL
URL local = URL.valueOf(url.toFullString())
.setProtocol(Constants.LOCAL_PROTOCOL) // injvm
.setHost(LOCALHOST) // 本地
.setPort(0); // 端口=0
// 添加服务的真实类名,例如 DemoServiceImpl ,仅用于 RestProtocol 中。
ServiceClassHolder.getInstance().pushServiceClass(getServiceClass(ref));
// 使用 ProxyFactory 创建 Invoker 对象
// 使用 Protocol 暴露 Invoker 对象
//实际上,Protocol 有两个 Wrapper 拓展实现类: ProtocolFilterWrapper、ProtocolListenerWrapper 。所以,#export(...) 方法的调用顺序是:Protocol$Adaptive => ProtocolFilterWrapper => ProtocolListenerWrapper => InjvmProtocol
Exporter exporter = protocol.export(proxyFactory.getInvoker(ref, (Class) interfaceClass, local));
// 添加到 `exporters`
exporters.add(exporter);
logger.info("Export dubbo service " + interfaceClass.getName() + " to local registry");
}
}
实现 Protocol 接口,Protocol 的 Wrapper 拓展实现类,用于给 Invoker 增加过滤链
public Exporter export(Invoker invoker) throws RpcException {
// 注册中心
if (Constants.REGISTRY_PROTOCOL.equals(invoker.getUrl().getProtocol())) {
return protocol.export(invoker);
}
// 建立带有 Filter 过滤链的 Invoker ,再暴露服务。
return protocol.export(buildInvokerChain(invoker, Constants.SERVICE_FILTER_KEY, Constants.PROVIDER));
}
创建带 Filter 链的 Invoker 对象。代码如下:
/**
* 创建带 Filter 链的 Invoker 对象
*
* @param invoker Invoker 对象
* @param key 获取 URL 参数名
* @param group 分组
* @param 泛型
* @return Invoker 对象
*/
private static Invoker buildInvokerChain(final Invoker invoker, String key, String group) {
Invoker last = invoker;
// 获得过滤器数组
List filters = ExtensionLoader.getExtensionLoader(Filter.class).getActivateExtension(invoker.getUrl(), key, group);
// 倒序循环 Filter ,创建带 Filter 链的 Invoker 对象
if (!filters.isEmpty()) {
for (int i = filters.size() - 1; i >= 0; i--) {
final Filter filter = filters.get(i);
final Invoker next = last;
last = new Invoker() {
@Override
public Class getInterface() {
return invoker.getInterface();
}
@Override
public URL getUrl() {
return invoker.getUrl();
}
@Override
public boolean isAvailable() {
return invoker.isAvailable();
}
@Override
public Result invoke(Invocation invocation) throws RpcException {
return filter.invoke(next, invocation);
}
@Override
public void destroy() {
invoker.destroy();
}
@Override
public String toString() {
return invoker.toString();
}
};
}
}
System.out.println("group:" + group);
for (Filter filter : filters) {
System.out.println(filter.getClass());
}
return last;
}
实现 Protocol 接口,Protocol 的 Wrapper 拓展实现类,用于给 Exporter 增加 ExporterListener ,监听 Exporter 暴露完成和取消暴露完成
public Exporter export(Invoker invoker) throws RpcException {
// 注册中心协议
if (Constants.REGISTRY_PROTOCOL.equals(invoker.getUrl().getProtocol())) {
return protocol.export(invoker);
}
// 暴露服务,创建 Exporter 对象
Exporter exporter = protocol.export(invoker);
// 获得 ExporterListener 数组
List listeners = Collections.unmodifiableList(ExtensionLoader.getExtensionLoader(ExporterListener.class).getActivateExtension(invoker.getUrl(), Constants.EXPORTER_LISTENER_KEY));
// 创建带 ExporterListener 的 Exporter 对象
return new ListenerExporterWrapper(exporter, listeners);
}
public abstract class AbstractProtocol implements Protocol {
/**
* Exporter 集合
*
* key: 服务键 {@link #serviceKey(URL)} 或 {@link URL#getServiceKey()} 。
* 不同协议会不同
*/
protected final Map> exporterMap = new ConcurrentHashMap>();
实现 AbstractProtocol 抽象类,Injvm 协议实现类。
/**
* 协议名
*/
public static final String NAME = Constants.LOCAL_PROTOCOL;
/**
* 默认端口
*/
public static final int DEFAULT_PORT = 0;
/**
* 单例。在 Dubbo SPI 中,被初始化,有且仅有一次。
*/
private static InjvmProtocol INSTANCE;
public InjvmProtocol() {
INSTANCE = this;
}
public static InjvmProtocol getInjvmProtocol() {
if (INSTANCE == null) {
ExtensionLoader.getExtensionLoader(Protocol.class).getExtension(InjvmProtocol.NAME); // load
}
return INSTANCE;
}
public Exporter export(Invoker invoker) throws RpcException {
return new InjvmExporter(invoker, invoker.getUrl().getServiceKey(), exporterMap);
}
本文涉及的 Exporter 类图如下:
实现 Exporter 接口,Exporter 抽象类。代码如下:
/**
* AbstractExporter.
*
* Exporter 抽象类
*/
public abstract class AbstractExporter implements Exporter {
protected final Logger logger = LoggerFactory.getLogger(getClass());
/**
* Invoker 对象
*/
private final Invoker invoker;
/**
* 是否取消暴露服务
*/
private volatile boolean unexported = false;
public AbstractExporter(Invoker invoker) {
if (invoker == null)
throw new IllegalStateException("service invoker == null");
if (invoker.getInterface() == null)
throw new IllegalStateException("service type == null");
if (invoker.getUrl() == null)
throw new IllegalStateException("service url == null");
this.invoker = invoker;
}
@Override
public Invoker getInvoker() {
return invoker;
}
@Override
public void unexport() {
// 标记已经取消暴露
if (unexported) {
return;
}
unexported = true;
// 销毁
getInvoker().destroy();
}
public String toString() {
return getInvoker().toString();
}
}
/**
* InjvmExporter
*
* Injvm Exporter 实现类
*/
class InjvmExporter extends AbstractExporter {
/**
* 服务键
*/
private final String key;
/**
* Exporter 集合
*
* key: 服务键
*
* 该值实际就是 {@link com.alibaba.dubbo.rpc.protocol.AbstractProtocol#exporterMap}
*/
private final Map> exporterMap;
InjvmExporter(Invoker invoker, String key, Map> exporterMap) {
super(invoker);
this.key = key;
this.exporterMap = exporterMap;
// 添加到 Exporter 集合
exporterMap.put(key, this);
}
@Override
public void unexport() {
super.unexport();
// 移除出 Exporter 集合
exporterMap.remove(key);
}
}
在本文中,我们来分享远程暴露服务。在 Dubbo 中提供多种协议( Protocol ) 的实现,大体流程一致,本文以 Dubbo Protocol 为例子,这也是 Dubbo 的默认协议。
相比本地暴露,远程暴露会多做如下几件事情:
启动通信服务器,绑定服务端口,提供远程调用。
向注册中心注册服务提供者,提供服务消费者从注册中心发现服务。
远程暴露服务的顺序图如下:
涉及远程暴露服务的代码如下:
// 服务远程暴露
// export to remote if the config is not local (export to local only when config is local)
if (!Constants.SCOPE_LOCAL.toString().equalsIgnoreCase(scope)) {
if (logger.isInfoEnabled()) {
logger.info("Export dubbo service " + interfaceClass.getName() + " to url " + url);
}
if (registryURLs != null && !registryURLs.isEmpty()) {
for (URL registryURL : registryURLs) {
// "dynamic" :服务是否动态注册,如果设为false,注册后将显示后disable状态,需人工启用,并且服务提供者停止时,也不会自动取消册,需人工禁用。
url = url.addParameterIfAbsent("dynamic", registryURL.getParameter("dynamic"));
// 获得监控中心 URL
URL monitorUrl = loadMonitor(registryURL); // TODO 芋艿,监控
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);
}
// 使用 ProxyFactory 创建 Invoker 对象
Invoker invoker = proxyFactory.getInvoker(ref, (Class) interfaceClass, registryURL.addParameterAndEncoded(Constants.EXPORT_KEY, url.toFullString()));
// 创建 DelegateProviderMetaDataInvoker 对象
DelegateProviderMetaDataInvoker wrapperInvoker = new DelegateProviderMetaDataInvoker(invoker, this);
// 使用 Protocol 暴露 Invoker 对象
Exporter exporter = protocol.export(wrapperInvoker);
// 添加到 `exporters`
exporters.add(exporter);
}
} else { // 用于被服务消费者直连服务提供者,参见文档 http://dubbo.io/books/dubbo-user-book/demos/explicit-target.html 。主要用于开发测试环境使用。
// 使用 ProxyFactory 创建 Invoker 对象
Invoker invoker = proxyFactory.getInvoker(ref, (Class) interfaceClass, url);
// 创建 DelegateProviderMetaDataInvoker 对象
DelegateProviderMetaDataInvoker wrapperInvoker = new DelegateProviderMetaDataInvoker(invoker, this);
// 使用 Protocol 暴露 Invoker 对象
Exporter exporter = protocol.export(wrapperInvoker);
// 添加到 `exporters`
exporters.add(exporter);
}
}
loadMonitor(registryURL) 方法,加载监控中心 com.alibaba.dubbo.common.URL 数组
/**
* 加载监控中心 URL
*
* @param registryURL 注册中心 URL
* @return 监控中心 URL
*/
protected URL loadMonitor(URL registryURL) {
// 从 属性配置 中加载配置到 MonitorConfig 对象。
if (monitor == null) {
String monitorAddress = ConfigUtils.getProperty("dubbo.monitor.address");
String monitorProtocol = ConfigUtils.getProperty("dubbo.monitor.protocol");
if ((monitorAddress == null || monitorAddress.length() == 0) && (monitorProtocol == null || monitorProtocol.length() == 0)) {
return null;
}
monitor = new MonitorConfig();
if (monitorAddress != null && monitorAddress.length() > 0) {
monitor.setAddress(monitorAddress);
}
if (monitorProtocol != null && monitorProtocol.length() > 0) {
monitor.setProtocol(monitorProtocol);
}
}
appendProperties(monitor);
// 添加 `interface` `dubbo` `timestamp` `pid` 到 `map` 集合中
Map map = new HashMap();
map.put(Constants.INTERFACE_KEY, MonitorService.class.getName());
map.put("dubbo", Version.getVersion());
map.put(Constants.TIMESTAMP_KEY, String.valueOf(System.currentTimeMillis()));
if (ConfigUtils.getPid() > 0) {
map.put(Constants.PID_KEY, String.valueOf(ConfigUtils.getPid()));
}
// 将 MonitorConfig ,添加到 `map` 集合中。
appendParameters(map, monitor);
// 获得地址
String address = monitor.getAddress();
String sysaddress = System.getProperty("dubbo.monitor.address");
if (sysaddress != null && sysaddress.length() > 0) {
address = sysaddress;
}
// 直连监控中心服务器地址
if (ConfigUtils.isNotEmpty(address)) {
// 若不存在 `protocol` 参数,默认 "dubbo" 添加到 `map` 集合中。
if (!map.containsKey(Constants.PROTOCOL_KEY)) {
if (ExtensionLoader.getExtensionLoader(MonitorFactory.class).hasExtension("logstat")) {
map.put(Constants.PROTOCOL_KEY, "logstat");
} else {
map.put(Constants.PROTOCOL_KEY, "dubbo");
}
}
// 解析地址,创建 Dubbo URL 对象。
return UrlUtils.parseURL(address, map);
// 从注册中心发现监控中心地址
} else if (Constants.REGISTRY_PROTOCOL.equals(monitor.getProtocol()) && registryURL != null) {
return registryURL.setProtocol("dubbo").addParameter(Constants.PROTOCOL_KEY, "registry").addParameterAndEncoded(Constants.REFER_KEY, StringUtils.toQueryString(map));
}
return null;
}
本文涉及的 Protocol 类图如下:
RegistryProtocol ,实现 Protocol 接口,注册中心协议实现类。
/**
* 绑定关系集合。
*
* key:服务 Dubbo URL
*/
// To solve the problem of RMI repeated exposure port conflicts, the services that have been exposed are no longer exposed.
// 用于解决rmi重复暴露端口冲突的问题,已经暴露过的服务不再重新暴露
// providerurl <--> exporter
private final Map> bounds = new ConcurrentHashMap>();
/**
* Protocol 自适应拓展实现类,通过 Dubbo SPI 自动注入。
*/
private Protocol protocol;
/**
* RegistryFactory 自适应拓展实现类,通过 Dubbo SPI 自动注入。
*/
private RegistryFactory registryFactory;
public RegistryProtocol() {
INSTANCE = this;
}
public static RegistryProtocol getRegistryProtocol() {
if (INSTANCE == null) {
ExtensionLoader.getExtensionLoader(Protocol.class).getExtension(Constants.REGISTRY_PROTOCOL); // load
}
return INSTANCE;
}
本文涉及的 #export(invoker) 方法,代码如下:
public Exporter export(final Invoker originInvoker) throws RpcException {
// 暴露服务
// export invoker
final ExporterChangeableWrapper exporter = doLocalExport(originInvoker);
// 获得注册中心 URL
URL registryUrl = getRegistryUrl(originInvoker);
// 获得注册中心对象
// registry provider
final Registry registry = getRegistry(originInvoker);
// 获得服务提供者 URL
final URL registedProviderUrl = getRegistedProviderUrl(originInvoker);
//to judge to delay publish whether or not
boolean register = registedProviderUrl.getParameter("register", true);
// 向注册中心订阅服务消费者
ProviderConsumerRegTable.registerProvider(originInvoker, registryUrl, registedProviderUrl);
// 向注册中心注册服务提供者(自己)
if (register) {
register(registryUrl, registedProviderUrl);
ProviderConsumerRegTable.getProviderWrapper(originInvoker).setReg(true); // // 标记向本地注册表的注册服务提供者,已经注册
}
// 使用 OverrideListener 对象,订阅配置规则
// Subscribe the override data
// FIXME When the provider subscribes, it will affect the scene : a certain JVM exposes the service and call the same service. Because the subscribed is cached key with the name of the service, it causes the subscription information to cover.
// 创建订阅配置规则的 URL
final URL overrideSubscribeUrl = getSubscribedOverrideUrl(registedProviderUrl);
// 创建 OverrideListener 对象,并添加到 `overrideListeners` 中
final OverrideListener overrideSubscribeListener = new OverrideListener(overrideSubscribeUrl, originInvoker);
overrideListeners.put(overrideSubscribeUrl, overrideSubscribeListener);
// 向注册中心,发起订阅
registry.subscribe(overrideSubscribeUrl, overrideSubscribeListener);
//Ensure that a new exporter instance is returned every time export
return new DestroyableExporter(exporter, originInvoker, overrideSubscribeUrl, registedProviderUrl);
}
调用 #getRegistryUrl(originInvoker) 方法,获得注册中心 URL 。代码如下:
/**
* 获得注册中心 URL
*
* @param originInvoker 原始 Invoker
* @return URL
*/
private URL getRegistryUrl(Invoker originInvoker) {
URL registryUrl = originInvoker.getUrl();
if (Constants.REGISTRY_PROTOCOL.equals(registryUrl.getProtocol())) { // protocol
String protocol = registryUrl.getParameter(Constants.REGISTRY_KEY, Constants.DEFAULT_DIRECTORY);
registryUrl = registryUrl.setProtocol(protocol).removeParameter(Constants.REGISTRY_KEY);
}
return registryUrl;
}
该过程是红线部分 :
#register(registryUrl, registedProviderUrl) 方法,向注册中心注册服务提供者。代码如下:
public void register(URL registryUrl, URL registedProviderUrl) {
Registry registry = registryFactory.getRegistry(registryUrl);
registry.register(registedProviderUrl);
}
doLocalExport() 方法,暴露服务。此处的 Local 指的是,本地启动服务,但是不包括向注册中心注册服务的意思,代码如下:
/**
* 暴露服务。
*
* 此处的 Local 指的是,本地启动服务,但是不包括向注册中心注册服务的意思。
*
* @param originInvoker 原始 Invoker
* @param 泛型
* @return Exporter 对象
*/
@SuppressWarnings("unchecked")
private ExporterChangeableWrapper doLocalExport(final Invoker originInvoker) {
// 获得在 `bounds` 中的缓存 Key
String key = getCacheKey(originInvoker);
// 从 `bounds` 获得,是不是已经暴露过服务
ExporterChangeableWrapper exporter = (ExporterChangeableWrapper) bounds.get(key);
if (exporter == null) {
synchronized (bounds) {
exporter = (ExporterChangeableWrapper) bounds.get(key);
// 未暴露过,进行暴露服务
if (exporter == null) {
// 创建 Invoker Delegate 对象
final Invoker invokerDelegete = new InvokerDelegete(originInvoker, getProviderUrl(originInvoker));
// 暴露服务,创建 Exporter 对象
// 使用 创建的Exporter对象 + originInvoker ,创建 ExporterChangeableWrapper 对象
exporter = new ExporterChangeableWrapper((Exporter) protocol.export(invokerDelegete), originInvoker);
// 添加到 `bounds`
bounds.put(key, exporter);
}
}
}
return exporter;
}
实现 AbstractProtocol 抽象类,Dubbo 协议实现类。
本文涉及的 #export(invoker) 方法,代码如下:
public Exporter export(Invoker invoker) throws RpcException {
URL url = invoker.getUrl();
// 创建 DubboExporter 对象,并添加到 `exporterMap` 。
// export service.
String key = serviceKey(url);
DubboExporter exporter = new DubboExporter(invoker, key, exporterMap);
exporterMap.put(key, exporter);
// TODO 【8005 sub】
//export an stub service for dispatching 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);
// 初始化序列化优化器
optimizeSerialization(url);
return exporter;
}
启动通信服务器。代码如下:
/**
* 启动通信服务器
*
* @param url URL
*/
private void openServer(URL url) {
// find server.
String key = url.getAddress();
//client 也可以暴露一个只有server可以调用的服务。
//client can export a service which's only for server to invoke
boolean isServer = url.getParameter(Constants.IS_SERVER_KEY, true); // isserver
if (isServer) {
ExchangeServer server = serverMap.get(key);
if (server == null) {
serverMap.put(key, createServer(url));
} else {
// server supports reset, use together with override
server.reset(url);
}
}
}
创建并启动通信服务器。代码如下:
/**
* 创建并启动通信服务器
*
* @param url URL
* @return 服务器
*/
private ExchangeServer createServer(URL url) {
// 默认开启 server 关闭时发送 READ_ONLY 事件
// send readonly event when server closes, it's enabled by default
url = url.addParameterIfAbsent(Constants.CHANNEL_READONLYEVENT_SENT_KEY, Boolean.TRUE.toString());
// 默认开启 heartbeat
// enable heartbeat by default
url = url.addParameterIfAbsent(Constants.HEARTBEAT_KEY, String.valueOf(Constants.DEFAULT_HEARTBEAT));
// 校验 Server 的 Dubbo SPI 拓展是否存在
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);
}
// 设置编解码器为 Dubbo ,即 DubboCountCodec
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);
}
// 校验 Client 的 Dubbo SPI 拓展是否存在
str = url.getParameter(Constants.CLIENT_KEY);
if (str != null && str.length() > 0) {
Set supportedTypes = ExtensionLoader.getExtensionLoader(Transporter.class).getSupportedExtensions();
if (!supportedTypes.contains(str)) {
throw new RpcException("Unsupported client type: " + str);
}
}
return server;
}
本文涉及的 Exporter 类图如下:
实现 Exporter 接口,Exporter 可变的包装器。
/**
* exporter proxy, establish the corresponding relationship between the returned exporter and the exporter exported by the protocol, and can modify the relationship at the time of override.
*
* exporter 代理, 建立返回的 exporter 与 protocol export 出的 exporter 的对应关系,在 override 时可以进行关系修改.
*
* @param
*/
private class ExporterChangeableWrapper implements Exporter {
/**
* 原 Invoker 对象
*/
private final Invoker originInvoker;
/**
* 暴露的 Exporter 对象
*/
private Exporter exporter;
public ExporterChangeableWrapper(Exporter exporter, Invoker originInvoker) {
this.exporter = exporter;
this.originInvoker = originInvoker;
}
public Invoker getOriginInvoker() {
return originInvoker;
}
@Override
public Invoker getInvoker() {
return exporter.getInvoker();
}
// 可以重新设置 Exporter 对象
public void setExporter(Exporter exporter) {
this.exporter = exporter;
}
@Override
public void unexport() {
String key = getCacheKey(this.originInvoker);
// 移除出 `bounds`
bounds.remove(key);
// 取消暴露
exporter.unexport();
}
}
实现 Exporter 接口,可销毁的 Exporter 实现类。
/**
* 可销毁的 Exporter
*
* @param 泛型
*/
static private class DestroyableExporter implements Exporter {
public static final ExecutorService executor = Executors.newSingleThreadExecutor(new NamedThreadFactory("Exporter-Unexport", true));
private Exporter exporter;
private Invoker originInvoker;
private URL subscribeUrl;
private URL registerUrl;
public DestroyableExporter(Exporter exporter, Invoker originInvoker, URL subscribeUrl, URL registerUrl) {
this.exporter = exporter;
this.originInvoker = originInvoker;
this.subscribeUrl = subscribeUrl;
this.registerUrl = registerUrl;
}
public Invoker getInvoker() {
return exporter.getInvoker();
}
public void unexport() {
//
Registry registry = RegistryProtocol.INSTANCE.getRegistry(originInvoker);
try {
registry.unregister(registerUrl);
} catch (Throwable t) {
logger.warn(t.getMessage(), t);
}
//
try {
NotifyListener listener = RegistryProtocol.INSTANCE.overrideListeners.remove(subscribeUrl);
registry.unsubscribe(subscribeUrl, listener);
} catch (Throwable t) {
logger.warn(t.getMessage(), t);
}
//
executor.submit(new Runnable() {
@Override
public void run() {
try {
int timeout = ConfigUtils.getServerShutdownTimeout();
if (timeout > 0) {
logger.info("Waiting " + timeout + "ms for registry to notify all consumers before unexport. Usually, this is called when you use dubbo API");
Thread.sleep(timeout);
}
exporter.unexport();
} catch (Throwable t) {
logger.warn(t.getMessage(), t);
}
}
});
}
}
实现 AbstractExporter 抽象类,Dubbo Exporter 实现类。代码如下:
/**
* DubboExporter
*/
public class DubboExporter extends AbstractExporter {
/**
* 服务键
*/
private final String key;
/**
* Exporter 集合
*
* key: 服务键
*
* 该值实际就是 {@link com.alibaba.dubbo.rpc.protocol.AbstractProtocol#exporterMap}
*/
private final Map> exporterMap;
public DubboExporter(Invoker invoker, String key, Map> exporterMap) {
super(invoker);
this.key = key;
this.exporterMap = exporterMap;
}
@Override
public void unexport() {
// 取消暴露
super.unexport();
// 移除
exporterMap.remove(key);
}
}