服務方初始化 服務方初始化的入口在ServiceConfig類的export方法 這裡的初始化是針對一個服務的 首先判斷是否要發佈,以及延遲發佈 先處理各種配置,按優先順序覆蓋 從配置中取註冊中心URL,註冊中心可能有多個 ...
### 服務方初始化
服務方初始化的入口在ServiceConfig類的export方法
這裡的初始化是針對一個服務的
public class ServiceConfig<T> extends AbstractServiceConfig {
private static final long serialVersionUID = 3033787999037024738L;
/**
* 獲取自適應擴展點
* 自適應擴展點和普通擴展點的區別是:普通擴展點的實現類是確定的,自適應擴展點的實現類會根據參數不同變化
* 這裡自適應擴展點用了位元組碼生成代理類來優化性能,使用動態代理應該也可以實現
*/
private static final Protocol protocol = ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension();
// 代理工廠,用來生成代理類
private static final ProxyFactory proxyFactory = ExtensionLoader.getExtensionLoader(ProxyFactory.class).getAdaptiveExtension();
// 保存隨機生成的埠號
private static final Map<String, Integer> RANDOM_PORT_MAP = new HashMap<String, Integer>();
// 延遲發佈的執行器
private static final ScheduledExecutorService delayExportExecutor = Executors.newSingleThreadScheduledExecutor(new NamedThreadFactory("DubboServiceDelayExporter", true));
// 生成的服務發佈url
private final List<URL> urls = new ArrayList<URL>();
private final List<Exporter<?>> exporters = new ArrayList<Exporter<?>>();
// 介面類型
private String interfaceName;
private Class<?> interfaceClass;
// 介面實現類引用
private T ref;
// 服務名稱
private String path;
// 方法配置
private List<MethodConfig> methods;
private ProviderConfig provider;
private transient volatile boolean exported;
private transient volatile boolean unexported;
// 是否泛化引用
private volatile String generic;
// ...
}
首先判斷是否要發佈,以及延遲發佈
public synchronized void export() {
if (provider != null) {
if (export == null) {
export = provider.getExport();
}
if (delay == null) {
delay = provider.getDelay();
}
}
// 是否要發佈服務
if (export != null && !export) {
return;
}
// 延遲發佈配置
if (delay != null && delay > 0) {
delayExportExecutor.schedule(new Runnable() {
public void run() {
doExport();
}
}, delay, TimeUnit.MILLISECONDS);
} else {
doExport();
}
}
先處理各種配置,按優先順序覆蓋
protected synchronized void doExport() {
if (unexported) {
throw new IllegalStateException("Already unexported!");
}
if (exported) {
return;
}
exported = true;
if (interfaceName == null || interfaceName.length() == 0) {
throw new IllegalStateException("<dubbo:service interface=\"\" /> interface not allow null!");
}
// 處理ProviderConfig
checkDefault();
// ...
// 對配置各種處理
// ...
// 校驗配置
checkApplication();
checkRegistry();
checkProtocol();
// 最後處理ServiceConfig,優先順序最高
appendProperties(this);
checkStubAndMock(interfaceClass);
if (path == null || path.length() == 0) {
path = interfaceName;
}
// 發佈服務
doExportUrls();
// 把要服務提供者信息封裝成model,並設置方法可見性
ProviderModel providerModel = new ProviderModel(getUniqueServiceName(), this, ref);
// 放進全局的context中
ApplicationModel.initProviderModel(getUniqueServiceName(), providerModel);
}
從配置中取註冊中心URL,註冊中心可能有多個
private void doExportUrls() {
List<URL> registryURLs = loadRegistries(true);
// 發佈到多種協議中
for (ProtocolConfig protocolConfig : protocols) {
doExportUrlsFor1Protocol(protocolConfig, registryURLs);
}
}
private void doExportUrlsFor1Protocol(ProtocolConfig protocolConfig, List<URL> registryURLs) {
// ...
// 將各種配置取出來放到map中,準備組裝成URL
// ...
// 根據優先順序取出配置的host和port
String host = this.findConfigedHosts(protocolConfig, registryURLs, map);
Integer port = this.findConfigedPorts(protocolConfig, name, map);
// 將所有參數封裝到URL對象中, 如: hessian://192.168.9.212:20880/com.alibaba.dubbo.demo.UserService?accesslog=true&anyhost=true&application=demo-provider&bind.ip=192.168.9.212&bind.port=20880&dispatcher=all&dubbo=2.0.0&generic=false&interface=com.alibaba.dubbo.demo.UserService&methods=getName&pid=20064&revision=1.0.0&side=provider&threadpool=cached&timeout=5000×tamp=1513650421650&version=1.0.0
URL url = new URL(name, host, port, (contextPath == null || contextPath.length() == 0 ? "" : contextPath + "/") + path, map);
if (ExtensionLoader.getExtensionLoader(ConfiguratorFactory.class)
.hasExtension(url.getProtocol())) {
url = ExtensionLoader.getExtensionLoader(ConfiguratorFactory.class)
.getExtension(url.getProtocol()).getConfigurator(url).configure(url);
}
String scope = url.getParameter(Constants.SCOPE_KEY);
//配置為none不暴露
if (!Constants.SCOPE_NONE.equalsIgnoreCase(scope)) {
//配置不是remote的情況下做本地暴露 (配置為remote,則表示只暴露遠程服務)
if (!Constants.SCOPE_REMOTE.equalsIgnoreCase(scope)) {
exportLocal(url);
}
//如果配置不是local則暴露為遠程服務.(配置為local,則表示只暴露本地服務)
if (!Constants.SCOPE_LOCAL.equalsIgnoreCase(scope)) {
if (logger.isInfoEnabled()) {
logger.info("Export dubbo service " + interfaceClass.getName() + " to url " + url);
}
if (registryURLs != null && registryURLs.size() > 0) {
for (URL registryURL : registryURLs) {
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);
}
// 介面實現類生成動態代理的介面調用器,此調用器調用的是本地介面實現類,而消費方的調用器是調用遠程的方法。
// registryURL是註冊服務用的URL,url則是調用服務的url
Invoker<?> invoker = proxyFactory.getInvoker(ref, (Class) interfaceClass, registryURL.addParameterAndEncoded(Constants.EXPORT_KEY, url.toFullString()));
// 包裝Invoker
DelegateProviderMetaDataInvoker wrapperInvoker = new DelegateProviderMetaDataInvoker(invoker, this);
// 發佈服務方法
Exporter<?> exporter = protocol.export(wrapperInvoker);
exporters.add(exporter);
}
} else {
// 沒有配置註冊中心時,直接發佈服務,服務只能通過直連方式引用
Invoker<?> invoker = proxyFactory.getInvoker(ref, (Class) interfaceClass, url);
DelegateProviderMetaDataInvoker wrapperInvoker = new DelegateProviderMetaDataInvoker(invoker, this);
Exporter<?> exporter = protocol.export(wrapperInvoker);
exporters.add(exporter);
}
}
}
this.urls.add(url);
}
// 預設情況服務會發佈到遠程和本地,如果指定了local或remote則只發佈到本地或遠程
private void exportLocal(URL url) {
// 將host設置成localhost,這樣只能本地調用了
// 如果本身就指定了injvm則不用再次發佈到本地了。
if (!Constants.LOCAL_PROTOCOL.equalsIgnoreCase(url.getProtocol())) {
// injvm://127.0.0.1/com.alibaba.dubbo.demo.UserService?accesslog=true&anyhost=true&application=demo-provider&bind.ip=192.168.9.212&bind.port=20880&dispatcher=all&dubbo=2.0.0&generic=false&interface=com.alibaba.dubbo.demo.UserService&methods=getName&pid=20064&revision=1.0.0&side=provider&threadpool=cached&timeout=5000×tamp=1513650421650&version=1.0.0
URL local = URL.valueOf(url.toFullString())
.setProtocol(Constants.LOCAL_PROTOCOL)
.setHost(LOCALHOST)
.setPort(0);
// 暴露服務, 將介面實現類生成動態代理的介面調用器
Exporter<?> exporter = protocol.export(
proxyFactory.getInvoker(ref, (Class) interfaceClass, local));
exporters.add(exporter);
logger.info("Export dubbo service " + interfaceClass.getName() + " to local registry");
}
}