關注消費者端服務的訂閱,我們從ReferenceConfig中的createProxy()開始分析,我們重點先關注其中的一句,當註冊中心只有一個(單個或集羣)時進入此分支即執行下面這一句。(關於createProxy整個流程會在後面博文中重點介紹)
invoker = refprotocol.refer(interfaceClass, urls.get(0));
其中refprotocol = ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension();之前關於@SPI的文章中分析過,refProtocol其實是字節碼動態生成的適配類。 public class Protocol$Adpative implements Protocol {
public Exporter export(Invoker invoker) throws com.alibaba.dubbo.rpc.RpcException {
if (invoker == null || invoker.getUrl() == null) {
throw new IllegalArgumentException("xxx");
}
URL url = invoker.getUrl();
String extName = url.getProtocol() == null ? "dubbo" : url.getProtocol();
if(extName == null) {
throw new IllegalStateException("xxx");
}
Protocol extension = (Protocol)ExtensionLoader.getExtensionLoader(Protocol.class).getExtension(extName);
return extension.export(invoker);
}
public Invoker refer(Class cls, URL u) throws RpcException {
if (u == null) {
throw new IllegalArgumentException("url == null");
}
URL url = u;
String extName = url.getProtocol() == null ? "dubbo" : url.getProtocol();
if(extName == null) {
throw new IllegalStateException("xxx");
}
Protocol extension = (Protocol)ExtensionLoader.getExtensionLoader(Protocol.class).getExtension(extName);
return extension.refer(cls, u);
}
}
實際調用了其中的refer()方法,我們可以看到SPI的動態選擇在這裏體現,這裏傳入的URL的protocol爲reistery,所以這裏的extension實際上是registryProtocol的實例。這裏調用了registryProtocol的refer()方法。
@SuppressWarnings("unchecked")
public <T> Invoker<T> refer(Class<T> type, URL url) throws RpcException {
url = url.setProtocol(url.getParameter(Constants.REGISTRY_KEY, Constants.DEFAULT_REGISTRY))
.removeParameter(Constants.REGISTRY_KEY);
Registry registry = registryFactory.getRegistry(url);
if (RegistryService.class.equals(type)) {
return proxyFactory.getInvoker((T) registry, type, url);
}
// group="a,b" or group="*"
Map<String, String> qs = StringUtils.parseQueryString(url.getParameterAndDecoded(Constants.REFER_KEY));
String group = qs.get(Constants.GROUP_KEY);
if (group != null && group.length() > 0 ) {
if ( ( Constants.COMMA_SPLIT_PATTERN.split( group ) ).length > 1
|| "*".equals( group ) ) {
return doRefer( getMergeableCluster(), registry, type, url );
}
}
return doRefer(cluster, registry, type, url);
}
先修改url數據結構上的protocol改爲registry對應的協議(比如multicast或者zookeeper),調用registryFactory的getRegister,根據url返回不同的registry對象,在初始化registry對象時,通過代理和註冊中心建立連接。其中registryFactory也是個代理適配器對象(會根據url對應不同的協議實際上可能是ZookeeperRegistryFactory、MulticastRegistryFactory等)。這裏採用默認的dubbo實現,其中getRegistry()方法具體邏輯在AbstractRegistryFactory中
public Registry getRegistry(URL url) {
url = url.setPath(RegistryService.class.getName())
.addParameter(Constants.INTERFACE_KEY, RegistryService.class.getName())
.removeParameters(Constants.EXPORT_KEY, Constants.REFER_KEY);
String key = url.toServiceString();
// 鎖定註冊中心獲取過程,保證註冊中心單一實例
LOCK.lock();
try {
Registry registry = REGISTRIES.get(key);
if (registry != null) {
return registry;
}
registry = createRegistry(url);
if (registry == null) {
throw new IllegalStateException("Can not create registry " + url);
}
REGISTRIES.put(key, registry);
return registry;
} finally {
// 釋放鎖
LOCK.unlock();
}
}
先把要註冊的服務跟接口都改爲RegistryService(RegistryService是註冊中心的接口)。這裏會根據生成的註冊中心服務url尋找註冊中心的實例,如果已經生成過,那麼直接返回,否則調用createRegister()生成新的註冊中心。
默認是dubbo實現的registryFactory爲DubboRegistryFactory,我們來看下具體的createRegistry()方法。
public Registry createRegistry(URL url) {
url = getRegistryURL(url);
List<URL> urls = new ArrayList<URL>();
urls.add(url.removeParameter(Constants.BACKUP_KEY));
String backup = url.getParameter(Constants.BACKUP_KEY);
if (backup != null && backup.length() > 0) {
String[] addresses = Constants.COMMA_SPLIT_PATTERN.split(backup);
for (String address : addresses) {
urls.add(url.setAddress(address));
}
}
RegistryDirectory<RegistryService> directory = new RegistryDirectory<RegistryService>(RegistryService.class,
url.addParameter(Constants.INTERFACE_KEY, RegistryService.class.getName())
.addParameterAndEncoded(Constants.REFER_KEY, url.toParameterString()));
Invoker<RegistryService> registryInvoker = cluster.join(directory);
RegistryService registryService = proxyFactory.getProxy(registryInvoker);
DubboRegistry registry = new DubboRegistry(registryInvoker, registryService);
directory.setRegistry(registry);
directory.setProtocol(protocol);
directory.notify(urls);
directory.subscribe(new URL(Constants.CONSUMER_PROTOCOL, NetUtils.getLocalHost(), 0,
RegistryService.class.getName(), url.getParameters()));
return registry;
}
我們可以看到,這裏構造了接口registryService的代理,(這裏,我們可以把註冊中心理解成暴露服務端,與註冊中心連接的過程可以看成RegistryService的遠程方法調用的過程,當然跟其他服務調用不同的是,這裏註冊中心作爲生產者是直接與當前消費者直連,而不是在註冊中心得到url,redirect訪問生產者)。這裏directory的subscribe實際上是調用register的subsubscribe。
public void subscribe(URL url) {
setConsumerUrl(url);
registry.subscribe(url, this);
}
protected void doSubscribe(URL url, NotifyListener listener) {
registryService.subscribe(url, listener);
}
實則調用了registryService代理的subscrible,根據之前對代理的分析我們明確知道,他是調用了registryInvoker的invoke方法,其實就是一次普通的遠程調用方法。傳入參數的url中被加入了回調參數,listener是變更事件監聽器,註冊中心通過對這個參數的調用回調給消費者。在得到registry之後,我們繼續registryProtocol的refer()方法。
我們可以看到根據url上面的group的配置,分爲group模式跟非group模式,剩下都是調用doRefer(),無非在group模式時返回的invoker爲MergeableClusterInvoker,非group模式時返回的invoker爲FailoverClusterInvoker。
private <T> Invoker<T> doRefer(Cluster cluster, Registry registry, Class<T> type, URL url) {
RegistryDirectory<T> directory = new RegistryDirectory<T>(type, url);
directory.setRegistry(registry);
directory.setProtocol(protocol);
URL subscribeUrl = new URL(Constants.CONSUMER_PROTOCOL, NetUtils.getLocalHost(), 0,
type.getName(), directory.getUrl().getParameters());
if (! Constants.ANY_VALUE.equals(url.getServiceInterface())
&& url.getParameter(Constants.REGISTER_KEY, true)) {
registry.register(subscribeUrl.addParameters(Constants.CATEGORY_KEY, Constants.CONSUMERS_CATEGORY,
Constants.CHECK_KEY, String.valueOf(false)));
}
directory.subscribe(subscribeUrl.addParameter(Constants.CATEGORY_KEY,
Constants.PROVIDERS_CATEGORY
+ "," + Constants.CONFIGURATORS_CATEGORY
+ "," + Constants.ROUTERS_CATEGORY));
return cluster.join(directory);
}
這裏的registry就是之前的registryService,通過遠程調用的方式,其中url中以接口名字爲參數,來完成接口方法的訂閱。在默認dubbo協議下異步等待註冊中心回調,通過.join()方法得到默認的invoker,FailoverClusterInvoker即遠程服務執行體。當然,註冊中心需要在subscribe()方法末尾遠程回調listener的notify方法,即在完成訂閱後告訴消費者,達到服務地址的回調。
小結
通過配置選擇合適的協議,生成對應的registryService代理,通過遠程回調的方式,連接上註冊中心,再通過registryService代理,傳遞帶有接口類型url數據結構,實現在註冊中心的服務的訂閱,得到服務地址,返回invoker。
說在最後的話
本文可能思路多多少少不是特別清楚,比較我也是邊看邊寫的,等我把生產者服務暴露端的流程過一遍後,再把整個串起來總結一遍。