源碼解讀Dubbo分層設計思想
時間:2021-09-16 14:17:49
手機看文章
掃描二維碼
隨時隨地手機看文章
[導讀]I作者:vivo互聯(lián)網(wǎng)服務器團隊-WangGenfu一、Dubbo分層整體設計概述我們先從下圖開始簡單介紹Dubbo分層設計概念:(引用自Duboo開發(fā)指南-框架設計文檔)如圖描述Dubbo實現(xiàn)的RPC整體分10層:service、config、proxy、registry、cl...
I作者:vivo互聯(lián)網(wǎng)服務器團隊-Wang Genfu
一、Dubbo分層整體設計概述
我們先從下圖開始簡單介紹Dubbo分層設計概念:

如圖描述Dubbo實現(xiàn)的RPC整體分10層:service、config、proxy、registry、cluster、monitor、protocol、exchange、transport、serialize。
- service:使用方定義的接口和實現(xiàn)類;
- config:負責解析Dubbo定義的配置,比如注解和xml配置,各種參數(shù);
- proxy:主要負責生成消費者和提供者的代理對象,加載框架功能,比如提供者過濾器鏈,擴展點;
- registry:負責注冊服務的定義和實現(xiàn)類的裝載;
- cluster:只有消費者有這么一層,負責包裝多個服務提供者成一個‘大提供者’,加載負載均衡、路有等擴展點;
- monitor:定義監(jiān)控服務,加載監(jiān)控實現(xiàn)提供者;
- protocol:封裝RPC調(diào)用接口,管理調(diào)用實體的生命周期;
- exchange:封裝請求響應模式,同步轉(zhuǎn)異步;
- transport:抽象傳輸層模型,兼容netty、mina、grizzly等通訊框架;
- serialize:抽象序列化模型,兼容多種序列化框架,包括:fastjson、fst、hessian2、kryo、kryo2、protobuf等,通過序列化支持跨語言的方式,支持跨語言的rpc調(diào)用;
Dubbo這么分層的目的在于實現(xiàn)層與層之間的解耦,每一層都定義了接口規(guī)范,也可以根據(jù)不同的業(yè)務需求定制、加載不同的實現(xiàn),具有極高的擴展性。
1.1. RPC調(diào)用過程
接下來結(jié)合上圖簡單描述一次完整的rpc調(diào)用過程:
從Dubbo分層的角度看,詳細時序圖如下,藍色部分是服務消費端,淺綠色部分是服務提供端,時序圖從消費端一次Dubbo方法調(diào)用開始,到服務端本地方法執(zhí)行結(jié)束。

從Dubbo核心領域?qū)ο蟮慕嵌瓤?,我們引?span>Dubbo官方文檔說明,如下圖所示。Dubbo核心領域?qū)ο笫荌nvoker,消費端代理對象是proxy,包裝了Invoker的調(diào)用;服務端代理對象是一個Invoker,他通過exporter包裝,當服務端接收到調(diào)用請求后,通過exporter找到Invoker,Invoker去實際執(zhí)行用戶的業(yè)務邏輯。

1.2 Dubbo服務的注冊和發(fā)現(xiàn)流程
下圖出自開發(fā)指南-框架設計-引用服務時序,主要流程是:從注冊中心訂閱服務提供者,然后啟動tcp服務連接遠端提供者,將多個服務提供者合并成一個Invoker,用這個Invoker創(chuàng)建代理對象。

下圖出自開發(fā)指南-框架設計-暴露服務時序,主要流程是:創(chuàng)建本地服務的代理Invoker,啟動tcp服務暴露服務,然后將服務注冊到注冊中心。

接下來我們結(jié)合Dubbo服務的注冊和發(fā)現(xiàn),從配置層開始解釋每一層的作用和原理。
示例服務接口定義如下:
public interface CouponServiceViewFacade {
/**
* 查詢單張優(yōu)惠券
*/
CouponViewDTO query(String code);
}
二、配置層
2.1. 做什么
配置層提供配置處理工具類,在容器啟動的時候,通過ServiceConfig.export實例化服務提供者,ReferenceConfig.get實例化服務消費者對象。
Dubbo應用使用spring容器啟動時,Dubbo服務提供者配置處理器通過ServiceConfig.export啟動Dubbo遠程服務暴露本地服務。Dubbo服務消費者配置處理器通過ReferenceConfig.get實例化一個代理對象,并通過注冊中心服務發(fā)現(xiàn),連接遠端服務提供者。
Dubbo配置可以使用注解和xml兩種形式,本文采用注解的形式進行說明。
2.2. 怎么做
2.2.1 服務消費端的解析
Spring容器啟動過程中,填充bean屬性時,對含有Dubbo引用注解的屬性使用org.apache.dubbo.config.spring.beans.factory.annotation.ReferenceAnnotationBeanPostProcessor進行初始化。
如下是ReferenceAnnotationBeanPostProcessor的構造方法,Dubbo服務消費者注解處理器處理以下三個注解:DubboReference.class,?Reference.class,?com.alibaba.dubbo.config.annotation.Reference.class修飾的類。
ReferenceAnnotationBeanPostProcessor類定義:
public class ReferenceAnnotationBeanPostProcessor extends AbstractAnnotationBeanPostProcessor implements
ApplicationContextAware {
public ReferenceAnnotationBeanPostProcessor() {
super(DubboReference.class, Reference.class, com.alibaba.dubbo.config.annotation.Reference.class);
}
}
Dubbo服務發(fā)現(xiàn)到這一層,Dubbo即將開始構建服務消費者的代理對象,CouponServiceViewFacade接口的代理實現(xiàn)類。
2.2.2 服務提供端的解析
Spring容器啟動的時候,加載注解@org.apache.dubbo.config.spring.context.annotation.DubboComponentScan指定范圍的類,并初始化;初始化使用dubbo實現(xiàn)的擴展點org.apache.dubbo.config.spring.beans.factory.annotation.ServiceClassPostProcessor。
ServiceClassPostProcessor處理的注解類有DubboService.class,Service.class,com.alibaba.dubbo.config.annotation.Service.class。
如下是ServiceClassPostProcessor類定義:
public class ServiceClassPostProcessor implements BeanDefinitionRegistryPostProcessor, EnvironmentAware,
ResourceLoaderAware, BeanClassLoaderAware {
private final static List> serviceAnnotationTypes = asList(
DubboService.class,Service.class,com.alibaba.dubbo.config.annotation.Service.class
);
。。。
}
等待Spring容器ContextRefreshedEvent事件,啟動Dubbo應用服務監(jiān)聽端口,暴露本地服務。
Dubbo服務注冊到這一層,Dubbo即將開始構建服務提供者的代理對象,CouponServiceViewFacade實現(xiàn)類的反射代理類。
三、?代理層
3.1 做什么
為服務消費者生成代理實現(xiàn)實例,為服務提供者生成反射代理實例。
CouponServiceViewFacade的代理實現(xiàn)實例,消費端在調(diào)用query方法的時候,實際上是調(diào)用代理實現(xiàn)實例的query方法,通過他調(diào)用遠程服務。
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//
package org.apache.dubbo.common.bytecode;
public class proxy1 implements DC, Destroyable, CouponServiceViewFacade, EchoService {
public static Method[] methods;
private InvocationHandler handler;
public proxy1(InvocationHandler var1) {
this.handler = var1;
}
public proxy1() {
}
public CouponViewDTO query(String var1) {
Object[] var2 = new Object[]{var1};
Object var3 = this.handler.invoke(this, methods[0], var2);
return (CouponViewDTO)var3;
}
}
CouponServiceViewFacade的反射代理實例,服務端接收到請求后,通過該實例的Invoke方法最終執(zhí)行本地方法query。
/**
* InvokerWrapper
*/
public class AbstractProxyInvoker implements Invoker {
// 。。。
public AbstractProxyInvoker(CouponServiceViewFacade proxy, Class type, URL url) {
//。。。
this.proxy = proxy;
this.type = type;
this.url = url;
}
@Override
public Result invoke(Invocation invocation) throws RpcException {
//。。。
Object value = doInvoke(proxy, invocation.getMethodName(), invocation.getParameterTypes(), invocation.getArguments());
//。。。
}
protected Object doInvoke(CouponServiceViewFacade proxy, String methodName, Class>[] parameterTypes, Object[] arguments) throws Throwable{
//。。。
return wrapper.invokeMethod(proxy, methodName, parameterTypes, arguments);
}
}
3.2 怎么做
Dubbo代理工廠接口定義如下,定義了服務提供者和服務消費者的代理對象工廠方法。服務提供者代理對象和服務消費者代理對象都是通過工廠方法創(chuàng)建,工廠實現(xiàn)類可以通過SPI自定義擴展。
@SPI("javassist")
public interface ProxyFactory {
// 生成服務消費者代理對象
@Adaptive({PROXY_KEY})
T getProxy(Invoker invoker) throws RpcException;
// 生成服務消費者代理對象
@Adaptive({PROXY_KEY})
T getProxy(Invoker invoker, boolean generic) throws RpcException;
// 生成服務提供者代理對象
@Adaptive({PROXY_KEY})
Invoker getInvoker(T proxy, Class type, URL url) throws RpcException ;
}
3.2.1 服務消費者
3.2.1.1 創(chuàng)建服務消費者代理類
默認采用Javaassist代理工廠實現(xiàn),Proxy.getProxy(interfaces)創(chuàng)建代理工廠類,newInstance創(chuàng)建具體代理對象。
public class JavassistProxyFactory extends AbstractProxyFactory {
@Override
@SuppressWarnings("unchecked")
public T getProxy(Invoker invoker, Class>[] interfaces) {
return (T) Proxy.getProxy(interfaces).newInstance(new InvokerInvocationHandler(invoker));
}
。。。
}
3.2.1.2 服務消費者代理
Dubbo為每個服務消費者生成兩個代理類:代理工廠類,接口代理類。
CouponServiceViewFacade代理工廠類:
public class Proxy1 extends Proxy implements DC {
public Proxy1() {
}
public Object newInstance(InvocationHandler var1) {
return new proxy1(var1);
}
}
最終生成的CouponServiceViewFacade的代理對象如下,其中handler的實現(xiàn)類是InvokerInvocationHandler,this.handler.invoke方法發(fā)起Dubbo調(diào)用。
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//
package org.apache.dubbo.common.bytecode;
public class proxy1 implements DC, Destroyable, CouponServiceViewFacade, EchoService {
public static Method[] methods;
private InvocationHandler handler;
public proxy1(InvocationHandler var1) {
this.handler = var1;
}
public proxy1() {
}
public CouponViewDTO query(String var1) {
Object[] var2 = new Object[]{var1};
Object var3 = this.handler.invoke(this, methods[0], var2);
return (CouponViewDTO)var3;
}
}
3.2.2 服務提供者
3.2.2.1 創(chuàng)建服務提供者代理類
默認Javaassist代理工廠實現(xiàn),使用Wrapper包裝本地服務提供者。proxy是實際的服務提供者實例,即CouponServiceViewFacade的本地實現(xiàn)類,type是接口類定義,URL是injvm協(xié)議URL。
public class JavassistProxyFactory extends AbstractProxyFactory {
。。。
@Override
public Invoker getInvoker(T proxy, Class type, URL url) {
// 代理包裝類,包裝了本地的服務提供者
final Wrapper wrapper = Wrapper.getWrapper(proxy.getClass().getName().indexOf('$') < 0 ? proxy.getClass() : type);
// 代理類入口
return new AbstractProxyInvoker(proxy, type, url) {
@Override
protected Object doInvoke(T proxy, String methodName,
Class>[] parameterTypes,
Object[] arguments) throws Throwable {
return wrapper.invokeMethod(proxy, methodName, parameterTypes, arguments);
}
};
}
}
3.2.2.2 Wrapper包裝類
Dubbo為每個服務提供者的本地實現(xiàn)生成一個Wrapper代理類,抽象Wrapper類定義如下:
public abstract class Wrapper {
。。。
abstract public Object invokeMethod(Object instance, String mn, Class>[] types, Object[] args) throws NoSuchMethodException, InvocationTargetException;
}
具體Wrapper代理類使用字節(jié)碼技術動態(tài)生成,本地服務CouponServiceViewFacade的代理包裝類舉例:
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//
package org.apache.dubbo.common.bytecode;
import com.xxx.CouponServiceViewFacade;
import java.lang.reflect.InvocationTargetException;
import java.util.Map;
import org.apache.dubbo.common.bytecode.ClassGenerator.DC;
public class Wrapper25 extends Wrapper implements DC {
。。。
public Wrapper25() {
}
public Object invokeMethod(Object var1, String var2, Class[] var3, Object[] var4) throws InvocationTargetException {
CouponServiceViewFacade var5;
try {
var5 = (CouponServiceViewFacade)var1;
} catch (Throwable var8) {
throw new IllegalArgumentException(var8);
}
try {
if ("query".equals(var2)