ServiceBean類圖
類繼承圖
成員變量和方法
服務暴露
服務暴露流程圖
我們看幾個核心的節點
ServiceConfig#doExport
protected synchronized void doExport()
該方法爲同步方法,在初始化時同步阻塞。
方法開始時進行一些列的檢查,包含對provider,module,application,ref。
if (ref instanceof GenericService) {
interfaceClass = GenericService.class;
if (StringUtils.isEmpty(generic)) {
generic = Boolean.TRUE.toString();
}
} else {
try {
interfaceClass = Class.forName(interfaceName, true, Thread.currentThread()
.getContextClassLoader());
} catch (ClassNotFoundException e) {
throw new IllegalStateException(e.getMessage(), e);
}
checkInterfaceAndMethods(interfaceClass, methods);
checkRef();
generic = Boolean.FALSE.toString();
}
ref爲當前暴露對象的引用,首先檢查當前對象是否爲GenericService實例,否則會對對應的接口和方法進行檢查。
ServiceConfig#doExportUrls
private void doExportUrls() {
List<URL> registryURLs = loadRegistries(true);
for (ProtocolConfig protocolConfig : protocols) {
doExportUrlsFor1Protocol(protocolConfig, registryURLs);
}
}
Dubbo支持多註冊協議導出服務,也允許我們向多個註冊中心註冊服務,在上面的方法會加載對應的註冊中心,並在每個協議下導出服務。
ServiceConfig#doExportUrlsFor1Protocol
Dubbo導出服務默認爲dubbo協議
String name = protocolConfig.getName();
if (name == null || name.length() == 0) {
name = "dubbo";
}
然後獲取一些列基本參數
如果我們沒有配置scope爲remote,那麼dubbo總會執行一次injvm暴露
// 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);
}
exportLocal
private void exportLocal(URL url) {
if (!Constants.LOCAL_PROTOCOL.equalsIgnoreCase(url.getProtocol())) {
URL local = URL.valueOf(url.toFullString())
.setProtocol(Constants.LOCAL_PROTOCOL)
.setHost(LOCALHOST)
.setPort(0);
ServiceClassHolder.getInstance().pushServiceClass(getServiceClass(ref));
Exporter<?> exporter = protocol.export(
proxyFactory.getInvoker(ref, (Class) interfaceClass, local));
exporters.add(exporter);
logger.info("Export dubbo service " + interfaceClass.getName() + " to local registry");
}
}
執行InjvmProtocol#export方法,執行injvm暴露
多註冊中心暴露
if (registryURLs != null && registryURLs.size() > 0) {
for (URL registryURL : registryURLs) {
獲取對應invoker方法,並執行export
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 創建過程
Invoker 是實體域,它是 Dubbo 的核心模型,其它模型都向它靠擾,或轉換成它,它代表一個可執行體,可向它發起 invoke 調用,它有可能是一個本地的實現,也可能是一個遠程的實現,也可能一個集羣實現。
private static final ProxyFactory proxyFactory = ExtensionLoader.getExtensionLoader(ProxyFactory.class).getAdaptiveExtension();
Invoker<?> invoker = proxyFactory.getInvoker(ref, (Class) interfaceClass, registryURL.addParameterAndEncoded(Constants.EXPORT_KEY, url.toFullString()));
DelegateProviderMetaDataInvoker wrapperInvoker = new DelegateProviderMetaDataInvoker(invoker, this);
如上看到,dubbo的集中代理生成方式,Dubbo 默認的 ProxyFactory 實現類是 JavassistProxyFactory。
public class JavassistProxyFactory extends AbstractProxyFactory {
@SuppressWarnings("unchecked")
public <T> T getProxy(Invoker<T> invoker, Class<?>[] interfaces) {
return (T) Proxy.getProxy(interfaces).newInstance(new InvokerInvocationHandler(invoker));
}
public <T> Invoker<T> getInvoker(T proxy, Class<T> type, URL url) {
// TODO Wrapper cannot handle this scenario correctly: the classname contains '$'
final Wrapper wrapper = Wrapper.getWrapper(proxy.getClass().getName().indexOf('$') < 0 ? proxy.getClass() : type);
return new AbstractProxyInvoker<T>(proxy, type, url) {
@Override
protected Object doInvoke(T proxy, String methodName,
Class<?>[] parameterTypes,
Object[] arguments) throws Throwable {
return wrapper.invokeMethod(proxy, methodName, parameterTypes, arguments);
}
};
}
}
核心在getInvoke時生成對應的getWrapper方法
public static Wrapper getWrapper(Class<?> c) {
while (ClassGenerator.isDynamicClass(c)) // can not wrapper on dynamic class.
c = c.getSuperclass();
if (c == Object.class)
return OBJECT_WRAPPER;
Wrapper ret = WRAPPER_MAP.get(c);
if (ret == null) {
ret = makeWrapper(c);
WRAPPER_MAP.put(c, ret);
}
return ret;
}
getWrapper調用makeWrapper生成對應的代理對象,代碼較多,暫不展示
RegistryProtocol#export
該方法主要負責完成:
- 調用 doLocalExport 導出服務
- 獲取註冊中心 URL
- 根據 URL 加載 Registry 實現類,比如 ZookeeperRegistry
- 取已註冊的服務提供者 URL
- 獲取 register 參數
- 向服務提供者與消費者註冊表中註冊服務提供者
- 根據 register 的值決定是否註冊服務
- 向註冊中心註冊服務
- 獲取訂閱 URL
- 創建監聽器
- 向註冊中心進行訂閱 override 數據
- 創建並返回 DestroyableExporter
真實export
在doLocalExport方法中,會調用具體的協議的暴露方法
RegistryProtocol#doLocalExport
exporter = new ExporterChangeableWrapper<T>((Exporter<T>) protocol.export(invokerDelegete), originInvoker);
會根據對應的協議執行協議的export方法
DubboProtocol#export
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 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;
}
- 獲取註冊的url
- 獲取服務標識,理解成服務座標也行。由服務組名,服務名,服務版本號以及端口組成 serviceKey
- 將 <key, exporter> 鍵值對放入緩存中
- 啓動服務器
- 優化序列化
openServer
private void openServer(URL url) {
// find server.
String key = url.getAddress();
//client can export a service which's only for server to invoke
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 supports reset, use together with override
server.reset(url);
}
}
}
最終創建NettyServer