Dubbo之服務消費

Dubbo的服務消費主要包括兩個部分。第一大步是ReferenceConfig類的init方法調用Protocolrefer方法生成Invoker實例,這是服務消息的關鍵。第二大步是把Invoker經過動態代理轉換成實現用戶接口的動態代理引用。這裏的Invoker承載了網絡鏈接、服務調用和重試等功能。java

服務暴露起點

在消費者的配置文件中存在這個代碼:apache

<!-- 生成遠程服務代理,能夠和本地bean同樣使用demoService -->
<dubbo:reference id="demoService" interface="org.apache.dubbo.demo.DemoService" />

它會生成一個ReferenceBean,實現了FactoryBean接口,繼承了ReferenceConfig,因此ReferenceBean做爲dubbo中可以生產對象的工廠Bean,而咱們要引用服務,也就要有一個該服務的對象。服務器

public class ReferenceBean<T> extends ReferenceConfig<T> implements FactoryBean,
        ApplicationContextAware, InitializingBean, DisposableBean {

服務引用被觸發有兩個時機:網絡

  • Spring容器調用ReferenceBean的afterPropertiesSet方法時引用服務(餓漢式)
  • 在ReferenceBean對應的服務被注入到其餘類中時引用(懶漢式)

默認狀況下,Dubbo使用懶漢式引用服務。若是須要使用餓漢式,可經過配置dubbo:reference的init屬性開啓。app

由於ReferenceBean實現了FactoryBean接口的getObject()方法,因此在加載bean的時候,會調用ReferenceBean的getObject()方法。異步

ReferenceBean.getObject()  --->  ReferenceConfig.get()   --> ReferenceConfig.init()

在init()方法中主要有這幾步:
(1) 檢測本地存根和mock合法性
(2) 添加協議版本、發佈版本、時間戳、application、module、consumer、protocol等全部信息到map中。
(3) 單獨處理方法配置,設置重試次數配置以及設置該方法對異步配置信息。
(4) 添加消費者ip地址到map
(5)建立代理對象,調用ReferenceConfig.createProxy()方法。
(6) 生成ConsumerMpdel存入到ApplicationModel中。jvm

以後介紹ReferenceConfig.createProxy()方法。主要有下面幾步:ide

  • 若是是本地調用,則直接使用InjvmProtocol的refer方法生成Invoker實例。
  • 若是不是本地調用,可是選擇直連的方式進行調用,則分割配置的多個url。若是協議是配置registry,則代表用戶想使用指定的註冊中心,配置url後將url保存到urls裏面,不然就合併url,而且保存到urls。
  • 若是是經過註冊中心來進行調用,則先校驗全部的註冊中心,而後假加載註冊中心的url,遍歷每一個url,加入監控中心url配置,最後把每一個url保存到urls。
  • 若是urls的個數是1,是單註冊中心,直接引用RegistryProtocol的refer構建Invoker實例;若是urls的數量大於1,說明是多註冊中心,則對每一個url都生成Invoker,利用cluster.join()方法將多個Invoker進行合併成一個Invoker。
  • 最後調用proxyFactory.getProxy(invoker)方法。

而後介紹RegistryProtocol.refer(Class<T> type, URL url)方法生成invoker。url

若是是註冊中心服務,則直接返回註冊中心服務的invoker;若是不是,則先處理組配置,根據組配置來決定Cluster的實現方式,若是有多個組,則使用MergeableCluster,而後調用doRefer(Cluster, Registry, Class, URL)方法。代理

而後介紹doRefer()方法。
(1) 建立一個RegistryDirectory實例,設置註冊中心、協議等信息
(2) 生成服務消費者連接。
(3) 註冊消費信息到註冊中心。
(4) 訂閱該服務下的providers、configurators、routers等節點下的數據。完成訂閱後,RegistryDirectory會受到這幾個節點下的子節點信息。
(5) 因爲一個服務可能部署在多臺服務器上,這樣就會在providers產生多個節點,這個時候就須要Cluster將多個服務節點合併成一個,並生成一個Invoker.

在RegistryDirectory實現了NotifyListener接口,服務變動會觸發這個類回調notify方法,用於從新引用服務。當發起訂閱請求時會進行一次數據拉取操做,同時觸發RegistryDirectory.nofity()方法。這是會執行toInvokers()方法進行Invoker轉換。
(1) 根據消費者protocol配置過濾不匹配的協議。
(2) 合併provider端配置數據,好比服務端IP和port等。
(3) 忽略重複推送的服務列表
(4) 使用具體協議建立遠程鏈接,new InvokerDelegate<T>(protocol.refer(serviceType, url), url, providerUrl)

具體的Invoker建立是在DubboProtocol.refer()中實現。Dubbo協議在返回DubboInvoker對象以前會初始化客戶端鏈接對象。
調用DubboProtocol.initClient()方法 -> Exchangers.connect()方法,根據SPI機制加載HeaderExchangeClent,調用connect()方法。而後調用Transporter類的connect()方法,默認是NettyTransporter類。

public class DubboInvoker<T> extends AbstractInvoker<T> {
    private final ExchangeClient[] clients;
    private final AtomicPositiveInteger index = new AtomicPositiveInteger();
    private final String version;
    private final ReentrantLock destroyLock = new ReentrantLock();
    private final Set<Invoker<?>> invokers;
public abstract class AbstractInvoker<T> implements Invoker<T> {
    protected final Logger logger = LoggerFactory.getLogger(getClass());
    private final Class<T> type;
    private final URL url;
    private final Map<String, Object> attachment;
    private volatile boolean available = true;
    private AtomicBoolean destroyed = new AtomicBoolean(false);
相關文章
相關標籤/搜索