Dubbo調用流程一覽

前言

Apache Dubbo做爲一款高性能的Java RPC框架,在國內服務化體系的演進過程當中扮演了一個很是重要的角色,被大量公司普遍使用。面試

臨近年關,或許有小夥伴有着尋找新機會的想法,那麼在面試過程當中極可能會經常見到這樣一個問題:apache

你瞭解Dubbo嗎 ? 能不能講一講它的調用流程。bash

對於不瞭解的盆友而言,無疑會下降印象分;若是僅僅會使用,其實也不太夠,最起碼咱們要了解它的基本原理。網絡

本文試圖從Dubbo使用者的角度上,結合流程圖和關鍵代碼把相應知識點串聯起來,回答咱們上面的問題。app

1、服務提供者

從程序開發者的角度來看,咱們要先有服務提供者。一般,咱們在具體接口的實現上標註Dubbo的Service註解。負載均衡

package com.viewscenes.producer.dubbo;
import org.apache.dubbo.config.annotation.Service;
import com.viewscenes.common.service.DubboUserService;
@Service
public class DubboUserServiceImpl implements DubboUserService {
}
複製代碼

這個實現類在Dubbo中對應的解析類爲ServiceBean,它負責將這個實現對外暴露成一個服務。過程以下:框架

Dubbo服務暴露過程

結合上圖來看,咱們能夠說在提供者端,暴露一個服務的過程以下:post

首先,ServiceConfig類引用對外提供服務的實現類ref (如DubboUserServiceImpl) , 而後經過ProxyFactoty接口的擴展實現類的getInvoker()方法使用ref生成一個AbstractProxyInvoker實例,到此就完成了具體服務到Invoker的轉化。性能

接下來,經過Dubbo協議的export()方法,將Invoker轉化爲Exporter。那麼在這裏,就會先啓動Netty Server的監聽,而後將服務註冊到服務註冊中心。ui

在這裏,咱們必需要注意的是,做爲服務提供者端,已經經過Netty開啓了TCP端口的監聽。那麼,當消費者調用的時候,經過一系列Netty Handler處理器,就會調用到DubboProtocol > ExchangeHandler.reply()

在這個方法裏,就是一個反推的過程。經過要調用的服務接口名稱,找到Exporter ,而後再獲取到Invoker對象。

Invoker<?> getInvoker(Channel channel, Invocation inv) throws RemotingException {
    int port = channel.getLocalAddress().getPort();
    String path = inv.getAttachments().get(PATH_KEY);
    String serviceKey = serviceKey(port, path, inv.getAttachments().get(VERSION_KEY),  inv.getAttachments().get(GROUP_KEY));
    DubboExporter<?> exporter = (DubboExporter<?>) exporterMap.get(serviceKey);
    return exporter.getInvoker();
}
複製代碼

從上面的分析中咱們已經知道,這裏的Invoker對象是根據服務實現類生成的一個AbstractProxyInvoker實例。它最終會調用到wrapper.invokeMethod()方法。這裏的wrapper類是經過Javassist生成的,在內存中的類,它的核心方法長這樣:

Dubbo會給每一個服務提供者的實現生成一個Wrapper類。當接收到消費方的請求後,根據傳遞的方法名和參數,Wrapper類調用服務提供者的接口類實現便可。這樣作的目的主要是爲了減小反射的調用。

2、服務消費者

在服務消費者端,咱們直接引用一個接口便可。

@Reference
DubboUserService userService;
複製代碼

或許你可能要問,爲啥只注入了這麼一個普通的接口,就能夠調用到遠端的服務呢 ?

咱們想一想在 Mybatis中的Dao接口和XML文件裏的SQL是如何創建關係的? 這個問題中,它們是怎麼關聯起來的呢 ?

說穿了仍是Spring的功勞,或者說是Spring FactoryBean的功勞。

Dubbo中,標註了@Reference的接口,都會被當成一個Factory Bean ,這個Bean通常都會返回一個代理對象,來屏蔽底層一些複雜的操做。好比Mybatis裏的mapper接口和xml文件關聯,Dubbo中的網絡通訊等。

咱們仍是先來經過一張圖看看消費者端的具體過程:

服務引用

結合上圖來看,咱們總結下服務引用的過程:

Reference註解標註的Dubbo接口,會被註冊成FactoryBean,並最終返回一個代理對象。

在建立代理的過程當中,會調用其餘方法構建以及合併 Invoker 實例。

首先,調用DubboProtocol的refer方法,返回DubboInvoker對象。在這裏,比較重要的是獲取客戶端實例。好比NettyClientDubbo要依靠它來進行網絡通訊。

而後,還須要將多個服務提供者實例合併成一個,這是集羣容錯機制的實現。

最後,經過JavassistProxyFactory建立代理並返回。在這裏,它的處理器是InvokerInvocationHandler ,這就意味着,當咱們在消費者端調用一個Dubbo接口的時候,實際上會調用到InvokerInvocationHandler.invoke()方法,在這裏面Dubbo完成了譬如集羣容錯、負載均衡、調用遠程方法的一系列動做。

Dubbo消費者發送請求的時候,最終會調用到DubboInvoker中的方法。在這裏,會完成具體的請求邏輯,好比發送請求數據。

final class HeaderExchangeChannel implements ExchangeChannel {
    //建立請求消息對象
    Request req = new Request();
    req.setVersion(Version.getProtocolVersion());
    req.setTwoWay(true);
    req.setData(request);
    
    //建立Future,用於獲取返回結果
    DefaultFuture future = DefaultFuture.newFuture(this.channel, req, timeout, executor);
    try {
        //經過Netty客戶端發送數據
        this.channel.send(req);
        return future;
    } catch (RemotingException var7) {
    	future.cancel();
    	throw var7;
    }
}
複製代碼

3、請求-響應過程

Dubbo請求響應過程
相關文章
相關標籤/搜索