Apache Dubbo做爲一款高性能的Java RPC框架,在國內服務化體系的演進過程當中扮演了一個很是重要的角色,被大量公司普遍使用。面試
臨近年關,或許有小夥伴有着尋找新機會的想法,那麼在面試過程當中極可能會經常見到這樣一個問題:apache
你瞭解Dubbo嗎 ? 能不能講一講它的調用流程。bash
對於不瞭解的盆友而言,無疑會下降印象分;若是僅僅會使用,其實也不太夠,最起碼咱們要了解它的基本原理。網絡
本文試圖從Dubbo使用者的角度上,結合流程圖和關鍵代碼把相應知識點串聯起來,回答咱們上面的問題。app
從程序開發者的角度來看,咱們要先有服務提供者。一般,咱們在具體接口的實現上標註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
,它負責將這個實現對外暴露成一個服務。過程以下:框架
結合上圖來看,咱們能夠說在提供者端,暴露一個服務的過程以下: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
類調用服務提供者的接口類實現便可。這樣作的目的主要是爲了減小反射的調用。
在服務消費者端,咱們直接引用一個接口便可。
@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
對象。在這裏,比較重要的是獲取客戶端實例。好比NettyClient
,Dubbo
要依靠它來進行網絡通訊。
而後,還須要將多個服務提供者實例合併成一個,這是集羣容錯機制的實現。
最後,經過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;
}
}
複製代碼