dubbo源碼分析4-- ReferenceBean 獲取消費者代理對象

主要工作

  1. 獲取invoker對象
    a)註冊消費端的臨時節點
    b) 訂閱以下的節點
    /dubbo/com.test.ITestService/providers
    /dubbo/com.test.ITestService/configurators
    /dubbo/com.test.ITestService/routers
    c) 當這些節點下的子節點發生變化: 刷新本地的RequestDirector的本地urlInvokerMap列表
    第一次初始化invoker,創建DubboInvoker。啓動netty客戶端連接

  2. 將invoker包裝在代理對象Proxy$0中。返回代理對象

流程

主流程
在這裏插入圖片描述
消費端接口初始化或者有提供者節點變化時,通知更新本地zookeeper緩存文件
在這裏插入圖片描述
消費端接口初始化或者有提供者節點變化時,通知更新本地的invoker列表 urlInvokerMap
在這裏插入圖片描述
在更新本地的invoker列表時,沒有生成過Invoker,需要指定Invoker且netty客戶端連接服務端
在這裏插入圖片描述

入口

ReferenceBean 實現了FactoryBean, 那麼在從io容器獲取Bean時,就調用了Object getObject()
private transient volatile T ref; //保存代理對象
當有對象時,直接返回,否則初始化

public synchronized T get() {
    if (destroyed) {
         throw new IllegalStateException("Already destroyed!");
     }
     if (ref == null) {
         init();
     }
     return ref;
 }

init()組裝參數,最終調用 ref = createProxy(map);

  • 封裝urls
    用戶指定URL,指定的URL可能是對點對直連地址;否則通過註冊中心配置拼裝URL
//用戶指定URL,指定的URL可能是對點對直連地址,也可能是註冊中心URL
if (url != null && url.length() > 0) { 
    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.isEmpty()) {
        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.isEmpty()) {
        throw new IllegalStateException();
    }
}
  • 獲取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; 
          }
      }
      //有註冊中心的地址, StaticDirectory
      if (registryURL != null) { 
          URL u = registryURL.addParameter(Constants.CLUSTER_KEY, AvailableCluster.NAME);
          invoker = cluster.join(new StaticDirectory(u, invokers));
      } else { // not a registry url
          invoker = cluster.join(new StaticDirectory(invokers));
      }
  }

獲取invoker對象

refprotocol.refer(interfaceClass, url)
refprotocol這裏的對象是RegistryProtocol

1.獲取Registry:ZookeeperRegistry

//將registry協議改成zookeeper。
 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);
  }

2.向zookeeper註冊consumer://10.118.14.24/com.test.ITestService節點
registry.register(subscribeUrl.addParameters(Constants.CATEGORY_KEY, Constants.CONSUMERS_CATEGORY,
Constants.CHECK_KEY, String.valueOf(false)));
a)將url保存到註冊列表中
b)創建提供者的臨時節點
c)出現異常時,開啓啓動時檢測,直接拋出異常;否則記錄到failedRegistered,等待定時重新註冊
3.創建RegistryDirectory,並且訂閱consumer://10.118.14.24/com.test.ITestService

consumer://10.118.14.24/com.test.ITestService?category=providers,configurators,routers&side=consumer…

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());
 //加參數category:providers,configurators,routers
 directory.subscribe(subscribeUrl.addParameter(Constants.CATEGORY_KEY, 
         Constants.PROVIDERS_CATEGORY 
         + "," + Constants.CONFIGURATORS_CATEGORY 
         + "," + Constants.ROUTERS_CATEGORY));

4.cluster.join(directory);
默認是FailoverCluster, 所以返回的invoker對象是MockClusterInvoker(FailoverClusterInvoker)

mock=com.alibaba.dubbo.rpc.cluster.support.wrapper.MockClusterWrapper
failover=com.alibaba.dubbo.rpc.cluster.support.FailoverCluster
failfast=com.alibaba.dubbo.rpc.cluster.support.FailfastCluster
failsafe=com.alibaba.dubbo.rpc.cluster.support.FailsafeCluster
failback=com.alibaba.dubbo.rpc.cluster.support.FailbackCluster
forking=com.alibaba.dubbo.rpc.cluster.support.ForkingCluster
available=com.alibaba.dubbo.rpc.cluster.support.AvailableCluster
mergeable=com.alibaba.dubbo.rpc.cluster.support.MergeableCluster
broadcast=com.alibaba.dubbo.rpc.cluster.support.BroadcastCluster

創建代理對象

上面創建了invoker對象
proxyFactory.getProxy(invoker)
StubProxyFactoryWrapper(JavassistProxyFactory)

stub=com.alibaba.dubbo.rpc.proxy.wrapper.StubProxyFactoryWrapper
jdk=com.alibaba.dubbo.rpc.proxy.jdk.JdkProxyFactory
javassist=com.alibaba.dubbo.rpc.proxy.javassist.JavassistProxyFactory
  • JavassistProxyFactory創建代理對象

獲取代理方法

public <T> T getProxy(Invoker<T> invoker, Class<?>[] interfaces) {
     return (T) Proxy.getProxy(interfaces).newInstance(new InvokerInvocationHandler(invoker));
  }

攔截器

public class InvokerInvocationHandler implements InvocationHandler {

    private final Invoker<?> invoker;
    
    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();
    }
}

總結

從ioc獲取的對象是代理對象Proxy$0
交給InvokerInvocationHandler進行處理->MockClusterInvoker -> FailoverClusterInvoker-> RegistryDirectory->invoke列表

以下是分支流程=======================


zookeeper 訂閱

RegistryDirectory 實現了NotifyListener, 本身就是個監聽器
調用RegistryDirectory的subscribe,registry就是ZookeeperRegistry
而訂閱直接交給registry去處理, 而其中的this是RegistryDirectory

 public void subscribe(URL url) {
      setConsumerUrl(url);
      registry.subscribe(url, this);
  }

一:url和RegistryDirectory保存在subscribed中
consumer://10.118.14.24/com.test.ITestService?category=providers,configurators,routers&side=consumer…
二:ZookeeperRegistry 訂閱url
調用ZookeeperRegistry.doSubscribe(final URL url, final NotifyListener listener) ;
consumer://10.118.14.24/10.118.14.24/com.test.ITestService?category=providers,configurators,routers&default.check=false&default.cluster=failfast&default.timeout=60000&dubbo=2.8.4&interface=com.insaic.itbase.service.BusinessNoGeneratorService&logger=slf4j&methods=test,save&pid=10132&revision=1.0-20191223.094125-383&side=consumer&timestamp=1577323081022&version=1.0.0

  • I) 創建接口下providers、configurators、routers的路徑,並且增加他們的子節點變化監聽器

/dubbo/com.test.ITestService/providers
/dubbo/com.test.ITestService/configurators
/dubbo/com.test.ITestService/routers

private String[] toCategoriesPath(URL url) {
    String[] categroies;
    //接口是* ,那麼categroies 就是全量
    if (Constants.ANY_VALUE.equals(url.getParameter(Constants.CATEGORY_KEY))) {
        categroies = new String[] {Constants.PROVIDERS_CATEGORY, Constants.CONSUMERS_CATEGORY, 
                Constants.ROUTERS_CATEGORY, Constants.CONFIGURATORS_CATEGORY};
    } else {
    //從url中獲取category
        categroies = url.getParameter(Constants.CATEGORY_KEY, new String[] {Constants.DEFAULT_CATEGORY});
    }
    String[] paths = new String[categroies.length];
    for (int i = 0; i < categroies.length; i ++) {
       //dubbo+接口+category
        paths[i] = toServicePath(url) + Constants.PATH_SEPARATOR + categroies[i];
    }
    return paths;
}

以下是遍歷這些路徑path
-------> 1. 獲取ChildListener子節點監聽器

查看是否創建了監聽器
沒有,對/dubbo/com.test.ITestService/providers
/dubbo/com.test.ITestService/configurators
/dubbo/com.test.ITestService/routers
初始化監聽器
子節點變化通知當前節點子節點有變化,見下一節

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) {
          	ZookeeperRegistry.this.notify(url, listener, toUrlsWithEmpty(url, parentPath, currentChilds));
          }
      });
      zkListener = listeners.get(listener);
  }

------->2. 創建持久化節點,並且增加子節點監聽器
/dubbo/com.test.ITestService/providers
/dubbo/com.test.ITestService/configurators
/dubbo/com.test.ITestService/routers

 zkClient.create(path, false);
 List<String> children = zkClient.addChildListener(path, zkListener);

------->3. 獲取當前com.test.ITestService的下的providers、routes、configurators所有的子節點

在第一步遍歷providers時,當前子節點是
dubbo://10.118.22.51:20710/com.test.ITestService?default.cluster=failfast&default.timeout=3000&dubbo=2.8.4&generic=false&interface=com.test.ITestService&logger=slf4j&methods=test,save&pid=3091&revision=1.0.0&side=provider&timestamp=1576650287438&version=1.0.0
當configurators和routers當前沒有子節點,就封裝成
configurators:
empty://10.118.14.24/com.test.ITestService?&category=configurators&side=consumer&timestamp=1577329888735&version=1.0.0
routers:
empty://10.118.14.24/com.test.ITestService?&category=routers&side=consumer&timestamp=1577329888735&version=1.0.0

 List<String> children = zkClient.addChildListener(path, zkListener);
 if (children != null) {
 	urls.addAll(toUrlsWithEmpty(url, path, children));
 }

//當沒有子節點時,對consumer地址將協議改成empty,增加對應的category

private List<URL> toUrlsWithEmpty(URL consumer, String path, List<String> providers) {
     List<URL> urls = toUrlsWithoutEmpty(consumer, providers);
      if (urls.isEmpty()) {
      	int i = path.lastIndexOf('/');
      	String category = i < 0 ? path : path.substring(i + 1);
      	URL empty = consumer.setProtocol(Constants.EMPTY_PROTOCOL).addParameter(Constants.CATEGORY_KEY, category);
          urls.add(empty);
      }
      return urls;
  }
  • II)通知子節點發生變化
    見下一節

訂閱節點的子節點發生變化

notify(url, listener, urls);
url:消費者的url
urls: com.test.ITestService的下的providers、routes、configurators變化的子節點。
listener: RegistryDirectory

  • 場景
    1. 在訂閱初始化的時候 (providers、routes、configurators全量)
    url: 當前節點
    consumer://10.118.14.24/com.test.ITestService?category=providers,configurators,routers&default.check=false&default.cluster=failfast&default.timeout=60000&dubbo=2.8.4&side=consumer&timestamp=1577329888735&version=1.0.0…
    listener: RegistryDirectory
    urls:當前com.test.ITestService的下的providers、routes、configurators所有的子節點
    dubbo://10.118.22.51:20710/com.test.ITestService?default.cluster=failfast&default.timeout=3000&dubbo=2.8.4&generic=false&interface=com.test.ITestService&logger=slf4j&methods=test,save&pid=3091&revision=1.0.0&side=provider&timestamp=1576650287438&version=1.0.0
    empty://10.118.14.24/com.test.ITestService?&category=configurators&side=consumer&timestamp=1577329888735&version=1.0.0
    empty://10.118.14.24/com.test.ITestService?&category=routers&side=consumer&timestamp=1577329888735&version=1.0.0
    2. 子節點發生變化時(providers、routes、configurators哪個有變化才調用)
 new ChildListener() {
    public void childChanged(String parentPath, List<String> currentChilds) {
       	ZookeeperRegistry.this.notify(url, listener,
       	      toUrlsWithEmpty(url, parentPath, currentChilds));
       }
   });

providers、routes、configurators任意一個的子節點發生變化就調用notify。
url: 當前的消費者url
currentChilds是所有的子節點路徑
parentPath:providers、routes、configurators

  • 源碼

1. 對當前的url進行分類。按照category(providers、routes、configurators)分類, 並且放入到notified中

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);
        }
    }

2. 保存urls到文件中和properties

private void saveProperties(URL url) {
        if (file == null) {
            return;
        }
        //組裝內容
        StringBuilder buf = new StringBuilder();
         Map<String, List<URL>> categoryNotified = notified.get(url);
         if (categoryNotified != null) {
             for (List<URL> us : categoryNotified.values()) {
                 for (URL u : us) {
                     if (buf.length() > 0) {
                        //空格分隔符
                         buf.append(' ');
                     }
                     buf.append(u.toFullString());
                 }
             }
         }
         //放入到properties
         properties.setProperty(url.getServiceKey(), buf.toString());
         long version = lastCacheChanged.incrementAndGet();
         //保存文件
         if (syncSaveFile) {
             doSaveProperties(version);
         } else {
             registryCacheExecutor.execute(new SaveProperties(version));
         }
    }

先創建dubbo-registry-10.118.22.55.cache.lock 空文件, 對空文件進行上鎖,保存文件。然後再釋放鎖。

public void doSaveProperties(long version) {
  // 當前版本小於最後一次,不再保存
  if(version < lastCacheChanged.get()){
      return;
  }
  if (file == null) {
      return;
  }
  Properties newProperties = new Properties();
  // 保存之前先讀取一遍,防止多個註冊中心之間衝突
  InputStream in = null;
   if (file.exists()) {
       in = new FileInputStream(file);
       newProperties.load(in);
   } 
// 保存
  try {
      newProperties.putAll(properties);
      //先創建dubbo-registry-10.118.22.55.cache.lock 空文件
      File lockfile = new File(file.getAbsolutePath() + ".lock");
      if (!lockfile.exists()) {
      	lockfile.createNewFile();
      }
      //先對空文件進行上鎖
      RandomAccessFile raf = new RandomAccessFile(lockfile, "rw");
      try {
          FileChannel channel = raf.getChannel();
          try {
              FileLock lock = channel.tryLock();
          	if (lock == null) {
                  throw new IOException("");
              }
          	// 保存
              try {
              	if (! file.exists()) {
                      file.createNewFile();
                  }
                  FileOutputStream outputFile = new FileOutputStream(file);  
                  try {
                      newProperties.store(outputFile, "Dubbo Registry Cache");
                  } finally {
                  	outputFile.close();
                  }
              } finally {
              	lock.release();
              }
          } finally {
              channel.close();
          }
      } finally {
          raf.close();
      }
  } catch (Throwable e) {
      if (version < lastCacheChanged.get()) {
          return;
      } else {
          registryCacheExecutor.execute(new SaveProperties(lastCacheChanged.incrementAndGet()));
      }
      logger.warn("Failed to save registry store file, cause: " + e.getMessage(), e);
  }
}

3.listener.notify(categoryList); 刷新本地的invoker
見下一節


RegistryDirectory.notify 刷新本地的invoker列表

  • 分類
    routerUrls : category是routers或者protocol是route
    configuratorUrls : category是configurators或者override是route
    invokerUrls : category是providers
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 ("routers".equals(category) || "route".equals(protocol)) {
        routerUrls.add(url);
    } else if ("configurators".equals(category)  || "override".equals(protocol)) {
        configuratorUrls.add(url);
    } else if (“”providers“”.equals(category)) {
        invokerUrls.add(url);
    } 
}
  • 獲取List<Configurator> configurators
    對configuratorUrls 循環處理, configuratorFactory.getConfigurator(url)

根據協議來判斷來做處理

override=com.alibaba.dubbo.rpc.cluster.configurator.override.OverrideConfiguratorFactory
absent=com.alibaba.dubbo.rpc.cluster.configurator.absent.AbsentConfiguratorFactory
  • 獲取List<Router> routers
    Router router = routerFactory.getRouter(url);
file=com.alibaba.dubbo.rpc.cluster.router.file.FileRouterFactory
script=com.alibaba.dubbo.rpc.cluster.router.script.ScriptRouterFactory
condition=com.alibaba.dubbo.rpc.cluster.router.condition.ConditionRouterFactory
  • 刷新本地的invoker列表
    最終將生成的invoker列表保存在本地urlInvokerMap中
private void refreshInvoker(List<URL> invokerUrls){
     //....
      this.forbidden = false; // 允許訪問
     //舊的invoker列表
      Map<String, Invoker<T>> oldUrlInvokerMap = this.urlInvokerMap; 
      if (invokerUrls.size() ==0 ){
      	return;
      }
      // 將URL列表轉成Invoker列表
      Map<String, Invoker<T>> newUrlInvokerMap = toInvokers(invokerUrls) ;
       // 換方法名映射Invoker列表
      Map<String, List<Invoker<T>>> newMethodInvokerMap = toMethodInvokers(newUrlInvokerMap);
      //沒有新的invoker,則不再處理
      if (newUrlInvokerMap == null || newUrlInvokerMap.size() == 0 ){
          return ;
      }
      
      this.methodInvokerMap = multiGroup ? toMergeMethodInvokerMap(newMethodInvokerMap) : newMethodInvokerMap;
      //保存到本地urlInvokerMap中
      this.urlInvokerMap = newUrlInvokerMap;
      try{
          //銷燬未使用的invoker列表(在舊的invoker列表中,不在新的invoker列表)
          destroyUnusedInvokers(oldUrlInvokerMap,newUrlInvokerMap); 
      }catch (Exception e) {
          logger.warn("destroyUnusedInvokers error. ", e);
      }
 }

重點:獲取新的invoker列表
a) 合併url參數 順序爲override >Consumer > Provider
b) 查看urlInvokerMap中是否有了,沒有重新生成消費端的invoker代理對象
new InvokerDelegete(protocol.refer(serviceType, url), url, providerUrl);見下一節

c)銷燬 未使用的invoker列表( in(老的invoker列表) and not in(新的invoker列表))

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) {
        if (Constants.EMPTY_PROTOCOL.equals(providerUrl.getProtocol())) {
            continue;
        }
        //查看協議是否支持
        if (! ExtensionLoader.getExtensionLoader(Protocol.class).hasExtension(providerUrl.getProtocol())) {
            continue;
        }
        //合併url (提供者的url和消費者的url進行合併)
        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
                //...查看是否增加disabled或者enabled
            	boolean enabled = true;
            	if (enabled) {
            		invoker = new InvokerDelegete<T>(protocol.refer(serviceType, url), url, providerUrl);
            	}
            if (invoker != null) { // 將新的引用放入緩存
                newUrlInvokerMap.put(key, invoker);
            }
        }else {
            newUrlInvokerMap.put(key, invoker);
        }
    }
    keys.clear();
    return newUrlInvokerMap;
}

合併url參數 順序爲override > -D >Consumer > Provider

private URL mergeUrl(URL providerUrl){
   providerUrl = ClusterUtils.mergeUrl(providerUrl, queryMap); // 合併消費端參數
   //對提供者進行處理
   List<Configurator> localConfigurators = this.configurators; 
   if (localConfigurators != null && localConfigurators.size() > 0) {
       for (Configurator configurator : localConfigurators) {
           providerUrl = configurator.configure(providerUrl);
       }
   }
   
   providerUrl = providerUrl.addParameter(Constants.CHECK_KEY, String.valueOf(false)); // 不檢查連接是否成功,總是創建Invoker!
   
   //directoryUrl 與 override 合併是在notify的最後,這裏不能夠處理
   this.overrideDirectoryUrl = this.overrideDirectoryUrl.addParametersIfAbsent(providerUrl.getParameters()); // 合併提供者參數        
   
   return providerUrl;
}

DubboProtocol.refer

ProtocolFilterWrapper:
過濾所有的consumer下的Filter,循環對invoker進行包裝

  • 創建DubboInvoker並且保存在invokers
public <T> Invoker<T> refer(Class<T> serviceType, URL url) throws RpcException {
        optimizeSerialization(url);

        DubboInvoker<T> invoker = new DubboInvoker<T>(serviceType, url, getClients(url), invokers);
        invokers.add(invoker);
        return invoker;
    }
  • 初始化netty客戶端
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){
        String key = url.getAddress();
        ReferenceCountExchangeClient client = referenceClientMap.get(key);
        if ( client != null ){
            if ( !client.isClosed()){
                client.incrementAndGetCount();
                return client;
            } else {
                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) {。
        //默認開啓heartbeat
        url = url.addParameterIfAbsent(Constants.HEARTBEAT_KEY, String.valueOf(Constants.DEFAULT_HEARTBEAT));        
        
        ExchangeClient client ;
        try {
            //設置連接應該是lazy的 
            if (url.getParameter(Constants.LAZY_CONNECT_KEY, false)){
                client = new LazyConnectExchangeClient(url ,requestHandler);
            } else {
                client = Exchangers.connect(url ,requestHandler);
            }
        } catch (RemotingException e) {
            throw new RpcException("Fail to create remoting client for service(" + url
                    + "): " + e.getMessage(), e);
        }
        return client;
    }

netty客戶端連接

創建客戶端連接 Exchangers.connect(url ,requestHandler)
new HeaderExchangeClient(Transporters.connect(url, new DecodeHandler(new HeaderExchangeHandler(handler))))

  • HeaderExchangeClient
    初始化時,開啓定時心跳
    this.channel = new HeaderExchangeChannel(client);
    定時發送心跳數據給服務端,並且超時了,重連服務端
 heatbeatTimer = scheduled.scheduleWithFixedDelay(
     new HeartBeatTask( new HeartBeatTask.ChannelProvider() {
            public Collection<Channel> getChannels() {
                return Collections.<Channel>singletonList( HeaderExchangeClient.this );
            }
        }, heartbeat, heartbeatTimeout),
        heartbeat, heartbeat, TimeUnit.MILLISECONDS );
  //上次讀取的時間
  Long lastRead = ( Long ) channel.getAttribute(
          HeaderExchangeHandler.KEY_READ_TIMESTAMP );
 //上次寫入的時間
  Long lastWrite = ( Long ) channel.getAttribute(
          HeaderExchangeHandler.KEY_WRITE_TIMESTAMP );
  //在時間內, 發送心跳信息給服務端
  if ( ( lastRead != null && now - lastRead > heartbeat )
          || ( lastWrite != null && now - lastWrite > heartbeat ) ) {
      Request req = new Request();
      req.setVersion( "2.0.0" );
      req.setTwoWay( true );
      req.setEvent( Request.HEARTBEAT_EVENT );
      channel.send( req );
  }
  //超時了,重連
  if ( lastRead != null && now - lastRead > heartbeatTimeout ) {
      if (channel instanceof Client) {
      		((Client)channel).reconnect();
      } else {
      	channel.close();
      }
  }
  • netty客戶端初始化 new NettyClient

初始化

 protected void doOpen() throws Throwable {
        NettyHelper.setNettyLoggerFactory();
        bootstrap = new ClientBootstrap(channelFactory);
        // config
        // @see org.jboss.netty.channel.socket.SocketChannelConfig
        bootstrap.setOption("keepAlive", true);
        bootstrap.setOption("tcpNoDelay", true);
        bootstrap.setOption("connectTimeoutMillis", getTimeout());
        final NettyHandler nettyHandler = new NettyHandler(getUrl(), this);
        bootstrap.setPipelineFactory(new ChannelPipelineFactory() {
            public ChannelPipeline getPipeline() {
                NettyCodecAdapter adapter = new NettyCodecAdapter(getCodec(), getUrl(), NettyClient.this);
                ChannelPipeline pipeline = Channels.pipeline();
                pipeline.addLast("decoder", adapter.getDecoder());
                pipeline.addLast("encoder", adapter.getEncoder());
                pipeline.addLast("handler", nettyHandler);
                return pipeline;
            }
        });
    }

連接
啓動定時,查看是否連接,沒有的話,重新連接

protected void connect() throws RemotingException {
        connectLock.lock();
        try {
            if (isConnected()) {
                return;
            }
            //啓動
            initConnectStatusCheckCommand();
            doConnect();
            if (! isConnected()) {
                throw new RemotingException();
            }
            reconnect_count.set(0);
            reconnect_error_log_flag.set(false);
        } finally {
            connectLock.unlock();
        }
    }

等待服務器的響應

protected void doConnect() throws Throwable {
        long start = System.currentTimeMillis();
        ChannelFuture future = bootstrap.connect(getConnectAddress());
        try{
            //等待
            boolean ret = future.awaitUninterruptibly(getConnectTimeout(), TimeUnit.MILLISECONDS);
            //連接成功
            if (ret && future.isSuccess()) {
            
                Channel newChannel = future.getChannel();
                newChannel.setInterestOps(Channel.OP_READ_WRITE);
                try {
                    // 關閉舊的連接
                    Channel oldChannel = NettyClient.this.channel; // copy reference
                    if (oldChannel != null) {
                        try {
                            oldChannel.close();
                        } finally {
                            NettyChannel.removeChannelIfDisconnected(oldChannel);
                        }
                    }
                } finally {
                    //新連接也是關閉的,將新連接關閉
                    if (NettyClient.this.isClosed()) {
                        try {
                            newChannel.close();
                        } finally {
                            NettyClient.this.channel = null;
                            NettyChannel.removeChannelIfDisconnected(newChannel);
                        }
                    } else {
                        //賦值
                        NettyClient.this.channel = newChannel;
                    }
                }
            } else if (future.getCause() != null) {
                throw new RemotingException();
            } else {
                throw new RemotingException();
            }
        }finally{
            if (! isConnected()) {
                future.cancel();
            }
        }
    }
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章