導讀
- 上一篇我們分析了 Dubbo設計之ExtensionLoader,瞭解了其原理之後,就方便我們分析Dubbo客戶端得初始化過程了。
- 關鍵字 :Dubbo客戶端初始化、Netty入門級用法
- Dubbo版本 : 2.7+
- 入口: ReferenceConfig
客戶端初始化:
- 下面是Dubbo消費者API使用方式的示例代碼
// 創建ReferenceConfig,此類會有一系列的初始化準備過程
ReferenceConfig<AlipayInfoAbility> reference = new ReferenceConfig<>();
// 設置消費者應用配置
reference.setApplication(new ApplicationConfig("dubbo-demo-api-consumer"));
// 設置註冊地址
reference.setRegistry(new RegistryConfig("zookeeper://" + zookeeperHost + ":" + zookeeperPort));
// 設置引用的API接口
reference.setInterface(AlipayInfoAbility.class);
// 關閉服務啓動檢查(建議不要關閉,找不到服務要在應用發佈時就要解決)
reference.setCheck(false);
// 獲取引用
AlipayInfoAbility service = reference.get();
上述代碼中, reference.get()
是整個調用方獲取接口代理得核心起始點。主要核心邏輯如下:
① 設置接口級別得公共參數 Map<String, String> map:爲初始化代理引用ref
做準備,例如:設置key = side,value = consumer,表示當前是服務調用方;通過 MetricsConfig
、RegistryConfig(註冊中心)
、ConfigCenterConfig(配置中心)
、ApplicationConfig
、ModuleConfig
、ConsumerConfig(可以指定消費端線程池類型:cached, fixed, limit, eager;核心線程數、最大線程數、隊列大小、共享連接數shareconnections(TCP長連接得客戶端數量))
、MethodConfig(可以指定方法相關得參數配置:可以設置重試次數、併發數(executes)、是否啓動session粘連(sticky)、方法是否有異步回調等)
等初始化配置來填充 map。
② 接着就是調用創建代理引用得核心方法org.apache.dubbo.config.ReferenceConfig#createProxy
private T createProxy(Map<String, String> map) {
if (shouldJvmRefer(map)) { // 1.判斷是否是本地調用(通過scope=local、injvm=false都可以),重要用於本地測試,防止調用遠程服務器
URL url = new URL(LOCAL_PROTOCOL, LOCALHOST_VALUE, 0, interfaceClass.getName()).addParameters(map);
invoker = REF_PROTOCOL.refer(interfaceClass, url); // 通過自適應擴展點機制獲取代理Invoker
if (logger.isInfoEnabled()) {
logger.info("Using injvm service " + interfaceClass.getName());
}
} else { // 2.不是本地調用
urls.clear(); // reference retry init will add url to urls, lead to OOM
if (url != null && url.length() > 0) { // user specified URL, could be peer-to-peer address, or register center's address.
String[] us = SEMICOLON_SPLIT_PATTERN.split(url);
if (us != null && us.length > 0) {
for (String u : us) {
URL url = URL.valueOf(u);
if (StringUtils.isEmpty(url.getPath())) {
url = url.setPath(interfaceName);
}
if (REGISTRY_PROTOCOL.equals(url.getProtocol())) { // 3.協議類型是註冊(registry)
urls.add(url.addParameterAndEncoded(REFER_KEY, StringUtils.toQueryString(map)));
} else {
urls.add(ClusterUtils.mergeUrl(url, map));
}
}
}
} else { // assemble URL from register center's configuration
// if protocols not injvm checkRegistry
if (!LOCAL_PROTOCOL.equalsIgnoreCase(getProtocol())){ // 若註冊協議不是injvm
// 校驗註冊中心配置(RegistryConfig),會向後兼容-Ddubbo.registry.address啓動參數
checkRegistry();
// 4.通過配置中心相關設置解析成URL(一般註冊中心都使用 zookeeper,沒配置默認是dubbo)
// 執行完之後會將protocol屬性key設置爲registry,當下面調用協議自適應擴展點得時候就會找到 Registryprotocol
List<URL> us = loadRegistries(false);
if (CollectionUtils.isNotEmpty(us)) {
for (URL u : us) {
// 5.加載監控配置,顯式設置直接使用。沒顯式設置:判斷是否有logstat監控擴展點工廠,沒有會使用Dubbo得監控
URL monitorUrl = loadMonitor(u);
if (monitorUrl != null) {
map.put(MONITOR_KEY, URL.encode(monitorUrl.toFullString()));
}
urls.add(u.addParameterAndEncoded(REFER_KEY, StringUtils.toQueryString(map)));
}
}
if (urls.isEmpty()) {
throw new IllegalStateException("No such any registry to reference " + interfaceName + " on the consumer " + NetUtils.getLocalHost() + " use dubbo version " + Version.getVersion() + ", please config <dubbo:registry address=\"...\" /> to your spring config.");
}
}
}
if (urls.size() == 1) {// 註冊中心單節點
// 6.通過Registryprotocol$Apaptive 自適應擴展點實現獲取Invoker
invoker = REF_PROTOCOL.refer(interfaceClass, urls.get(0));
} else { // 註冊中心集羣
List<Invoker<?>> invokers = new ArrayList<Invoker<?>>();
URL registryURL = null;
for (URL url : urls) {
// 7.通過Registryprotocol$Apaptive獲取Invoker,放入到列表中
invokers.add(REF_PROTOCOL.refer(interfaceClass, url));
if (REGISTRY_PROTOCOL.equals(url.getProtocol())) {
registryURL = url; // use last registry url
}
}
if (registryURL != null) { // registry url is available
// use RegistryAwareCluster only when register's CLUSTER is available
URL u = registryURL.addParameter(CLUSTER_KEY, RegistryAwareCluster.NAME);
// The invoker wrap relation would be: RegistryAwareClusterInvoker(StaticDirectory) -> FailoverClusterInvoker(RegistryDirectory, will execute route) -> Invoker
invoker = CLUSTER.join(new StaticDirectory(u, invokers));// 8.通過集羣RegistryAwareCluster對Invoker進行包裝
} else { // not a registry url, must be direct invoke.
invoker = CLUSTER.join(new StaticDirectory(invokers));
}
}
}
// 若開啓Dubbo服務檢查,並且服務接口不可用,則直接拋異常
if (shouldCheck() && !invoker.isAvailable()) {
throw new IllegalStateException("Failed to check the status of the service " + interfaceName + ". No provider available for the service " + (group == null ? "" : group + "/") + interfaceName + (version == null ? "" : ":" + version) + " from the url " + invoker.getUrl() + " to the consumer " + NetUtils.getLocalHost() + " use dubbo version " + Version.getVersion());
}
if (logger.isInfoEnabled()) {
logger.info("Refer dubbo service " + interfaceClass.getName() + " from url " + invoker.getUrl());
}
/**
* @since 2.7.0
* ServiceData Store
*/
MetadataReportService metadataReportService = null;
if ((metadataReportService = getMetadataReportService()) != null) {
URL consumerURL = new URL(CONSUMER_PROTOCOL, map.remove(REGISTER_IP_KEY), 0, map.get(INTERFACE_KEY), map);
metadataReportService.publishConsumer(consumerURL);// 9.消費者元數據信息發佈
}
// create service proxy
return (T) PROXY_FACTORY.getProxy(invoker);// 10.真正開始創建代理對象,通過代理工廠創建代理引用(代理工廠的默認擴展點名是 javassist )
}
上述方法中有幾處重要得地方:1、2、3、4、5、6(單節點)、7、8(集羣)、9、10;其中6(7)、8、10是重中之重。下面主要來分析6(7)、8、10這幾個關鍵步驟:
6(7) 步驟核心子流程如下:
① 調用協議的自適應擴展點獲取真正端得協議擴展點(Registryprotocol),(擴展點的原理:Dubbo設計之ExtensionLoader),但是由於Protocol擴展點有包裝類(ProtocolFilterWrapper、ProtocolListenerWrapper)
,所以獲取真正的擴展實現之後會被包裝類包裝起來: ProtocolFilterWrapper.refer -> ProtocolListenerWrapper.refer -> Registryprotocol.refer :
// ProtocolFilterWrapper
@Override
public <T> Invoker<T> refer(Class<T> type, URL url) throws RpcException {
if (REGISTRY_PROTOCOL.equals(url.getProtocol())) {
return protocol.refer(type, url); // 由於當前協議是registry,調用ProtocolListenerWrapper的 refer方法
}
return buildInvokerChain(protocol.refer(type, url), REFERENCE_FILTER_KEY, CommonConstants.CONSUMER);
}
// ProtocolListenerWrapper
@Override
public <T> Invoker<T> refer(Class<T> type, URL url) throws RpcException {
if (REGISTRY_PROTOCOL.equals(url.getProtocol())) {
return protocol.refer(type, url); // 由於當前協議是registry,調用 RegistryProtocol 的refer方法
}
return new ListenerInvokerWrapper<T>(protocol.refer(type, url),
Collections.unmodifiableList(
ExtensionLoader.getExtensionLoader(InvokerListener.class)
.getActivateExtension(url, INVOKER_LISTENER_KEY)));
}
② 接着通過ProtocolListenerWrapper調用真實的擴展實現RegistryProtocol 的refer方法:
// RegistryProtocol
@Override
@SuppressWarnings("unchecked")
public <T> Invoker<T> refer(Class<T> type, URL url) throws RpcException {
// 重新構建一個protocol爲 `zookeeper`的url,如果url沒有指定protocol的話,默認是 `dubbo`
url = URLBuilder.from(url)
.setProtocol(url.getParameter(REGISTRY_KEY, DEFAULT_REGISTRY))
.removeParameter(REGISTRY_KEY)
.build();
// 調用 `RegistryFactory擴展點`的自適應擴展實現,此處根據url中的protocol=zookeeper,會去找到真正的擴展點實現`ZookeeperRegistryFactory`,然後調用其getRegistry方法(實際上此方法繼承自父類 AbstractRegistryFactory)生成ZookeeperRegistry實例。
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(REFER_KEY));
String group = qs.get(GROUP_KEY);// 假設group爲空的情況
if (group != null && group.length() > 0) {
if ((COMMA_SPLIT_PATTERN.split(group)).length > 1 || "*".equals(group)) {
return doRefer(getMergeableCluster(), registry, type, url);// 不執行此處代碼
}
}
return doRefer(cluster, registry, type, url);// 通過集羣擴展點對註冊實例進行包裝
}
③ 調用 ZookeeperRegistryFactory 的 getRegistry方法(繼承自 AbstractRegistryFactory )獲取註冊實現:
// AbstractRegistryFactory
@Override
public Registry getRegistry(URL url) {
// 構建一個路徑爲RegistryService的註冊URL
url = URLBuilder.from(url)
.setPath(RegistryService.class.getName())
.addParameter(INTERFACE_KEY, RegistryService.class.getName())
.removeParameters(EXPORT_KEY, REFER_KEY)
.build();
String key = url.toServiceStringWithoutResolving();
// Lock the registry access process to ensure a single instance of the registry
LOCK.lock();
try {
Registry registry = REGISTRIES.get(key);// 從緩存中取,不等於空直接返回
if (registry != null) {
return registry;
}
//create registry by spi/ioc
registry = createRegistry(url); // 模板方法,通過具體的實現類(此處是ZookeeperRegistryFactory)去獲取註冊實例
if (registry == null) {
throw new IllegalStateException("Can not create registry " + url);
}
REGISTRIES.put(key, registry); // 獲取之後放入緩存中,防止併發場景下重複創建
return registry;
} finally {
// Release the lock
LOCK.unlock();
}
}
protected abstract Registry createRegistry(URL url); // 抽象模板方法
// ZookeeperRegistryFactory 的方法實現
@Override
public Registry createRegistry(URL url) {
// 創建註冊實例,註冊的url爲 zookeeper://xxx:2181/org.apache.dubbo.registry.RegistryService?application=dubbo-api-consumer&dubbo=XXX&interface=org.apache.dubbo.registry.RegistryService&pid=13740×tamp=1640015273548
return new ZookeeperRegistry(url, zookeeperTransporter);
}
// 構造器
public ZookeeperRegistry(URL url, ZookeeperTransporter zookeeperTransporter) {
super(url);
if (url.isAnyHost()) {
throw new IllegalStateException("registry address == null");
}
String group = url.getParameter(GROUP_KEY, DEFAULT_ROOT);
if (!group.startsWith(PATH_SEPARATOR)) {
group = PATH_SEPARATOR + group;
}
this.root = group;
zkClient = zookeeperTransporter.connect(url);// 連接zk server端
zkClient.addStateListener(state -> {
if (state == StateListener.RECONNECTED) {
try {
recover(); // 故障恢復
} catch (Exception e) {
logger.error(e.getMessage(), e);
}
}
});
}
// FailbackRegistry( ZookeeperRegistry的父類 )
@Override
protected void recover() throws Exception {
// register
Set<URL> recoverRegistered = new HashSet<URL>(getRegistered());// 獲取已經註冊的URL
if (!recoverRegistered.isEmpty()) {
if (logger.isInfoEnabled()) {
logger.info("Recover register url " + recoverRegistered);
}
for (URL url : recoverRegistered) {
addFailedRegistered(url);// 添加到失敗的列表中,並通過時間輪(HashedWheelTimer)定時重試
}
}
// subscribe
Map<URL, Set<NotifyListener>> recoverSubscribed = new HashMap<URL, Set<NotifyListener>>(getSubscribed()); // 獲取訂閱的URL跟對應的通知狀態變更的監聽器
if (!recoverSubscribed.isEmpty()) {
if (logger.isInfoEnabled()) {
logger.info("Recover subscribe url " + recoverSubscribed.keySet());
}
for (Map.Entry<URL, Set<NotifyListener>> entry : recoverSubscribed.entrySet()) {
URL url = entry.getKey();
for (NotifyListener listener : entry.getValue()) {
addFailedSubscribed(url, listener);// 添加到失敗訂閱列表中,並通過時間輪(HashedWheelTimer)定時重試
}
}
}
}
④ 調用 org.apache.dubbo.registry.integration.RegistryProtocol#doRefer方法,通過集羣擴展點Cluster
對本地註冊目錄進行合併:
private <T> Invoker<T> doRefer(Cluster cluster, Registry registry, Class<T> type, URL url) {
// 創建本地註冊目錄(RegistryDirectory本身就是一個org.apache.dubbo.registry.NotifyListener),當監聽到服務端地址變化時,更新本地url信息
RegistryDirectory<T> directory = new RegistryDirectory<T>(type, url);
directory.setRegistry(registry);
directory.setProtocol(protocol);
// all attributes of REFER_KEY
Map<String, String> parameters = new HashMap<String, String>(directory.getUrl().getParameters());
URL subscribeUrl = new URL(CONSUMER_PROTOCOL, parameters.remove(REGISTER_IP_KEY), 0, type.getName(), parameters);
if (!ANY_VALUE.equals(url.getServiceInterface()) && url.getParameter(REGISTER_KEY, true)) {
// 設置消費的URL
directory.setRegisteredConsumerUrl(getRegisteredConsumerUrl(subscribeUrl, url));
// 調用註冊實例的註冊方法,將消費的URL註冊到本地
registry.register(directory.getRegisteredConsumerUrl());
}
// 構建路由鏈 (根據優先級依次爲 MockRouterFactory、TagRouterFactor、AppRouterFactory、ServiceRouterFactory)
directory.buildRouterChain(subscribeUrl);
// 訂閱URL(consumer://127.0..1/com.alibaba.mp.alipay.AlipayInfoAbility?application=dubbo-demo-api-consumer&category=providers,configurators,routers&dubbo=2.0.2&interface=com.alibaba.mp.alipay.AlipayInfoAbility&lazy=false&methods=getAlipayInfo&pid=11856&side=consumer&sticky=false×tamp=1640020348831)
directory.subscribe(subscribeUrl.addParameter(CATEGORY_KEY,
PROVIDERS_CATEGORY + "," + CONFIGURATORS_CATEGORY + "," + ROUTERS_CATEGORY));
// 合併成一個virtual的Invoker
Invoker invoker = cluster.join(directory);
ProviderConsumerRegTable.registerConsumer(invoker, url, subscribeUrl, directory);
return invoker;
}
⑤ 註冊消費的URL、構建調用路由鏈、訂閱要消費的URL(RegistryDirectory中)
// FailbackRegistry 重寫了AbstractRegistry的register方法,增加將消費者URL註冊到服務端的的邏輯
@Override
public void register(URL url) {
// 緩存到本地
super.register(url);
removeFailedRegistered(url);
removeFailedUnregistered(url);
try {
// Sending a registration request to the server side
doRegister(url); // 模板方法,向服務端發送註冊請求
} catch (Exception e) {
Throwable t = e;
...
}
}
// ZookeeperRegistry
@Override
public void doRegister(URL url) {
try {
// 在zk上創建提供方URL的臨時節點(提供方目錄是臨時的)
zkClient.create(toUrlPath(url), url.getParameter(DYNAMIC_KEY, true));
} catch (Throwable e) {
...
}
}
// 構建路由鏈,用於後續從集羣中選擇路由時使用
public void buildRouterChain(URL url) {
this.setRouterChain(RouterChain.buildChain(url));
}
// RouterChain
public static <T> RouterChain<T> buildChain(URL url) {
return new RouterChain<>(url);
}
private RouterChain(URL url) {
// 自然序獲取RouterFactory所有的激活擴展點實現,(MockRouterFactory、TagRouterFactor、AppRouterFactory、ServiceRouterFactory)
List<RouterFactory> extensionFactories = ExtensionLoader.getExtensionLoader(RouterFactory.class)
.getActivateExtension(url, (String[]) null);
// 獲取路由集合
List<Router> routers = extensionFactories.stream()
.map(factory -> factory.getRouter(url))
.collect(Collectors.toList());
initWithRouters(routers);
}
// RegistryDirectory 的訂閱方法
public void subscribe(URL url) {
// 緩存到本地註冊目錄中
setConsumerUrl(url);
// 添加 節點變更監聽器用於通知服務上下線
CONSUMER_CONFIGURATION_LISTENER.addNotifyListener(this);
// 通知 configurator/router 變更
serviceConfigurationListener = new ReferenceConfigurationListener(this, url);
// 開始訂閱消費URL,每個對應一個註冊目錄,並註冊與遠端服務子節點之間的映射關係,當zk節點發生變化時會回調當前監聽器更新本地消費URL
registry.subscribe(url, this);
}
// FailbackRegistry,listener爲 RegistryDirectory
@Override
public void subscribe(URL url, NotifyListener listener) {
super.subscribe(url, listener);
removeFailedSubscribed(url, listener);
try {
// Sending a subscription request to the server side
doSubscribe(url, listener); // 模板方法,調用子類(ZookeeperRegistry)的訂閱方法
} catch (Exception e) {
Throwable t = e;
...
}
}
// ZookeeperRegistry 的模板實現方法
@Override
public void doSubscribe(final URL url, final NotifyListener listener) {
try {
if (ANY_VALUE.equals(url.getServiceInterface())) {
...
} else {
List<URL> urls = new ArrayList<>();
// toCategoriesPath:
// 0 = "/dubbo/com.alibaba.mp.alipay.AlipayInfoAbility/providers"
// 1 = "/dubbo/com.alibaba.mp.alipay.AlipayInfoAbility/configurators"
// 2 = "/dubbo/com.alibaba.mp.alipay.AlipayInfoAbility/routers"
for (String path : toCategoriesPath(url)) {
ConcurrentMap<NotifyListener, ChildListener> listeners = zkListeners.get(url);
if (listeners == null) {
zkListeners.putIfAbsent(url, new ConcurrentHashMap<>());
listeners = zkListeners.get(url);
}
ChildListener zkListener = listeners.get(listener);
if (zkListener == null) {
zkListener = listeners.get(listener);
// 創建zk子節點監聽器,用於監聽節點變化,然後調用notify()方法變更本地目錄
listeners.putIfAbsent(listener, (parentPath, currentChilds) -> ZookeeperRegistry.this.notify(url, listener, toUrlsWithEmpty(url, parentPath, currentChilds)));
}
// 創建持久化節點
zkClient.create(path, false);
List<String> children = zkClient.addChildListener(path, zkListener);
if (children != null) {
urls.addAll(toUrlsWithEmpty(url, path, children));
}
}
// 通知RegistryDirectory監聽器,修改本地URL
notify(url, listener, urls);
}
} catch (Throwable e) {
...
}
}
// FailbackRegistry
@Override
protected void notify(URL url, NotifyListener listener, List<URL> urls) {
if (url == null) {
throw new IllegalArgumentException("notify url == null");
}
if (listener == null) {
throw new IllegalArgumentException("notify listener == null");
}
try {
doNotify(url, listener, urls); // 模板方法,ZookeeperRegistry 實現通知變更
} catch (Exception t) {
...
}
}
// AbstractRegistry
protected void notify(URL url, NotifyListener listener, List<URL> urls) {
if (url == null) {
throw new IllegalArgumentException("notify url == null");
}
if (listener == null) {
throw new IllegalArgumentException("notify listener == null");
}
if ((CollectionUtils.isEmpty(urls))
&& !ANY_VALUE.equals(url.getServiceInterface())) {
logger.warn("Ignore empty notify urls for subscribe url " + url);
return;
}
if (logger.isInfoEnabled()) {
logger.info("Notify urls for subscribe url " + url + ", urls: " + urls);
}
// keep every provider's category.
Map<String, List<URL>> result = new HashMap<>();
for (URL u : urls) {
if (UrlUtils.isMatch(url, u)) {
String category = u.getParameter(CATEGORY_KEY, DEFAULT_CATEGORY);
List<URL> categoryList = result.computeIfAbsent(category, k -> new ArrayList<>());
categoryList.add(u);
}
}
if (result.size() == 0) {
return;
}
// 將提供方的URL放入到已通知的對象映射中
Map<String, List<URL>> categoryNotified = notified.computeIfAbsent(url, u -> new ConcurrentHashMap<>());
for (Map.Entry<String, List<URL>> entry : result.entrySet()) {
String category = entry.getKey();
List<URL> categoryList = entry.getValue();
categoryNotified.put(category, categoryList);
// 調用監聽器通知變更,listener = RegistryDirectory
listener.notify(categoryList);
// We will update our cache file after each notification.
// When our Registry has a subscribe failure due to network jitter, we can return at least the existing cache URL.
saveProperties(url);
}
}
// RegistryDirectory,更新本地對應的目錄
@Override
public synchronized void notify(List<URL> urls) {
// 根據 configurators、routers、providers進行分組
Map<String, List<URL>> categoryUrls = urls.stream()
.filter(Objects::nonNull)
.filter(this::isValidCategory)
.filter(this::isNotCompatibleFor26x)
.collect(Collectors.groupingBy(url -> {
if (UrlUtils.isConfigurator(url)) {
return CONFIGURATORS_CATEGORY;
} else if (UrlUtils.isRoute(url)) {
return ROUTERS_CATEGORY;
} else if (UrlUtils.isProvider(url)) {
return PROVIDERS_CATEGORY;
}
return "";
}));
// 若監聽到category有變更,則取代之前的值
List<URL> configuratorURLs = categoryUrls.getOrDefault(CONFIGURATORS_CATEGORY, Collections.emptyList());
this.configurators = Configurator.toConfigurators(configuratorURLs).orElse(this.configurators);
// 若監聽到有新得路由,則添加到路由鏈中
List<URL> routerURLs = categoryUrls.getOrDefault(ROUTERS_CATEGORY, Collections.emptyList());
toRouters(routerURLs).ifPresent(this::addRouters);
// providers
List<URL> providerURLs = categoryUrls.getOrDefault(PROVIDERS_CATEGORY, Collections.emptyList());
// 刷新+覆蓋
refreshOverrideAndInvoker(providerURLs);
}
private void refreshOverrideAndInvoker(List<URL> urls) {
// mock zookeeper://xxx?mock=return null
overrideDirectoryUrl();
// 刷新本地緩存的Invokers
refreshInvoker(urls);
}
private void refreshInvoker(List<URL> invokerUrls) {
Assert.notNull(invokerUrls, "invokerUrls should not be null");
if (invokerUrls.size() == 1
&& invokerUrls.get(0) != null
&& EMPTY_PROTOCOL.equals(invokerUrls.get(0).getProtocol())) {
this.forbidden = true; // Forbid to access
this.invokers = Collections.emptyList();
routerChain.setInvokers(this.invokers);
destroyAllInvokers(); // Close all invokers
} else {
this.forbidden = false; // Allow to access
Map<String, Invoker<T>> oldUrlInvokerMap = this.urlInvokerMap; // local reference
if (invokerUrls == Collections.<URL>emptyList()) {
invokerUrls = new ArrayList<>();
}
if (invokerUrls.isEmpty() && this.cachedInvokerUrls != null) {
invokerUrls.addAll(this.cachedInvokerUrls);
} else {
this.cachedInvokerUrls = new HashSet<>();
this.cachedInvokerUrls.addAll(invokerUrls);//Cached invoker urls, convenient for comparison
}
if (invokerUrls.isEmpty()) {
return;
}
Map<String, Invoker<T>> newUrlInvokerMap = toInvokers(invokerUrls);// Translate url list to Invoker map
/**
* If the calculation is wrong, it is not processed.
*
* 1. The protocol configured by the client is inconsistent with the protocol of the server.
* eg: consumer protocol = dubbo, provider only has other protocol services(rest).
* 2. The registration center is not robust and pushes illegal specification data.
*
*/
if (CollectionUtils.isEmptyMap(newUrlInvokerMap)) {
logger.error(new IllegalStateException("urls to invokers error .invokerUrls.size :" + invokerUrls.size() + ", invoker.size :0. urls :" + invokerUrls
.toString()));
return;
}
List<Invoker<T>> newInvokers = Collections.unmodifiableList(new ArrayList<>(newUrlInvokerMap.values()));
// pre-route and build cache, notice that route cache should build on original Invoker list.
// toMergeMethodInvokerMap() will wrap some invokers having different groups, those wrapped invokers not should be routed.
routerChain.setInvokers(newInvokers);
// 覆蓋本地緩存,調用的時候就是從此成員變量中選取的
this.invokers = multiGroup ? toMergeInvokerList(newInvokers) : newInvokers;
this.urlInvokerMap = newUrlInvokerMap;
try {
destroyUnusedInvokers(oldUrlInvokerMap, newUrlInvokerMap); // Close the unused Invoker
} catch (Exception e) {
logger.warn("destroyUnusedInvokers error. ", e);
}
}
}
private Map<String, Invoker<T>> toInvokers(List<URL> urls) {
Map<String, Invoker<T>> newUrlInvokerMap = new HashMap<>();
if (urls == null || urls.isEmpty()) {
return newUrlInvokerMap;
}
Set<String> keys = new HashSet<>();
String queryProtocols = this.queryMap.get(PROTOCOL_KEY);
for (URL providerUrl : urls) {
// If protocol is configured at the reference side, only the matching protocol is selected
if (queryProtocols != null && queryProtocols.length() > 0) {
boolean accept = false;
String[] acceptProtocols = queryProtocols.split(",");
for (String acceptProtocol : acceptProtocols) {
if (providerUrl.getProtocol().equals(acceptProtocol)) {
accept = true;
break;
}
}
if (!accept) {
continue;
}
}
if (EMPTY_PROTOCOL.equals(providerUrl.getProtocol())) {
continue;
}
// protocol = dubbo
if (!ExtensionLoader.getExtensionLoader(Protocol.class).hasExtension(providerUrl.getProtocol())) {
logger.error(new IllegalStateException("Unsupported protocol " + providerUrl.getProtocol() +
" in notified url: " + providerUrl + " from registry " + getUrl().getAddress() +
" to consumer " + NetUtils.getLocalHost() + ", supported protocol: " +
ExtensionLoader.getExtensionLoader(Protocol.class).getSupportedExtensions()));
continue;
}
URL url = mergeUrl(providerUrl);
String key = url.toFullString(); // The parameter urls are sorted
if (keys.contains(key)) { // Repeated url
continue;
}
keys.add(key);
// Cache key is url that does not merge with consumer side parameters, regardless of how the consumer combines parameters, if the server url changes, then refer again
Map<String, Invoker<T>> localUrlInvokerMap = this.urlInvokerMap; // local reference
Invoker<T> invoker = localUrlInvokerMap == null ? null : localUrlInvokerMap.get(key);
if (invoker == null) { // Not in the cache, refer again
try {
boolean enabled = true;
if (url.hasParameter(DISABLED_KEY)) {
enabled = !url.getParameter(DISABLED_KEY, false);
} else {
enabled = url.getParameter(ENABLED_KEY, true);
}
// 默認情況下,enabled = true
if (enabled) {
// 根據當前key創建一個委派Invoker:InvokerDelegate。
// 重點:而此處url的protocol = dubbo,那麼 protocol.refer(serviceType, url)會執行 Dubboprotocol的refer方法
// 由於此處的 protocol引用是自適應擴展點,所以還是會經過ProtocolFilterWrapper跟ProtocolListenerWrapper進行包裝:ProtocolFilterWrapper.refer -> ProtocolListenerWrapper.refer -> Dubboprotocol.refer
invoker = new InvokerDelegate<>(protocol.refer(serviceType, url), url, providerUrl);
}
} catch (Throwable t) {
logger.error("Failed to refer invoker for interface:" + serviceType + ",url:(" + url + ")" + t.getMessage(), t);
}
if (invoker != null) { // Put new invoker in cache
newUrlInvokerMap.put(key, invoker);
}
} else {
newUrlInvokerMap.put(key, invoker);
}
}
keys.clear();
return newUrlInvokerMap;
}
// ProtocolFilterWrapper 的refer方法
@Override
public <T> Invoker<T> refer(Class<T> type, URL url) throws RpcException {
if (REGISTRY_PROTOCOL.equals(url.getProtocol())) {// 協議是dubbo,此處不成立
return protocol.refer(type, url);
}
// 調用 ProtocolListenerWrapper 的refer方法先生成一個Invoker
// 將生成的Invoker與Filter擴展點構建消費端的Invoker鏈,REFERENCE_FILTER_KEY = reference.filter,
// 經過返回 ProtocolFilterWrapper 之後,生成的是 CallbackRegistrationInvoker
return buildInvokerChain(protocol.refer(type, url), REFERENCE_FILTER_KEY, CommonConstants.CONSUMER);
}
// 構建Filter鏈
private static <T> Invoker<T> buildInvokerChain(final Invoker<T> invoker, String key, String group) {
// 此處的 invoker是ProtocolListenerWrapper.refer執行之後的Invoker,即 ListenerInvokerWrapper
Invoker<T> last = invoker;
// 獲取消費者端的所有滿足條件的激活擴展點,此處會根據排序字段升序排列
// ConsumerContextFilter -> FutureFilter -> MonitorFilter
List<Filter> filters = ExtensionLoader.getExtensionLoader(Filter.class).getActivateExtension(invoker.getUrl(), key, group);
if (!filters.isEmpty()) {
// 循環從後往前組成Invoker鏈,爲了調用時按照設定的順序執行
// 循環順序:MonitorFilter -> FutureFilter -> ConsumerContextFilter
// 請求處理順序:ConsumerContextFilter -> FutureFilter -> MonitorFilter
for (int i = filters.size() - 1; i >= 0; i--) {
final Filter filter = filters.get(i);
final Invoker<T> next = last;
last = new Invoker<T>() {
...
...
@Override
public Result invoke(Invocation invocation) throws RpcException {
Result asyncResult;
try {
asyncResult = filter.invoke(next, invocation);
} catch (Exception e) {
// onError callback
if (filter instanceof ListenableFilter) {
Filter.Listener listener = ((ListenableFilter) filter).listener();
if (listener != null) {
// 鉤子方法
listener.onError(e, invoker, invocation);
}
}
throw e;
}
return asyncResult;
}
@Override
public void destroy() {
invoker.destroy();
}
@Override
public String toString() {
return invoker.toString();
}
};
}
}
// 創建一個回調的 Invoker,如果定義的Filter是 ListenableFilter的實現類
// 那麼調用返回時,會回調Filter.Listener的 onResponse(正常返回) 和 onError方法(異常情況)
return new CallbackRegistrationInvoker<>(last, filters);
}
// ProtocolListenerWrapper的refer方法
@Override
public <T> Invoker<T> refer(Class<T> type, URL url) throws RpcException {
if (REGISTRY_PROTOCOL.equals(url.getProtocol())) {// protocol = dubbo,此處不成立
return protocol.refer(type, url);
}
// 此處的 protocol是真實的 DubboProtocol
// 調用 DubboProtocol的refer 之後生成 DubboInvoker
// 獲取 InvokerListener的激活擴展點實現,若是存在,則調用擴展實現的 referred(Invoker<?> invoker)方法,(類似Spring中的BeanPostProcessor)
// 返回 ListenerInvokerWrapper
return new ListenerInvokerWrapper<T>(protocol.refer(type, url),
Collections.unmodifiableList(
ExtensionLoader.getExtensionLoader(InvokerListener.class)
.getActivateExtension(url, INVOKER_LISTENER_KEY)));
}
// AbstractProtocol (DubboProtocol的父類)
@Override
public <T> Invoker<T> refer(Class<T> type, URL url) throws RpcException {
// protocolBindingRefer 方法是個抽象方法,讓其子類去實現
return new AsyncToSyncInvoker<>(protocolBindingRefer(type, url));
}
// DubboProtocol
@Override
public <T> Invoker<T> protocolBindingRefer(Class<T> serviceType, URL url) throws RpcException {
optimizeSerialization(url);
// create rpc invoker.
DubboInvoker<T> invoker = new DubboInvoker<T>(serviceType, url, getClients(url), invokers);
invokers.add(invoker);
return invoker;
}
private ExchangeClient[] getClients(URL url) {
// whether to share connection
boolean useShareConnect = false;
int connections = url.getParameter(CONNECTIONS_KEY, 0);
List<ReferenceCountExchangeClient> shareClients = null;
// if not configured, connection is shared, otherwise, one connection for one service
// 沒有配置 connections 參數的話,默認使用共享連接(一個服務公用一個連接)
if (connections == 0) {
useShareConnect = true;
/**
* The xml configuration should have a higher priority than properties.
*/
String shareConnectionsStr = url.getParameter(SHARE_CONNECTIONS_KEY, (String) null);
connections = Integer.parseInt(StringUtils.isBlank(shareConnectionsStr) ? ConfigUtils.getProperty(SHARE_CONNECTIONS_KEY,
DEFAULT_SHARE_CONNECTIONS) : shareConnectionsStr);
// 獲取共享客戶端連接
shareClients = getSharedClient(url, connections);
}
// 根據連接數創建請求客戶端數組(ReferenceCountExchangeClient)
ExchangeClient[] clients = new ExchangeClient[connections];
for (int i = 0; i < clients.length; i++) {
// 使用共享連接
if (useShareConnect) {
clients[i] = shareClients.get(i); // shareconnections 優先級高於 connections
} else {
clients[i] = initClient(url); // 初始化客戶端連接 HeaderExchangeClient
}
}
return clients;
}
private ReferenceCountExchangeClient buildReferenceCountExchangeClient(URL url) {
// 檢查完緩存中沒有的話,也會調用此方法去創建客戶端連接
ExchangeClient exchangeClient = initClient(url);
return new ReferenceCountExchangeClient(exchangeClient);
}
private ExchangeClient initClient(URL url) {
// client type setting.
String str = url.getParameter(CLIENT_KEY, url.getParameter(SERVER_KEY, DEFAULT_REMOTING_CLIENT));
// 設置默認的編解碼爲 codec = dubbo(DubboCodec)
url = url.addParameter(CODEC_KEY, DubboCodec.NAME);
// enable heartbeat by default(1分鐘)
url = url.addParameterIfAbsent(HEARTBEAT_KEY, String.valueOf(DEFAULT_HEARTBEAT));
// BIO is not allowed since it has severe performance issue.
if (str != null && str.length() > 0 && !ExtensionLoader.getExtensionLoader(Transporter.class).hasExtension(str)) {
throw new RpcException("Unsupported client type: " + str + "," +
" supported client type is " + StringUtils.join(ExtensionLoader.getExtensionLoader(Transporter.class).getSupportedExtensions(), " "));
}
ExchangeClient client;
try {
// connection should be lazy
if (url.getParameter(LAZY_CONNECT_KEY, false)) {
client = new LazyConnectExchangeClient(url, requestHandler);
} else {
// 通過交換層 Exchangers工具類 生成客戶端
client = Exchangers.connect(url, requestHandler);
}
} catch (RemotingException e) {
throw new RpcException("Fail to create remoting client for service(" + url + "): " + e.getMessage(), e);
}
return client;
}
// 中間的代碼就不一一粘貼了,下面來看一下創建NettyClient時關鍵性的一步:
/**
* The constructor of NettyClient.
* It wil init and start netty.
*/
public NettyClient(final URL url, final ChannelHandler handler) throws RemotingException {
// 調用父類構造器
super(url, wrapChannelHandler(url, handler));
}
// AbstractClient 的構造器,有幾處模板方法:doOpen()、connect()、close()
public AbstractClient(URL url, ChannelHandler handler) throws RemotingException {
super(url, handler);
needReconnect = url.getParameter(Constants.SEND_RECONNECT_KEY, false);
try {
// 打開連接通道
doOpen();
} catch (Throwable t) {
close();
...
}
try {
// connect.
connect();
...
}
} catch (RemotingException t) {
if (url.getParameter(Constants.CHECK_KEY, true)) {
close(); // 異常關閉連接
throw t;
} else {
...
}
} catch (Throwable t) {
close(); // 異常關閉連接
...
}
...
}
// NettyClient(注意此處說的這個類都是Dubbo自己的,Netty裏面可沒有叫這玩意的)抽象實現
@Override
protected void doOpen() throws Throwable {
// 創建一個統一出入站處理器(爲啥是出入? 因爲實現了 io.netty.channel.ChannelDuplexHandler)
final NettyClientHandler nettyClientHandler = new NettyClientHandler(getUrl(), this);
// 創建客戶端啓動類
bootstrap = new Bootstrap();
// nioEventLoopGroup 是一個線程數爲 CPU+1的 NioEventLoopGroup(事件循環組,可以當成線程池)
bootstrap.group(nioEventLoopGroup)
.option(ChannelOption.SO_KEEPALIVE, true)
.option(ChannelOption.TCP_NODELAY, true)
.option(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT)
//.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, getTimeout())
// io.netty.channel.socket.nio.NioSocketChannel中會緩存一個 SocketChannel 用於與服務端進行通信
.channel(NioSocketChannel.class);
bootstrap.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, Math.max(3000, getConnectTimeout()));
bootstrap.handler(new ChannelInitializer() {
@Override
protected void initChannel(Channel ch) throws Exception {
int heartbeatInterval = UrlUtils.getHeartbeat(getUrl());
// 自定義編解碼器(粘包拆包)
NettyCodecAdapter adapter = new NettyCodecAdapter(getCodec(), getUrl(), NettyClient.this);
ch.pipeline()
// 可以參考 io.netty.channel.ChannelPipeline 中的註釋說明
// 讀:decoder -> client-idle-handler -> handler
// 寫:handler -> client-idle-handler -> encoder
.addLast("decoder", adapter.getDecoder())
.addLast("encoder", adapter.getEncoder())
.addLast("client-idle-handler", new IdleStateHandler(heartbeatInterval, 0, 0, MILLISECONDS))
.addLast("handler", nettyClientHandler);
String socksProxyHost = ConfigUtils.getProperty(SOCKS_PROXY_HOST);
if(socksProxyHost != null) {
int socksProxyPort = Integer.parseInt(ConfigUtils.getProperty(SOCKS_PROXY_PORT, DEFAULT_SOCKS_PROXY_PORT));
Socks5ProxyHandler socks5ProxyHandler = new Socks5ProxyHandler(new InetSocketAddress(socksProxyHost, socksProxyPort));
ch.pipeline().addFirst(socks5ProxyHandler);
}
}
});
}
// NettyClient 抽象實現
@Override
protected void doConnect() throws Throwable {
long start = System.currentTimeMillis();
// 通過調用netty 封裝好的客戶端與服務端進行連接,此連接會立馬返回一個 ChannelFuture(DefaultChannelPromise)。
// 類似於 JUC 中的 Future
ChannelFuture future = bootstrap.connect(getConnectAddress());
try {
// 根據超時時間不中斷獲取連接結果
boolean ret = future.awaitUninterruptibly(getConnectTimeout(), MILLISECONDS);
// 連接成功
if (ret && future.isSuccess()) {
Channel newChannel = future.channel();
try {
// Close old channel
// copy reference
Channel oldChannel = NettyClient.this.channel;
if (oldChannel != null) {
try {
if (logger.isInfoEnabled()) {
logger.info("Close old netty channel " + oldChannel + " on create new netty channel " + newChannel);
}
oldChannel.close();
} finally {
NettyChannel.removeChannelIfDisconnected(oldChannel);
}
}
} finally {
if (NettyClient.this.isClosed()) {
try {
if (logger.isInfoEnabled()) {
logger.info("Close new netty channel " + newChannel + ", because the client closed.");
}
newChannel.close();
} finally {
NettyClient.this.channel = null;
NettyChannel.removeChannelIfDisconnected(newChannel);
}
} else {
NettyClient.this.channel = newChannel;
}
}
} else if (future.cause() != null) {
...
} finally {
// just add new valid channel to NettyChannel's cache
if (!isConnected()) {
//future.cancel(true);
}
}
}
關鍵流程:
- Invoker鏈創建過程:
① DubboInvoker -> 真正發送請求的Invoker
② AsyncToSyncInvoker(DubboInvoker ** ) ->
③ ListenerInvokerWrapper( AsyncToSyncInvoker ) ->
④ CallbackRegistrationInvoker( ListenerInvokerWrapper ) -> Filter擴展點鉤子回調
⑤ InvokerDelegate(CallbackRegistrationInvoker) ->
⑥ FailoverClusterInvoker( RegistryDirectory(InvokerDelegates) ) -> RegistryDirectory 中存放調用時的Invokes
⑦ MockClusterInvoker (RegistryDirectory,FailoverClusterInvoker**) 暴漏給接口攔截的入口Invoker
- PS:上面是創建順序,調用時自然是相反的順序。那麼如何方便記憶呢 ?
Ⅰ 最後一個Invoker,實際發出網絡請求的Invoker:DubboInvoker,鐵打不變 !!!
Ⅱ 由於 DubboInvoker 是異步返回,若是客戶端指定同步調用(默認同步),則需要在AsyncToSyncInvoker(最靠近DubboInvoker)
Ⅲ CallbackRegistrationInvoker,對返回結果的第一層處理。通過定義的Filter類型決定是否調用 onResponse方法 或者 onError方法
Ⅳ Mock優先
Ⅴ 高可用高於任何其他的執行Invoker( 故障轉移:FailoverClusterInvoker )
- Netty客戶端創建過程:
org.apache.dubbo.rpc.protocol.dubbo.DubboProtocol#requestHandler -> 真正處理消息的收發處理器
HeaderExchangeHandler(requestHandler) ->
DecodeHandler(HeaderExchangeHandler) -> 自定義編解碼處理器(拆包、粘包)
AllChannelHandler(DecodeHandler,URL) -> 請求分發處理器,默認所有請求都轉發
HeartbeatHandler(AllChannelHandler) ->
MultiMessageHandler (HeartbeatHandler) -> 消息類型是MultiMessage
,不過一般的請求都是Request
類型
NettyClient(Url,MultiMessageHandler) -> 封裝了一系列打開、鏈接通道邏輯
HeaderExchangeClient(NettyClient,true) ->
ReferenceCountExchangeClient(HeaderExchangeClient) 共享連接纔會創建 ReferenceCountExchangeClient,暴漏給客戶端使用的Client
8這一步是通過如下兩行代碼:
// 設置cluster = registryaware( RegistryAwareCluster )
URL u = registryURL.addParameter(CLUSTER_KEY, RegistryAwareCluster.NAME);
// 通過集羣的自適應擴展點將多個Invokers( MockClusterInvoker 集合)合併成一個Invoker(RegistryAwareClusterInvoker是用來做集羣路由,選擇一個實際調用的Invoker),此處RegistryAwareClusterInvoker是用來做集羣路由,選擇一個實際調用的Invoker
invoker = CLUSTER.join(new StaticDirectory(u, invokers))
到這裏,大家千萬不要被 "滿眼的Invoker" 給繞暈了,仔細分析一下Dubbo的執行鏈與模塊分層是可以理清楚的。
接着上面 執行鏈,此處會先包裝一個StaticDirectory,然後 :
MockClusterWrapper(MockClusterInvoker )-> RegistryAwareCluster(RegistryAwareClusterInvoker, 最終返回的Invoker)-> StaticDirectory (URL , List<MockClusterInvoker>)
StaticDirectory:通過前面設置的Router鏈來執行路由選擇,會選擇最後一個Router所路由到的List<Invoker>集合上
LoadBalance:負載均衡器(默認random,隨機),從前面選擇到的List<Invoker>集合上,根據負載均衡算法選擇一個
( 路由跟負載均衡都是調用invoke方法時纔會用到,我們初始化過程分析完之後,調用過程自然就清晰了 )
9步驟有以下核心子流程:
① 接着就開始創建代理引用了: 通過調用 ProxyFactory
擴展點的適配類的getProxy方法獲取代理對象:
// ProxyFactory$Adaptive.getProxy(invoker) -> 找到默認的擴展點名稱(javassist)-> JavassistProxyFactory(實際是父類 AbstractProxyFactory的 getProxy方法)
return (T) PROXY_FACTORY.getProxy(invoker)
// AbstractProxyFactory
@Override
public <T> T getProxy(Invoker<T> invoker) throws RpcException {
// 非泛化方式獲取接口代理對象
return getProxy(invoker, false);
}
// JavassistProxyFactory
@Override
@SuppressWarnings("unchecked")
public <T> T getProxy(Invoker<T> invoker, Class<?>[] interfaces) {
// 此處的創建代理的方式類似於 JDK動態代理,不過生成的class是藉助與 javassist 字節碼工具自定義的
// 而且生成的代理類繼承了org.apache.dubbo.common.bytecode.Proxy
return (T) Proxy.getProxy(interfaces).newInstance(new InvokerInvocationHandler(invoker));
}
② 若不指定 interfaces這個key的話,生成的代理類會同時實現interface
跟 EchoService(用於回聲測試)
接口,具體代碼 Proxy.getProxy(interfaces).newInstance(new InvokerInvocationHandler(invoker))
。其中 Proxy.getProxy(interfaces) 會爲接口生成一個代理類,代理繼承了 org.apache.dubbo.common.bytecode.Proxy
抽象類並且實現了 interfaces
。生成的代理類如下:
public class com.alibaba.dubbo.common.bytecode.proxy0 extends com.alibaba.dubbo.common.bytecode.Proxy implements com.alibaba.dubbo.common.bytecode.ClassGenerator$DC, com.alibaba.dubbo.rpc.service.EchoService, com.alibaba.mp.alipay.AlipayInfoAbility {
public static java.lang.reflect.Method[] methods;
private java.lang.reflect.InvocationHandler handler;
public (java.lang.reflect.InvocationHandler arg0){
handler = $1;
}
// 代理的接口方法
public com.alibaba.soa.common.response.SoaResponse getAlipayInfo(GetAlipayInfoRequest arg0){
Object[] args = new Object[1];
args[0] = ($w)$1;
// 調用handler的目標方法,此處是 InvokerInvocationHandler
Object ret = handler.invoke(this, methods[0], args);
return (com.alibaba.soa.common.response.SoaResponse)ret;
}
// 默認都會實現 EchoSerice接口,用於回聲測試
public java.lang.Object $echo(java.lang.Object arg0) {
Object[] args = new Object[1];
args[0] = ($w)$1;
Object ret = handler.invoke(this, methods[3], args);
return (java.lang.Object)ret;
}
// 創建代理對象
public Object newInstance(java.lang.reflect.InvocationHandler h) {
return new com.alibaba.dubbo.common.bytecode.proxy0($1);
}
③ 看了上述動態生成代理類的過程,再來看 Proxy.getProxy(interfaces).newInstance(new InvokerInvocationHandler(invoker))
這個操作就會很清晰了。拿上面的實現方法來看,當調用ReferenceConcig.get()
獲取的對象就是Proxy0
。調用接口的getAlipayInfo
方法,實際上調用的是 Proxy0
的 getAlipayInfo
方法,此代理中,會調用handler的 invoke方法,再來看一下 InvokerInvocationHandler
中的實現邏輯。
④ 正常情況下,我們使用JDK動態代理的時候纔會實現java.lang.reflect.InvocationHandler
,此處完全可以不實現InvocationHandler
。但是由於代理的本質是相同的,作者爲了代碼邏輯複用,統一處理了。(PS: 最後這句是我猜的 😁
)
public class InvokerInvocationHandler implements InvocationHandler {
private final Invoker<?> invoker; // RegistryAwareClusterInvoker
public InvokerInvocationHandler(Invoker<?> handler) {
this.invoker = handler;
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
String methodName = method.getName();
Class<?>[] parameterTypes = method.getParameterTypes();
if (method.getDeclaringClass() == Object.class) {
return method.invoke(invoker, args);
}
if ("toString".equals(methodName) && parameterTypes.length == 0) {
return invoker.toString();
}
if ("hashCode".equals(methodName) && parameterTypes.length == 0) {
return invoker.hashCode();
}
if ("equals".equals(methodName) && parameterTypes.length == 1) {
return invoker.equals(args[0]);
}
// 此處終於開始調用了。。。
return invoker.invoke(new RpcInvocation(method, args)).recreate();
}
}
- 到此,客戶端的初始化Invoker過程已經完畢,後面就是真正開始調用的過程,下一篇我們來分析客戶端的調用過程與Netty底層源碼解讀。
- ☛ 文章要是勘誤或者知識點說的不正確,歡迎評論與交流, 希望學習的道路不孤獨。由於文章是作者通過閱讀源碼獲得的知識,難免會有疏忽!
- ☛ 要是感覺文章對你有所幫助,不妨點個關注,或者移駕看一下作者的其他文集,也都是幹活多多哦,文章也在全力更新中。
- ☛ 著作權歸作者所有。商業轉載請聯繫作者獲得授權,非商業轉載請註明出處!