dubbo消費者原始碼分析
根據上面的圖先總結一下dubbo消費者初始化和呼叫的流程
1.當消費者端呼叫getBean獲取服務提供者的例項的時候,dubbo的消費者初始化就開始了;
2.RefrenceConfig首先會獲取到消費者端的配置資訊(主要包括註冊中心資訊和呼叫的遠端服務資訊),載入這些資訊並檢查這些資訊。如果配置了多個註冊中心,遍歷註冊資訊,將註冊資訊和要呼叫的遠端介面資訊交由Protocol處理;
3.在消費者端Protocol的作用是由RegistryProtocol將消費者資訊註冊到註冊中心,開啟監聽註冊中心的模式和訂閱服務者的資訊,再由DubboProtocol獲取到服務者資訊後連線服務端,並生成相應的Invoke;
4.Invoker負責傳送請求和獲取處理結果。Invoke還會交由cluster封裝,cluster將多個Invoker生成一個Invoker,並且還負責負載均衡和容錯機制;
5.將生成好的Invoker交給ProxyFactory生成代理物件,代理物件實現了遠端的介面,並能呼叫代理處理器InvokerInvocationHandler,InvokerInvocationHandler再呼叫Invoker處理相關的請求。
下面是原始碼分析
消費者的配置如下
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://code.alibabatech.com/schema/dubbo
http://code.alibabatech.com/schema/dubbo/dubbo.xsd ">
<!-- 消費方應用名,用於計算依賴關係,不是匹配條件,不要與提供方一樣 -->
<dubbo:application name="consumer" />
<!-- 使用multicast廣播註冊中心暴露發現服務地址 -->
<dubbo:registry protocol="zookeeper" address="zookeeper://127.0.0.1:2181" />
<!-- 生成遠端服務代理,可以和本地bean一樣使用demoService -->
<dubbo:reference id="testService" interface="com.ts.service.TestService" timeout="30"/>
</beans>
spring會使用parseCustomElement解析dubbo標籤,使用的解析器是dubbo的DubboBeanDefinitionParser,解析完成之後返回ReferenceBean給Spring管理,當獲取testService這個例項的時候,ReferenceBean會被初始化,其實啟動dubbo就是ReferenceBean被初始化的過程。
/**
* ReferenceFactoryBean
*
* @author william.liangf
* @export
*/
public class ReferenceBean<T> extends ReferenceConfig<T> implements FactoryBean, ApplicationContextAware, InitializingBean, DisposableBean {
private static final long serialVersionUID = 213195494150089726L;
private transient ApplicationContext applicationContext;
public ReferenceBean() {
super();
}
public ReferenceBean(Reference reference) {
super(reference);
}
public void setApplicationContext(ApplicationContext applicationContext) {
this.applicationContext = applicationContext;
SpringExtensionFactory.addApplicationContext(applicationContext);
}
//重點是這個方法,重寫了FactoryBean方法,當呼叫getBean方法返回例項的時候,其實就是這個方法的返回值
public Object getObject() throws Exception {
return get();
}
public Class<?> getObjectType() {
return getInterfaceClass();
}
@Parameter(excluded = true)
public boolean isSingleton() {
return true;
}
@SuppressWarnings({ "unchecked"})
public void afterPropertiesSet() throws Exception {
if (getConsumer() == null) {
Map<String, ConsumerConfig> consumerConfigMap = applicationContext == null ? null : BeanFactoryUtils.beansOfTypeIncludingAncestors(applicationContext, ConsumerConfig.class, false, false);
if (consumerConfigMap != null && consumerConfigMap.size() > 0) {
ConsumerConfig consumerConfig = null;
for (ConsumerConfig config : consumerConfigMap.values()) {
if (config.isDefault() == null || config.isDefault().booleanValue()) {
if (consumerConfig != null) {
throw new IllegalStateException("Duplicate consumer configs: " + consumerConfig + " and " + config);
}
consumerConfig = config;
}
}
if (consumerConfig != null) {
setConsumer(consumerConfig);
}
}
}
if (getApplication() == null
&& (getConsumer() == null || getConsumer().getApplication() == null)) {
Map<String, ApplicationConfig> applicationConfigMap = applicationContext == null ? null : BeanFactoryUtils.beansOfTypeIncludingAncestors(applicationContext, ApplicationConfig.class, false, false);
if (applicationConfigMap != null && applicationConfigMap.size() > 0) {
ApplicationConfig applicationConfig = null;
for (ApplicationConfig config : applicationConfigMap.values()) {
if (config.isDefault() == null || config.isDefault().booleanValue()) {
if (applicationConfig != null) {
throw new IllegalStateException("Duplicate application configs: " + applicationConfig + " and " + config);
}
applicationConfig = config;
}
}
if (applicationConfig != null) {
setApplication(applicationConfig);
}
}
}
if (getModule() == null
&& (getConsumer() == null || getConsumer().getModule() == null)) {
Map<String, ModuleConfig> moduleConfigMap = applicationContext == null ? null : BeanFactoryUtils.beansOfTypeIncludingAncestors(applicationContext, ModuleConfig.class, false, false);
if (moduleConfigMap != null && moduleConfigMap.size() > 0) {
ModuleConfig moduleConfig = null;
for (ModuleConfig config : moduleConfigMap.values()) {
if (config.isDefault() == null || config.isDefault().booleanValue()) {
if (moduleConfig != null) {
throw new IllegalStateException("Duplicate module configs: " + moduleConfig + " and " + config);
}
moduleConfig = config;
}
}
if (moduleConfig != null) {
setModule(moduleConfig);
}
}
}
if ((getRegistries() == null || getRegistries().size() == 0)
&& (getConsumer() == null || getConsumer().getRegistries() == null || getConsumer().getRegistries().size() == 0)
&& (getApplication() == null || getApplication().getRegistries() == null || getApplication().getRegistries().size() == 0)) {
Map<String, RegistryConfig> registryConfigMap = applicationContext == null ? null : BeanFactoryUtils.beansOfTypeIncludingAncestors(applicationContext, RegistryConfig.class, false, false);
if (registryConfigMap != null && registryConfigMap.size() > 0) {
List<RegistryConfig> registryConfigs = new ArrayList<RegistryConfig>();
for (RegistryConfig config : registryConfigMap.values()) {
if (config.isDefault() == null || config.isDefault().booleanValue()) {
registryConfigs.add(config);
}
}
if (registryConfigs != null && registryConfigs.size() > 0) {
super.setRegistries(registryConfigs);
}
}
}
if (getMonitor() == null
&& (getConsumer() == null || getConsumer().getMonitor() == null)
&& (getApplication() == null || getApplication().getMonitor() == null)) {
Map<String, MonitorConfig> monitorConfigMap = applicationContext == null ? null : BeanFactoryUtils.beansOfTypeIncludingAncestors(applicationContext, MonitorConfig.class, false, false);
if (monitorConfigMap != null && monitorConfigMap.size() > 0) {
MonitorConfig monitorConfig = null;
for (MonitorConfig config : monitorConfigMap.values()) {
if (config.isDefault() == null || config.isDefault().booleanValue()) {
if (monitorConfig != null) {
throw new IllegalStateException("Duplicate monitor configs: " + monitorConfig + " and " + config);
}
monitorConfig = config;
}
}
if (monitorConfig != null) {
setMonitor(monitorConfig);
}
}
}
Boolean b = isInit();
if (b == null && getConsumer() != null) {
b = getConsumer().isInit();
}
if (b != null && b.booleanValue()) {
getObject();
}
}
可以看出ReferenceBean實現了FactoryBean和InitializingBean介面,當想從spring容器中獲取ReferenceBean例項的時候,ReferenceBean首先會呼叫重寫了InitializingBean的afterPropertiesSet方法來配置一些屬性,然乎呼叫重寫FactoryBean的getObject方法返回消費者想要的例項。可以將dubbo消費者初始化的入口看成是getObject方法。
ReferenceBean的getObject方法會呼叫ReferenceConfig的get方法
public synchronized T get() {
if (destroyed){
throw new IllegalStateException("Already destroyed!");
}
if (ref == null) {
init();
}
return ref;
}
進入到init方法
private void init() {
//前面有一大段程式碼都是檢查配置資訊和儲存配置資訊的
.................
//attributes通過系統context進行儲存.
StaticContext.getSystemContext().putAll(attributes);
ref = createProxy(map);
}
進入到createProxy方法,重點來了,在createProxy方法裡面會獲取到連線了遠端服務端的Invoker,並對Invoker進行代理,生成客戶端想要的例項。
@SuppressWarnings({ "unchecked", "rawtypes", "deprecation" })
private T createProxy(Map<String, String> map) {
URL tmpUrl = new URL("temp", "localhost", 0, map);
final boolean isJvmRefer;
if (isInjvm() == null) {
if (url != null && url.length() > 0) { //指定URL的情況下,不做本地引用
isJvmRefer = false;
} else if (InjvmProtocol.getInjvmProtocol().isInjvmRefer(tmpUrl)) {
//預設情況下如果本地有服務暴露,則引用本地服務.
isJvmRefer = true;
} else {
isJvmRefer = false;
}
} else {
isJvmRefer = isInjvm().booleanValue();
}
if (isJvmRefer) {
URL url = new URL(Constants.LOCAL_PROTOCOL, NetUtils.LOCALHOST, 0, interfaceClass.getName()).addParameters(map);
invoker = refprotocol.refer(interfaceClass, url);
if (logger.isInfoEnabled()) {
logger.info("Using injvm service " + interfaceClass.getName());
}
} else {
if (url != null && url.length() > 0) { // 使用者指定URL,指定的URL可能是對點對直連地址,也可能是註冊中心URL
String[] us = Constants.SEMICOLON_SPLIT_PATTERN.split(url);
if (us != null && us.length > 0) {
for (String u : us) {
URL url = URL.valueOf(u);
if (url.getPath() == null || url.getPath().length() == 0) {
url = url.setPath(interfaceName);
}
if (Constants.REGISTRY_PROTOCOL.equals(url.getProtocol())) {
urls.add(url.addParameterAndEncoded(Constants.REFER_KEY, StringUtils.toQueryString(map)));
} else {
urls.add(ClusterUtils.mergeUrl(url, map));
}
}
}
} else { // 通過註冊中心配置拼裝URL
List<URL> us = loadRegistries(false);
if (us != null && us.size() > 0) {
for (URL u : us) {
URL monitorUrl = loadMonitor(u);
if (monitorUrl != null) {
map.put(Constants.MONITOR_KEY, URL.encode(monitorUrl.toFullString()));
}
urls.add(u.addParameterAndEncoded(Constants.REFER_KEY, StringUtils.toQueryString(map)));
}
}
if (urls == null || urls.size() == 0) {
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.");
}
}
/**
*當只有一個註冊中心的時候,直接返回invokr,
*如果有多個配置中心,cluster會選取其中的一個invoker返回,
*/
if (urls.size() == 1) {
invoker = refprotocol.refer(interfaceClass, urls.get(0));
} else {
List<Invoker<?>> invokers = new ArrayList<Invoker<?>>();
URL registryURL = null;
for (URL url : urls) {
invokers.add(refprotocol.refer(interfaceClass, url));
if (Constants.REGISTRY_PROTOCOL.equals(url.getProtocol())) {
registryURL = url; // 用了最後一個registry url
}
}
if (registryURL != null) { // 有 註冊中心協議的URL
// 對有註冊中心的Cluster 只用 AvailableCluster
URL u = registryURL.addParameter(Constants.CLUSTER_KEY, AvailableCluster.NAME);
invoker = cluster.join(new StaticDirectory(u, invokers));
} else { // 不是 註冊中心的URL
invoker = cluster.join(new StaticDirectory(invokers));
}
}
}
Boolean c = check;
if (c == null && consumer != null) {
c = consumer.isCheck();
}
if (c == null) {
c = true; // default true
}
if (c && ! 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());
}
// 建立服務代理
return (T) proxyFactory.getProxy(invoker);
}
上面createProxy方法中,最重要的兩段程式碼
invoker = refprotocol.refer(interfaceClass, urls.get(0));
return (T) proxyFactory.getProxy(invoker);
再來看看refprotocol的refer方法,refprotocol是由spi機制動態生成的一個protocol,這個refprotocol預設實現是DubboProtocol,但是refprotocol又用裝飾模式在外面套了多層,在refprotocol呼叫refer方法的時候會首先呼叫RegistryProtocol,ProtocolFilterWrapper,ProtocolListenerWrapper的refer方法,最後呼叫DubboProtocol的refer方法。
RegistryProtocol的refer方法所做的事情
1.根據配置檔案裡面資訊獲取註冊中心
2.將消費者資訊註冊到註冊中心
3.讓RegistryDirectory訂閱註冊中心
先看RegistryProtocol的refer方法
public <T> Invoker<T> refer(Class<T> type, URL url) throws RpcException {
//url:zookeeper://127.0.0.1:2181/com.alibaba.dubbo.registry.RegistryService?application=consumer&dubbo=2.5.3&pid=82246&refer=application%3Dconsumer%26dubbo%3D2.5.3%26interface%3Dcom.ts.service.TestService%26methods%3DgetName%26pid%3D82246%26side%3Dconsumer%26timeout%3D30%26timestamp%3D1562837163280×tamp=1562837163352
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 ) ) {
//url中是帶有分組的,採取合併策略返回invoker
return doRefer( getMergeableCluster(), registry, type, url );
}
}
return doRefer(cluster, registry, type, url);
}
如何獲取註冊中心的
先看AbstractRegistryFactory中的getRegistry方法
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);
//key:zookeeper://127.0.0.1:2181/com.alibaba.dubbo.registry.RegistryService
String key = url.toServiceString();
// 鎖定註冊中心獲取過程,保證註冊中心單一例項
LOCK.lock();
try {
//先根據key到快取中獲取registry中心
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();
}
}
ZookeeperRegistryFactory的createRegistry方法,dubbo的註冊中心一般都是zookeeper
public Registry createRegistry(URL url) {
return new ZookeeperRegistry(url, zookeeperTransporter);
}
public ZookeeperRegistry(URL url, ZookeeperTransporter zookeeperTransporter) {
//FailbackRegistry(url),當我們建立註冊中心失敗後,FailbackRegistry能定時重試
super(url);
if (url.isAnyHost()) {
throw new IllegalStateException("registry address == null");
}
String group = url.getParameter(Constants.GROUP_KEY, DEFAULT_ROOT);
if (! group.startsWith(Constants.PATH_SEPARATOR)) {
group = Constants.PATH_SEPARATOR + group;
}
//zookeeper上的跟節點,預設的是dubbo
this.root = group;
zkClient = zookeeperTransporter.connect(url);
//新增一個狀態改變的監聽器
zkClient.addStateListener(new StateListener() {
public void stateChanged(int state) {
if (state == RECONNECTED) {
try {
recover();
} catch (Exception e) {
logger.error(e.getMessage(), e);
}
}
}
});
}
FailbackRegistry(url)構造方法
public FailbackRegistry(URL url) {
//AbstractRegistry(url),起到一個儲存url和載入快取的檔案
super(url);
int retryPeriod = url.getParameter(Constants.REGISTRY_RETRY_PERIOD_KEY, Constants.DEFAULT_REGISTRY_RETRY_PERIOD);
//開啟一個定時重試的執行緒池,定時失敗的連結,這個不僅用在連線註冊中心,還會用在連線服務端
this.retryFuture = retryExecutor.scheduleWithFixedDelay(new Runnable() {
public void run() {
// 檢測並連線註冊中心
try {
retry();
} catch (Throwable t) { // 防禦性容錯
logger.error("Unexpected error occur at failed retry, cause: " + t.getMessage(), t);
}
}
}, retryPeriod, retryPeriod, TimeUnit.MILLISECONDS);
}
AbstractRegistry(URL url)方法
public AbstractRegistry(URL url) {
setUrl(url);
// 啟動檔案儲存定時器
syncSaveFile = url.getParameter(Constants.REGISTRY_FILESAVE_SYNC_KEY, false);
//會先去使用者主目錄下的.dubbo目錄下載入快取註冊中心的快取檔案
//filename: /Users/wangbiao/.dubbo/dubbo-registry-127.0.0.1.cache
String filename = url.getParameter(Constants.FILE_KEY, System.getProperty("user.home") + "/.dubbo/dubbo-registry-" + url.getHost() + ".cache");
File file = null;
if (ConfigUtils.isNotEmpty(filename)) {
file = new File(filename);
if(! file.exists() && file.getParentFile() != null && ! file.getParentFile().exists()){
if(! file.getParentFile().mkdirs()){
throw new IllegalArgumentException("Invalid registry store file " + file + ", cause: Failed to create directory " + file.getParentFile() + "!");
}
}
}
this.file = file;
loadProperties();
//通知訂閱
notify(url.getBackupUrls());
}
再看zkClient = zookeeperTransporter.connect(url);這段程式碼
public class ZkclientZookeeperTransporter implements ZookeeperTransporter {
public ZookeeperClient connect(URL url) {
return new ZkclientZookeeperClient(url);
}
}
public ZkclientZookeeperClient(URL url) {
super(url);
client = new ZkClient(url.getBackupAddress());
client.subscribeStateChanges(new IZkStateListener() {
public void handleStateChanged(KeeperState state) throws Exception {
ZkclientZookeeperClient.this.state = state;
if (state == KeeperState.Disconnected) {
stateChanged(StateListener.DISCONNECTED);
} else if (state == KeeperState.SyncConnected) {
stateChanged(StateListener.CONNECTED);
}
}
public void handleNewSession() throws Exception {
stateChanged(StateListener.RECONNECTED);
}
});
}
到此註冊中心已經連線上了,再回到RegistryProtocol中的doRefer方法,這個方法比較重要了,主要做了這幾件事
1.建立一個directory,directory封裝了被呼叫的介面資訊,註冊中心,通訊協議,directory封裝了Invoker,directory也是一個監聽器能隨著註冊中心配置的變動而動態生成新的Invoker公消費者端使用;
2.將消費者資訊註冊到配置中心;
3.監聽註冊中心的配置資訊,路由資訊和提供者資訊;
4.連線服務端,方便後面和服務端通訊,注意dubbo消費者和服務者是直接通訊的,是不經過註冊中心的;
5.用cluster把Directory封裝成一個帶有失敗後能策略轉移的Invoker;
private <T> Invoker<T> doRefer(Cluster cluster, Registry registry, Class<T> type, URL url) {
//可以將directory看成是一個動態的Invoker,它可以監聽註冊中心服務端的配置,能隨著註冊中心資訊變化重新生成新的Invoker,還封裝了所有服務真正引用邏輯,覆蓋配置,路由規則
RegistryDirectory<T> directory = new RegistryDirectory<T>(type, url);
directory.setRegistry(registry);
directory.setProtocol(protocol);
//subscribeUrl的值 consumer://192.168.1.59/com.ts.service.TestService?application=consumer&dubbo=2.5.3&interface=com.ts.service.TestService&methods=getName&pid=82774&side=consumer&timeout=30×tamp=1562837866153
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));
//將directory偽裝成帶有轉移策略的Invoker並返回,這裡是FailoverClusterInvoker
return cluster.join(directory);
}
下面看一下如何將消費者資訊註冊到註冊中心的,到FailbackRegistry的register方法
public void register(URL url) {
//到AbstractRegistry中儲存了一下url
//url的值:consumer://192.168.1.59/com.ts.service.TestService?application=consumer&category=consumers&check=false&dubbo=2.5.3&interface=com.ts.service.TestService&methods=getName&pid=82774&side=consumer&timeout=30×tamp=1562837866153
super.register(url);
failedRegistered.remove(url);
failedUnregistered.remove(url);
try {
// 向伺服器端傳送註冊請求
doRegister(url);
} catch (Exception e) {
Throwable t = e;
// 如果開啟了啟動時檢測,則直接丟擲異常
boolean check = getUrl().getParameter(Constants.CHECK_KEY, true)
&& url.getParameter(Constants.CHECK_KEY, true)
&& ! Constants.CONSUMER_PROTOCOL.equals(url.getProtocol());
boolean skipFailback = t instanceof SkipFailbackWrapperException;
if (check || skipFailback) {
if(skipFailback) {
t = t.getCause();
}
throw new IllegalStateException("Failed to register " + url + " to registry " + getUrl().getAddress() + ", cause: " + t.getMessage(), t);
} else {
logger.error("Failed to register " + url + ", waiting for retry, cause: " + t.getMessage(), t);
}
// 將失敗的註冊請求記錄到失敗列表,定時重試
failedRegistered.add(url);
}
}
ZookeeperRegistry的doRegister方法
protected void doRegister(URL url) {
try {
//根據url中dynamic的引數判斷在註冊中心建立臨時節點還是永久節點,true是臨時節點
zkClient.create(toUrlPath(url), url.getParameter(Constants.DYNAMIC_KEY, true));
} catch (Throwable e) {
throw new RpcException("Failed to register " + url + " to zookeeper " + getUrl() + ", cause: " + e.getMessage(), e);
}
}
AbstractZookeeperClient的create方法
public void create(String path, boolean ephemeral) {
int i = path.lastIndexOf('/');
if (i > 0) {
//遞迴呼叫,最終將path擷取成“/dubbo”
create(path.substring(0, i), false);
}
//path的值: /dubbo
//建立臨時節點
if (ephemeral) {
createEphemeral(path);
} else {
//建立永久節點,dubbo在這裡是建立的永久節點
//這裡是通過遞迴來建立節點,建立的節點全路徑是:
// /dubbo/com.ts.service.TestService/consumers/consumer%3A%2F%2F192.168.1.59%2Fcom.ts.service.TestService%3Fapplication%3Dconsumer%26category%3Dconsumers%26check%3Dfalse%26dubbo%3D2.5.3%26interface%3Dcom.ts.service.TestService%26methods%3DgetName%26pid%3D82774%26side%3Dconsumer%26timeout%3D30%26timestamp%3D1562837866153
createPersistent(path);
}
}
可以在zookeeper的客戶端檢視到有這麼一個節點: /dubbo/com.ts.service.TestService/consumers
消費者註冊到註冊中心後,就是消費者訂閱服務者資訊和連線服務端了,先看RegistryDirectory的subscribe方法
public void subscribe(URL url) {
setConsumerUrl(url);
//通過註冊中心來訂閱消費者端的服務,this是作為一個監聽器的
registry.subscribe(url, this);
}
進入到FailbackRegistry的subscribe方法,在連線註冊中心的時候也會用到這個類,這個類的主要作用就是當我們連線註冊中心或者是監聽註冊中心或者訂閱服務者失敗的時候,它都能幫我們重新連線
@Override
public void subscribe(URL url, NotifyListener listener) {
//AbstractRegistry儲存url和監聽器,並形成一種一對多的關係
super.subscribe(url, listener);
removeFailedSubscribed(url, listener);
try {
// 向伺服器端傳送訂閱請求
doSubscribe(url, listener);
} catch (Exception e) {
Throwable t = e;
List<URL> urls = getCacheUrls(url);
if (urls != null && urls.size() > 0) {
notify(url, listener, urls);
logger.error("Failed to subscribe " + url + ", Using cached list: " + urls + " from cache file: " + getUrl().getParameter(Constants.FILE_KEY, System.getProperty("user.home") + "/dubbo-registry-" + url.getHost() + ".cache") + ", cause: " + t.getMessage(), t);
} else {
// 如果開啟了啟動時檢測,則直接丟擲異常
boolean check = getUrl().getParameter(Constants.CHECK_KEY, true)
&& url.getParameter(Constants.CHECK_KEY, true);
boolean skipFailback = t instanceof SkipFailbackWrapperException;
if (check || skipFailback) {
if(skipFailback) {
t = t.getCause();
}
throw new IllegalStateException("Failed to subscribe " + url + ", cause: " + t.getMessage(), t);
} else {
logger.error("Failed to subscribe " + url + ", waiting for retry, cause: " + t.getMessage(), t);
}
}
// 將失敗的訂閱請求記錄到失敗列表,定時重試
addFailedSubscribed(url, listener);
}
}
進入到ZookeeperRegistry的doSubscribe方法,這個方法會根據url中category帶的三個值providers,configurators,routers,在註冊中心建立providers,configurators,routers三個節點
protected void doSubscribe(final URL url, final NotifyListener listener) {
try {
if (Constants.ANY_VALUE.equals(url.getServiceInterface())) {
String root = toRootPath();
ConcurrentMap<NotifyListener, ChildListener> listeners = zkListeners.get(url);
if (listeners == null) {
zkListeners.putIfAbsent(url, new ConcurrentHashMap<NotifyListener, ChildListener>());
listeners = zkListeners.get(url);
}
ChildListener zkListener = listeners.get(listener);
if (zkListener == null) {
listeners.putIfAbsent(listener, new ChildListener() {
public void childChanged(String parentPath, List<String> currentChilds) {
for (String child : currentChilds) {
if (! anyServices.contains(child)) {
anyServices.add(child);
subscribe(url.setPath(child).addParameters(Constants.INTERFACE_KEY, child,
Constants.CHECK_KEY, String.valueOf(false)), listener);
}
}
}
});
zkListener = listeners.get(listener);
}
zkClient.create(root, false);
List<String> services = zkClient.addChildListener(root, zkListener);
if (services != null && services.size() > 0) {
anyServices.addAll(services);
for (String service : services) {
subscribe(url.setPath(service).addParameters(Constants.INTERFACE_KEY, service,
Constants.CHECK_KEY, String.valueOf(false)), listener);
}
}
} else {
List<URL> urls = new ArrayList<URL>();
for (String path : toCategoriesPath(url)) {
ConcurrentMap<NotifyListener, ChildListener> listeners = zkListeners.get(url);
if (listeners == null) {
zkListeners.putIfAbsent(url, new ConcurrentHashMap<NotifyListener, ChildListener>());
listeners = zkListeners.get(url);
}
ChildListener zkListener = listeners.get(listener);
if (zkListener == null) {
listeners.putIfAbsent(listener, new ChildListener() {
public void childChanged(String parentPath, List<String> currentChilds) {
//接收到事件通知,重新整理directory的可用服務列表,這裡的listener跟蹤下來知道是Redirectory例項
ZookeeperRegistry.this.notify(url, listener, toUrlsWithEmpty(url, parentPath, currentChilds));
}
});
zkListener = listeners.get(listener);
}
//在zookeeper的三個路徑
// /dubbo/com.ts.service.TestService/configurators
// /dubbo/com.ts.service.TestService/routers
// /dubbo/com.ts.service.TestService/providers
zkClient.create(path, false);
//這個地方就是從註冊中心訂閱服務提供者的資訊,獲取到要訂閱的服務
//現在才知道在這裡,前面看完原始碼一直不知道是在那裡獲取到服務者資訊的
List<String> children = zkClient.addChildListener(path, zkListener);
if (children != null) {
urls.addAll(toUrlsWithEmpty(url, path, children));
}
}
//建立DubboInvoker,連線客戶端
notify(url, listener, urls);
}
} catch (Throwable e) {
throw new RpcException("Failed to subscribe " + url + " to zookeeper " + getUrl() + ", cause: " + e.getMessage(), e);
}
}
notify(url, listener, urls)方法,呼叫的是FailbackRegistry類中的notify方法,有FailbackRegistry的notify方法包裹著是當notify失敗的時候能重試。
@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);
} catch (Exception t) {
// 將失敗的通知請求記錄到失敗列表,定時重試
Map<NotifyListener, List<URL>> listeners = failedNotified.get(url);
if (listeners == null) {
failedNotified.putIfAbsent(url, new ConcurrentHashMap<NotifyListener, List<URL>>());
listeners = failedNotified.get(url);
}
listeners.put(listener, urls);
logger.error("Failed to notify for subscribe " + url + ", waiting for retry, cause: " + t.getMessage(), t);
}
}
protected void doNotify(URL url, NotifyListener listener, List<URL> urls) {
super.notify(url, listener, urls);
}
進入到AbstractRegistry的notify方法,
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 ((urls == null || urls.size() == 0)
&& ! Constants.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);
}
Map<String, List<URL>> result = new HashMap<String, List<URL>>();
for (URL u : urls) {
if (UrlUtils.isMatch(url, u)) {
String category = u.getParameter(Constants.CATEGORY_KEY, Constants.DEFAULT_CATEGORY);
List<URL> categoryList = result.get(category);
if (categoryList == null) {
categoryList = new ArrayList<URL>();
result.put(category, categoryList);
}
categoryList.add(u);
}
}
if (result.size() == 0) {
return;
}
Map<String, List<URL>> categoryNotified = notified.get(url);
if (categoryNotified == null) {
notified.putIfAbsent(url, new ConcurrentHashMap<String, List<URL>>());
categoryNotified = notified.get(url);
}
for (Map.Entry<String, List<URL>> entry : result.entrySet()) {
String category = entry.getKey();
List<URL> categoryList = entry.getValue();
categoryNotified.put(category, categoryList);
saveProperties(url);
//對這裡得到的providers,configurators,routers分別進行通知
//listener是RegistryDirectory
listener.notify(categoryList);
}
}
進入到RegistryDirectory的notify方法,主要是更新配置,建立Invoker
public synchronized void notify(List<URL> urls) {
List<URL> invokerUrls = new ArrayList<URL>();
List<URL> routerUrls = new ArrayList<URL>();
List<URL> configuratorUrls = new ArrayList<URL>();
for (URL url : urls) {
String protocol = url.getProtocol();
String category = url.getParameter(Constants.CATEGORY_KEY, Constants.DEFAULT_CATEGORY);
if (Constants.ROUTERS_CATEGORY.equals(category)
|| Constants.ROUTE_PROTOCOL.equals(protocol)) {
routerUrls.add(url);
} else if (Constants.CONFIGURATORS_CATEGORY.equals(category)
|| Constants.OVERRIDE_PROTOCOL.equals(protocol)) {
configuratorUrls.add(url);
} else if (Constants.PROVIDERS_CATEGORY.equals(category)) {
invokerUrls.add(url);
} else {
logger.warn("Unsupported category " + category + " in notified url: " + url + " from registry " + getUrl().getAddress() + " to consumer " + NetUtils.getLocalHost());
}
}
// configurators
if (configuratorUrls != null && configuratorUrls.size() >0 ){
this.configurators = toConfigurators(configuratorUrls);
}
// routers
if (routerUrls != null && routerUrls.size() >0 ){
List<Router> routers = toRouters(routerUrls);
if(routers != null){ // null - do nothing
setRouters(routers);
}
}
List<Configurator> localConfigurators = this.configurators; // local reference
// 合併override引數
this.overrideDirectoryUrl = directoryUrl;
if (localConfigurators != null && localConfigurators.size() > 0) {
for (Configurator configurator : localConfigurators) {
this.overrideDirectoryUrl = configurator.configure(overrideDirectoryUrl);
}
}
// providers
refreshInvoker(invokerUrls);
}
/**
* 根據invokerURL列表轉換為invoker列表。轉換規則如下:
* 1.如果url已經被轉換為invoker,則不在重新引用,直接從快取中獲取,注意如果url中任何一個引數變更也會重新引用
* 2.如果傳入的invoker列表不為空,則表示最新的invoker列表
* 3.如果傳入的invokerUrl列表是空,則表示只是下發的override規則或route規則,需要重新交叉對比,決定是否需要重新引用。
* @param invokerUrls 傳入的引數不能為null
*/
private void refreshInvoker(List<URL> invokerUrls){
if (invokerUrls != null && invokerUrls.size() == 1 && invokerUrls.get(0) != null
&& Constants.EMPTY_PROTOCOL.equals(invokerUrls.get(0).getProtocol())) {
this.forbidden = true; // 禁止訪問
this.methodInvokerMap = null; // 置空列表
destroyAllInvokers(); // 關閉所有Invoker
} else {
this.forbidden = false; // 允許訪問
Map<String, Invoker<T>> oldUrlInvokerMap = this.urlInvokerMap; // local reference
if (invokerUrls.size() == 0 && this.cachedInvokerUrls != null){
invokerUrls.addAll(this.cachedInvokerUrls);
} else {
this.cachedInvokerUrls = new HashSet<URL>();
this.cachedInvokerUrls.addAll(invokerUrls);//快取invokerUrls列表,便於交叉對比,但是沒有在其他地方看到使用cachedInvokerUrls
}
if (invokerUrls.size() ==0 ){
return;
}
Map<String, Invoker<T>> newUrlInvokerMap = toInvokers(invokerUrls) ;// 將URL列表轉成Invoker列表
Map<String, List<Invoker<T>>> newMethodInvokerMap = toMethodInvokers(newUrlInvokerMap); // 換方法名對映Invoker列表
// state change
//如果計算錯誤,則不進行處理.
if (newUrlInvokerMap == null || newUrlInvokerMap.size() == 0 ){
logger.error(new IllegalStateException("urls to invokers error .invokerUrls.size :"+invokerUrls.size() + ", invoker.size :0. urls :"+invokerUrls.toString()));
return ;
}
this.methodInvokerMap = multiGroup ? toMergeMethodInvokerMap(newMethodInvokerMap) : newMethodInvokerMap;
this.urlInvokerMap = newUrlInvokerMap;
try{
destroyUnusedInvokers(oldUrlInvokerMap,newUrlInvokerMap); // 關閉未使用的Invoker
}catch (Exception e) {
logger.warn("destroyUnusedInvokers error. ", e);
}
}
}
toInvokers(invokerUrls)方法
/**
* 將urls轉成invokers,如果url已經被refer過,不再重新引用。
*
* @param urls
* @param overrides
* @param query
* @return invokers
*/
private Map<String, Invoker<T>> toInvokers(List<URL> urls) {
Map<String, Invoker<T>> newUrlInvokerMap = new HashMap<String, Invoker<T>>();
if(urls == null || urls.size() == 0){
return newUrlInvokerMap;
}
Set<String> keys = new HashSet<String>();
String queryProtocols = this.queryMap.get(Constants.PROTOCOL_KEY);
for (URL providerUrl : urls) {
//如果reference端配置了protocol,則只選擇匹配的protocol
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 (Constants.EMPTY_PROTOCOL.equals(providerUrl.getProtocol())) {
continue;
}
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(); // URL引數是排序的
if (keys.contains(key)) { // 重複URL
continue;
}
keys.add(key);
// 快取key為沒有合併消費端引數的URL,不管消費端如何合併引數,如果服務端URL發生變化,則重新refer
Map<String, Invoker<T>> localUrlInvokerMap = this.urlInvokerMap; // local reference
Invoker<T> invoker = localUrlInvokerMap == null ? null : localUrlInvokerMap.get(key);
if (invoker == null) { // 快取中沒有,重新refer
try {
boolean enabled = true;
if (url.hasParameter(Constants.DISABLED_KEY)) {
enabled = ! url.getParameter(Constants.DISABLED_KEY, false);
} else {
enabled = url.getParameter(Constants.ENABLED_KEY, true);
}
if (enabled) {
// * 代理類,主要用於儲存註冊中心下發的url地址,用於重新重新refer時能夠根據providerURL queryMap overrideMap重新組裝
invoker = new InvokerDelegete<T>(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) { // 將新的引用放入快取
newUrlInvokerMap.put(key, invoker);
}
}else {
newUrlInvokerMap.put(key, invoker);
}
}
keys.clear();
return newUrlInvokerMap;
}
建立Invoker invoker = new InvokerDelegete<T>(protocol.refer(serviceType, url), url, providerUrl);
直接進入到DubboProtocol,檢視DubboProtocol的refer方法,主要是new一個DubboInvoker,然後對DubboInvoker傳參,其中有一個很重要的引數就是ExchangeClient,ExchangeClient就是負責消費者,服務者的連線和通訊。下面大段程式碼都是為了建立ExchangeClient
public <T> Invoker<T> refer(Class<T> serviceType, URL url) throws RpcException {
// create rpc invoker.
DubboInvoker<T> invoker = new DubboInvoker<T>(serviceType, url, getClients(url), invokers);
invokers.add(invoker);
return invoker;
}
private ExchangeClient[] getClients(URL url){
//是否共享連線
boolean service_share_connect = false;
int connections = url.getParameter(Constants.CONNECTIONS_KEY, 0);
//如果connections不配置,則共享連線,否則每服務每連線
if (connections == 0){
service_share_connect = true;
connections = 1;
}
ExchangeClient[] clients = new ExchangeClient[connections];
for (int i = 0; i < clients.length; i++) {
if (service_share_connect){
clients[i] = getSharedClient(url);
} else {
clients[i] = initClient(url);
}
}
return clients;
}
/**
*獲取共享連線
*/
private ExchangeClient getSharedClient(URL url){
//key的值:202.106.199.34:29014
String key = url.getAddress();
ReferenceCountExchangeClient client = referenceClientMap.get(key);
if ( client != null ){
if ( !client.isClosed()){
client.incrementAndGetCount();
return client;
} else {
// logger.warn(new IllegalStateException("client is closed,but stay in clientmap .client :"+ client));
referenceClientMap.remove(key);
}
}
ExchangeClient exchagneclient = initClient(url);
client = new ReferenceCountExchangeClient(exchagneclient, ghostClientMap);
referenceClientMap.put(key, client);
ghostClientMap.remove(key);
return client;
}
/**
* 建立新連線.
*/
private ExchangeClient initClient(URL url) {
// client type setting.
String str = url.getParameter(Constants.CLIENT_KEY, url.getParameter(Constants.SERVER_KEY, Constants.DEFAULT_REMOTING_CLIENT));
String version = url.getParameter(Constants.DUBBO_VERSION_KEY);
boolean compatible = (version != null && version.startsWith("1.0."));
url = url.addParameter(Constants.CODEC_KEY, Version.isCompatibleVersion() && compatible ? COMPATIBLE_CODEC_NAME : DubboCodec.NAME);
//預設開啟heartbeat
url = url.addParameterIfAbsent(Constants.HEARTBEAT_KEY, String.valueOf(Constants.DEFAULT_HEARTBEAT));
// BIO存在嚴重效能問題,暫時不允許使用
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 {
//設定連線應該是lazy的
if (url.getParameter(Constants.LAZY_CONNECT_KEY, false)){
client = new LazyConnectExchangeClient(url ,requestHandler);
} else {
//底層通過nettry通訊
client = Exchangers.connect(url ,requestHandler);
}
} catch (RemotingException e) {
throw new RpcException("Fail to create remoting client for service(" + url
+ "): " + e.getMessage(), e);
}
return client;
}
其實到這裡Invoker已經建立好了,client = Exchangers.connect(url ,requestHandler);這裡有一個很重要的類requestHandler,requestHandler其實是ExchangeHandler,ExchangeHandler是DubboProtocol的一個內部類,ExchangeClient主要是通過ExchangeHandler來對通訊進行接受處理。消費者和服務者的Invoker都會用到。檢視ExchangeClientde 實現類
HeaderExchangeClient,可以看出底層通訊是由HeaderExchangeClient來負責的。
public class HeaderExchangeClient implements ExchangeClient {
。。。。。。。。。
//接受請求
public ResponseFuture request(Object request) throws RemotingException {
return channel.request(request);
}
public ResponseFuture request(Object request, int timeout) throws RemotingException {
return channel.request(request, timeout);
}
//傳送請求
public void send(Object message) throws RemotingException {
channel.send(message);
}
public void send(Object message, boolean sent) throws RemotingException {
channel.send(message, sent);
}
}
底層通訊太複雜了,這裡就不細講了,上面所做的用一句話來講就是將配置中要呼叫的服務封裝成一個能和服務端進行同信的Invoker,這個Invoker就是我們得到的DubboInvoker。
還有最後一個操作就是將這個Invoker改造成消費端所需要的模樣,在ReferenceConfig中有這樣一行程式碼就是做這個的
// 建立服務代理
return (T) proxyFactory.getProxy(invoker);
首先進入到AbstractProxyFactory中
/*
* Copyright 1999-2011 Alibaba Group.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.alibaba.dubbo.rpc.proxy;
import com.alibaba.dubbo.common.Constants;
import com.alibaba.dubbo.common.utils.ReflectUtils;
import com.alibaba.dubbo.rpc.Invoker;
import com.alibaba.dubbo.rpc.ProxyFactory;
import com.alibaba.dubbo.rpc.RpcException;
import com.alibaba.dubbo.rpc.service.EchoService;
/**
* AbstractProxyFactory
*
* @author william.liangf
*/
public abstract class AbstractProxyFactory implements ProxyFactory {
public <T> T getProxy(Invoker<T> invoker) throws RpcException {
Class<?>[] interfaces = null;
String config = invoker.getUrl().getParameter("interfaces");
if (config != null && config.length() > 0) {
String[] types = Constants.COMMA_SPLIT_PATTERN.split(config);
if (types != null && types.length > 0) {
interfaces = new Class<?>[types.length + 2];
interfaces[0] = invoker.getInterface();
interfaces[1] = EchoService.class;
for (int i = 0; i < types.length; i ++) {
interfaces[i + 1] = ReflectUtils.forName(types[i]);
}
}
}
if (interfaces == null) {
interfaces = new Class<?>[] {invoker.getInterface(), EchoService.class};
}
//這裡呼叫的是JavassistProxyFactory的getProxy
return getProxy(invoker, interfaces);
}
public abstract <T> T getProxy(Invoker<T> invoker, Class<?>[] types);
}
進入到JavassistProxyFactory的getProxy方法中
public <T> T getProxy(Invoker<T> invoker, Class<?>[] interfaces) {
//運用動態代理生成消費者端想要的介面的實現物件
return (T) Proxy.getProxy(interfaces).newInstance(new InvokerInvocationHandler(invoker));
}
當我們呼叫這個代理物件的時候,例如下面在呼叫testService.getName();
TestService testService = (TestService) context.getBean("testService");
System.out.println(testService.getName());
首先會進入到InvokerInvocationHandler的invoke方法
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]);
}
//這個最後會呼叫AbstractClusterInvoker的Invoker
return invoker.invoke(new RpcInvocation(method, args)).recreate();
}
進入到AbstractClusterInvoker的invoke方法
public Result invoke(final Invocation invocation) throws RpcException {
checkWheatherDestoried();
LoadBalance loadbalance;
List<Invoker<T>> invokers = list(invocation);
if (invokers != null && invokers.size() > 0) {
loadbalance = ExtensionLoader.getExtensionLoader(LoadBalance.class).getExtension(invokers.get(0).getUrl()
.getMethodParameter(invocation.getMethodName(),Constants.LOADBALANCE_KEY, Constants.DEFAULT_LOADBALANCE));
} else {
//負載均衡策略,預設的是隨機的,random
loadbalance = ExtensionLoader.getExtensionLoader(LoadBalance.class).getExtension(Constants.DEFAULT_LOADBALANCE);
}
//冪等操作:非同步操作預設新增invocation id
RpcUtils.attachInvocationIdIfAsync(getUrl(), invocation);
return doInvoke(invocation, invokers, loadbalance);
}
進入到FailbackClusterInvoker的doInvoke方法
protected Result doInvoke(Invocation invocation, List<Invoker<T>> invokers, LoadBalance loadbalance) throws RpcException {
try {
checkInvokers(invokers, invocation);
//根據loadbalance選出invoker,這裡提現負載均衡策略和錯誤轉移策略
Invoker<T> invoker = select(loadbalance, invocation, invokers, null);
//呼叫AbstractInvoker的invoke方法
return invoker.invoke(invocation);
} catch (Throwable e) {
logger.error("Failback to invoke method " + invocation.getMethodName() + ", wait for retry in background. Ignored exception: "
+ e.getMessage() + ", ", e);
addFailed(invocation, this);
return new RpcResult(); // ignore
}
}
AbstractInvoker的invoke方法
public Result invoke(Invocation inv) throws RpcException {
if(destroyed) {
throw new RpcException("Rpc invoker for service " + this + " on consumer " + NetUtils.getLocalHost()
+ " use dubbo version " + Version.getVersion()
+ " is DESTROYED, can not be invoked any more!");
}
RpcInvocation invocation = (RpcInvocation) inv;
invocation.setInvoker(this);
if (attachment != null && attachment.size() > 0) {
invocation.addAttachmentsIfAbsent(attachment);
}
Map<String, String> context = RpcContext.getContext().getAttachments();
if (context != null) {
invocation.addAttachmentsIfAbsent(context);
}
if (getUrl().getMethodParameter(invocation.getMethodName(), Constants.ASYNC_KEY, false)){
invocation.setAttachment(Constants.ASYNC_KEY, Boolean.TRUE.toString());
}
RpcUtils.attachInvocationIdIfAsync(getUrl(), invocation);
try {
//呼叫DubboInvoker的doInvoke方法
return doInvoke(invocation);
} catch (InvocationTargetException e) { // biz exception
Throwable te = e.getTargetException();
if (te == null) {
return new RpcResult(e);
} else {
if (te instanceof RpcException) {
((RpcException) te).setCode(RpcException.BIZ_EXCEPTION);
}
return new RpcResult(te);
}
} catch (RpcException e) {
if (e.isBiz()) {
return new RpcResult(e);
} else {
throw e;
}
} catch (Throwable e) {
return new RpcResult(e);
}
}
DubboInvoker的doInvoke方法
@Override
protected Result doInvoke(final Invocation invocation) throws Throwable {
RpcInvocation inv = (RpcInvocation) invocation;
final String methodName = RpcUtils.getMethodName(invocation);
inv.setAttachment(Constants.PATH_KEY, getUrl().getPath());
inv.setAttachment(Constants.VERSION_KEY, version);
//傳送請求還是交給了ExchangeClient來做
ExchangeClient currentClient;
if (clients.length == 1) {
currentClient = clients[0];
} else {
currentClient = clients[index.getAndIncrement() % clients.length];
}
try {
boolean isAsync = RpcUtils.isAsync(getUrl(), invocation);
boolean isOneway = RpcUtils.isOneway(getUrl(), invocation);
int timeout = getUrl().getMethodParameter(methodName, Constants.TIMEOUT_KEY,Constants.DEFAULT_TIMEOUT);
if (isOneway) {//沒有返回值
boolean isSent = getUrl().getMethodParameter(methodName, Constants.SENT_KEY, false);
currentClient.send(inv, isSent);
RpcContext.getContext().setFuture(null);
return new RpcResult();
} else if (isAsync) {//非同步呼叫
ResponseFuture future = currentClient.request(inv, timeout) ;
RpcContext.getContext().setFuture(new FutureAdapter<Object>(future));
return new RpcResult();
} else {//有返回值的
RpcContext.getContext().setFuture(null);
return (Result) currentClient.request(inv, timeout).get();
}
} catch (TimeoutException e) {
throw new RpcException(RpcException.TIMEOUT_EXCEPTION, "Invoke remote method timeout. method: " + invocation.getMethodName() + ", provider: " + getUrl() + ", cause: " + e.getMessage(), e);
} catch (RemotingException e) {
throw new RpcException(RpcException.NETWORK_EXCEPTION, "Failed to invoke remote method: " + invocation.getMethodName() + ", provider: " + getUrl() + ", cause: " + e.getMessage(), e);
}
}
到此Dubbo消費者的初始化和呼叫都講完了。
最後總結一下Dubbo消費者初始化和呼叫的過程
1.讀取配置檔案,獲取註冊中心的資訊和要連線的遠端服務者資訊
2.根據配置的註冊中心資訊獲取配置中心,並將消費者資訊註冊到註冊中心
3.在註冊中心註冊config,provider和route三個節點,並監聽這三個節點,當有配置或者路由在註冊中心發生變化的時候,消費者這邊能動態的獲取到新的資訊。
4.獲取到要呼叫的遠端服務資訊,對這個資訊進行封裝生成Invoker,Invoker中有一個ExchangeClient,ExchangeClient底層通過netty去連線服務者和對服務端通訊。
5.消費者在呼叫服務的時候進行負載均衡和路由策略。
相關文章
- Dubbo原始碼分析(四)Dubbo呼叫鏈-消費端(叢集容錯機制)原始碼
- 【原始碼】RingBuffer(二)——消費者原始碼
- Dubbo服務消費者呼叫過程
- Dubbo之SPI原始碼分析原始碼
- Dubbo 原始碼分析 - SPI 機制原始碼
- Dubbo之限流TpsLimitFilter原始碼分析MITFilter原始碼
- dubbo服務者原始碼分期原始碼
- RocketMQ原始碼解析之訊息消費者(consume Message)MQ原始碼
- Dubbo原始碼分析(一)Dubbo與Spring整合例項原始碼Spring
- Dubbo原始碼分析(五)Dubbo呼叫鏈-服務端原始碼服務端
- Dubbo原始碼分析(三)Dubbo的服務引用Refer原始碼
- Dubbo原始碼分析(六)Dubbo通訊的編碼解碼機制原始碼
- dubbo原始碼分析之叢集Cluster原始碼
- dubbo原始碼分析02:服務引用原始碼
- Dubbo原始碼分析十一、服務路由原始碼路由
- Dubbo原始碼分析之服務引用原始碼
- Dubbo原始碼分析之服務暴露原始碼
- 圖解Dubbo,Dubbo服務消費詳解圖解
- 破解消費者密碼:線上消費者最重視什麼密碼
- sarama的消費者組分析、使用
- Dubbo原始碼分析(一)Dubbo的擴充套件點機制原始碼套件
- Dubbo RPC執行緒模型 原始碼分析RPC執行緒模型原始碼
- Dubbo原始碼分析(七)服務目錄原始碼
- dubbo 超時設定和原始碼分析原始碼
- Dubbo原始碼分析(二)Dubbo是從哪裡初始化的?原始碼
- 原始碼分析Dubbo網路通訊篇NettyClient原始碼Nettyclient
- Dubbo原始碼分析-叢集容錯之Router原始碼
- 使用ZIO-Streams的Redis Stream消費者和生產者實現原始碼Redis原始碼
- 大資料分析助力品牌洞察消費者大資料
- kafka消費者提交方式(程式碼演示)Kafka
- Kafka 消費組消費者分配策略Kafka
- Android原始碼角度分析事件分發消費(徹底整明白Android事件)Android原始碼事件
- Dubbo原始碼分析(十)同步呼叫與非同步呼叫原始碼非同步
- Dubbo原始碼分析(八)叢集容錯機制原始碼
- Dubbo原始碼分析(九)負載均衡演算法原始碼負載演算法
- Customology:2020年澳大利亞消費者價值分析
- 生產者消費者
- dubbo debug過程中發現的IDEA有趣的小發現及dubbo消費者debug小tipsIdea