如圖描述Dubbo實現的RPC整體分10層:service、config、proxy、registry、cluster、monitor、protocol、exchange、transport、serialize。
service:使用方定義的接口和實現類;
config:負責解析Dubbo定義的配置,比如注解和xml配置,各種參數;
proxy:主要負責生成消費者和提供者的代理對象,加載框架功能,比如提供者過濾器鏈,擴展點;
registry:負責注冊服務的定義和實現類的裝載;
cluster:只有消費者有這么一層,負責包裝多個服務提供者成一個‘大提供者’,加載負載均衡、路有等擴展點;
monitor:定義監控服務,加載監控實現提供者;
protocol:封裝RPC調用接口,管理調用實體的生命周期;
exchange:封裝請求響應模式,同步轉異步;
transport:抽象傳輸層模型,兼容netty、mina、grizzly等通訊框架;
serialize:抽象序列化模型,兼容多種序列化框架,包括:fastjson、fst、hessian2、kryo、kryo2、protobuf等,通過序列化支持跨語言的方式,支持跨語言的rpc調用;
Dubbo這么分層的目的在于實現層與層之間的解耦,每一層都定義了接口規范,也可以根據不同的業務需求定制、加載不同的實現,具有極高的擴展性。
1,RPC調用過程
接下來結合上圖簡單描述一次完整的rpc調用過程:
從Dubbo分層的角度看,詳細時序圖如下,藍色部分是服務消費端,淺綠色部分是服務提供端,時序圖從消費端一次Dubbo方法調用開始,到服務端本地方法執行結束。
從Dubbo核心領域對象的角度看,我們引用Dubbo官方文檔說明,如下圖所示。Dubbo核心領域對象是Invoker,消費端代理對象是proxy,包裝了Invoker的調用;服務端代理對象是一個Invoker,他通過exporter包裝,當服務端接收到調用請求后,通過exporter找到Invoker,Invoker去實際執行用戶的業務邏輯。
2,Dubbo服務的注冊和發現流程
主要流程是:從注冊中心訂閱服務提供者,然后啟動tcp服務連接遠端提供者,將多個服務提供者合并成一個Invoker,用這個Invoker創建代理對象。
下圖出自開發指南-框架設計-暴露服務時序,主要流程是:創建本地服務的代理Invoker,啟動tcp服務暴露服務,然后將服務注冊到注冊中心。
結合Dubbo服務的注冊和發現,從配置層開始解釋每一層的作用和原理。
示例服務接口定義如下:
public interface CouponServiceViewFacade {
? ? /**
? ? * 查詢單張優惠券
? ? */
? ? CouponViewDTO query(String code);
}
二:配置層:
1,做什么
配置層提供配置處理工具類,在容器啟動的時候,通過ServiceConfig.export實例化服務提供者,ReferenceConfig.get實例化服務消費者對象。
Dubbo應用使用spring容器啟動時,Dubbo服務提供者配置處理器通過ServiceConfig.export啟動Dubbo遠程服務暴露本地服務。Dubbo服務消費者配置處理器通過ReferenceConfig.get實例化一個代理對象,并通過注冊中心服務發現,連接遠端服務提供者。
Dubbo配置可以使用注解和xml兩種形式,本文采用注解的形式進行說明。
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服務發現到這一層,Dubbo即將開始構建服務消費者的代理對象,CouponServiceViewFacade接口的代理實現類。
2.2服務提供端的解析
Spring容器啟動的時候,加載注解@org.apache.dubbo.config.spring.context.annotation.DubboComponentScan指定范圍的類,并初始化;初始化使用dubbo實現的擴展點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<Class<? extends Annotation>> serviceAnnotationTypes = asList(
? ? ? ? ? ? DubboService.class,Service.class,com.alibaba.dubbo.config.annotation.Service.class
? ? );
。。。
}
等待Spring容器ContextRefreshedEvent事件,啟動Dubbo應用服務監聽端口,暴露本地服務。
Dubbo服務注冊到這一層,Dubbo即將開始構建服務提供者的代理對象,CouponServiceViewFacade實現類的反射代理類。
三:代理層:
為服務消費者生成代理實現實例,為服務提供者生成反射代理實例。
CouponServiceViewFacade的代理實現實例,消費端在調用query方法的時候,實際上是調用代理實現實例的query方法,通過他調用遠程服務。
//
// 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方法最終執行本地方法query。
/**
* InvokerWrapper
*/
public class AbstractProxyInvoker<CouponServiceViewFacade> implements Invoker<CouponServiceViewFacade> {
? ? ? ? // 。。。
? ? public AbstractProxyInvoker(CouponServiceViewFacade proxy, Class<CouponServiceViewFacade> 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);
? ? }
}
2,怎么做?
Dubbo代理工廠接口定義如下,定義了服務提供者和服務消費者的代理對象工廠方法。服務提供者代理對象和服務消費者代理對象都是通過工廠方法創建,工廠實現類可以通過SPI自定義擴展。
@SPI("javassist")
public interface ProxyFactory {
? ? // 生成服務消費者代理對象
? ? @Adaptive({PROXY_KEY})
? ? <T> T getProxy(Invoker<T> invoker) throws RpcException;
? ? // 生成服務消費者代理對象
? ? @Adaptive({PROXY_KEY})
? ? <T> T getProxy(Invoker<T> invoker, boolean generic) throws RpcException;
? ? // 生成服務提供者代理對象
? ? @Adaptive({PROXY_KEY})
? ? <T> Invoker<T> getInvoker(T proxy, Class<T> type, URL url) throws RpcException;
}
2.1服務消費者
2.1.1 創建服務消費者代理類
默認采用Javaassist代理工廠實現,Proxy.getProxy(interfaces)創建代理工廠類,newInstance創建具體代理對象。
public class JavassistProxyFactory extends AbstractProxyFactory {
? ? @Override
? ? @SuppressWarnings("unchecked")
? ? public <T> T getProxy(Invoker<T> invoker, Class<?>[] interfaces) {
? ? ? ? return (T) Proxy.getProxy(interfaces).newInstance(new InvokerInvocationHandler(invoker));
? ? }
? ? 。。。
}
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的實現類是InvokerInvocationHandler,this.handler.invoke方法發起Dubbo調用。
//
// 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;
? ? }
}
2.2 服務提供者
2.2.1 創建服務提供者代理類
默認Javaassist代理工廠實現,使用Wrapper包裝本地服務提供者。proxy是實際的服務提供者實例,即CouponServiceViewFacade的本地實現類,type是接口類定義,URL是injvm協議URL。
public class JavassistProxyFactory extends AbstractProxyFactory {
? ? 。。。
? ? @Override
? ? public <T> Invoker<T> getInvoker(T proxy, Class<T> type, URL url) {
? ? ? ? // 代理包裝類,包裝了本地的服務提供者
? ? ? ? final Wrapper wrapper = Wrapper.getWrapper(proxy.getClass().getName().indexOf('$') < 0 ? proxy.getClass() : type);
? ? ? ? // 代理類入口
? ? ? ? return new AbstractProxyInvoker<T>(proxy, type, url) {
? ? ? ? ? ? @Override
? ? ? ? ? ? protected Object doInvoke(T proxy, String methodName,
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? Class<?>[] parameterTypes,
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? Object[] arguments) throws Throwable {
? ? ? ? ? ? ? ? return wrapper.invokeMethod(proxy, methodName, parameterTypes, arguments);
? ? ? ? ? ? }
? ? ? ? };
? ? }
}
2.2.2 Wrapper包裝類
Dubbo為每個服務提供者的本地實現生成一個Wrapper代理類,抽象Wrapper類定義如下:
public abstract class Wrapper {
? ? 。。。
? ? abstract public Object invokeMethod(Object instance, String mn, Class<?>[] types, Object[] args) throws NoSuchMethodException, InvocationTargetException;
}
具體Wrapper代理類使用字節碼技術動態生成,本地服務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) && var3.length == 1) {
? ? ? ? ? ? ? ? return var5.query((String)var4[0]);
? ? ? ? ? ? }
? ? ? ? } catch (Throwable var9) {
? ? ? ? ? ? throw new InvocationTargetException(var9);
? ? ? ? }
? ? ? ? throw new NoSuchMethodException("Not found method \"" + var2 + "\" in class com.xxx.CouponServiceViewFacade.");
? ? }
。。。
}
在服務初始化流程中,服務消費者代理對象生成后初始化就完成了,服務消費端的初始化順序:ReferenceConfig.get->從注冊中心訂閱服務->啟動客戶端->創建DubboInvoker->構建ClusterInvoker→創建服務代理對象;
而服務提供端的初始化才剛開始,服務提供端的初始化順序:ServiceConfig.export->創建AbstractProxyInvoker,通過Injvm協議關聯本地服務->啟動服務端→注冊服務到注冊中心。
四、注冊層
4.1 做什么
封裝服務地址的注冊與發現,以服務 URL 為配置中心。服務提供者本地服務啟動成功后,監聽Dubbo端口成功后,通過注冊協議發布到注冊中心;服務消費者通過注冊協議訂閱服務,啟動本地應用連接遠程服務。
注冊協議URL舉例:
zookeeper://xxx/org.apache.dubbo.registry.RegistryService?application=xxx&...
4.2 怎么做
注冊服務工廠接口定義如下,注冊服務實現通過SPI擴展,默認是zk作為注冊中心。
@SPI("dubbo")
public interface RegistryFactory {
? ? @Adaptive({"protocol"})
? ? Registry getRegistry(URL url);
}
注冊服務接口定義;
public interface RegistryService {
? ? void register(URL url);
? ? void unregister(URL url);
? ? void subscribe(URL url, NotifyListener listener);
? ? void unsubscribe(URL url, NotifyListener listener);
? ? List<URL> lookup(URL url);
}
五、集群層
5.1 做什么
服務消費方從注冊中心訂閱服務提供者后,將多個提供者包裝成一個提供者,并且封裝路由及負載均衡策略;并橋接注冊中心,以 Invoker 為中心,擴展接口為 Cluster, Directory, Router, LoadBalance;
服務提供端不存在集群層。
5.2 怎么做
5.2.1 Cluster
集群領域主要負責將多個服務提供者包裝成一個ClusterInvoker,注入路由處理器鏈和負載均衡策略。主要策略有:failover、failfast、failsafe、failback、forking、available、mergeable、broadcast、zone-aware。
集群接口定義如下,只有一個方法:從服務目錄中的多個服務提供者構建一個ClusterInvoker。
作用是對上層-代理層屏蔽集群層的邏輯;代理層調用服務方法只需執行Invoker.invoke,然后通過ClusterInvoker內部的路由策略和負載均衡策略計算具體執行哪個遠端服務提供者。
@SPI(Cluster.DEFAULT)
public interface Cluster {
? ? String DEFAULT = FailoverCluster.NAME;
? ? @Adaptive
? ? <T> Invoker<T> join(Directory<T> directory) throws RpcException;
? 。。。
}
ClusterInvoker執行邏輯,先路由策略過濾,然后負載均衡策略選擇最終的遠端服務提供者。示例代理如下:
public abstract class AbstractClusterInvoker<T> implements ClusterInvoker<T> {
。。。
? ? @Override
? ? public Result invoke(final Invocation invocation) throws RpcException {
? ? ? ? checkWhetherDestroyed();
? ? ? ? // binding attachments into invocation.
? ? ? ? Map<String, Object> contextAttachments = RpcContext.getContext().getObjectAttachments();
? ? ? ? if (contextAttachments != null && contextAttachments.size() != 0) {
? ? ? ? ? ? ((RpcInvocation) invocation).addObjectAttachments(contextAttachments);
? ? ? ? }
? ? ? ? // 集群invoker執行時,先使用路由鏈過濾服務提供者
? ? ? ? List<Invoker<T>> invokers = list(invocation);
? ? ? ? LoadBalance loadbalance = initLoadBalance(invokers, invocation);
? ? ? ? RpcUtils.attachInvocationIdIfAsync(getUrl(), invocation);
? ? ? ? return doInvoke(invocation, invokers, loadbalance);
? ? }
。。。
}
5.2.2 Directory
服務目錄接口定義如下,Dubbo方法接口調用時,將方法信息包裝成invocation,通過Directory.list過濾可執行的遠端服務。
通過org.apache.dubbo.registry.integration.RegistryDirectory橋接注冊中心,監聽注冊中心的路由配置修改、服務治理等事件。
public interface Directory<T> extends Node {
? ? Class<T> getInterface();
? ? List<Invoker<T>> list(Invocation invocation) throws RpcException;
? ? List<Invoker<T>> getAllInvokers();
? ? URL getConsumerUrl();
}
5.2.3 Router
從已知的所有服務提供者中根據路由規則刷選服務提供者。
服務訂閱的時候初始化路由處理器鏈,調用遠程服務的時候先使用路由鏈過濾服務提供者,再通過負載均衡選擇具體的服務節點。
路由處理器鏈工具類,提供路由篩選服務,監聽更新服務提供者。
public class RouterChain<T> {
。。。
? ? public List<Invoker<T>> route(URL url, Invocation invocation) {
? ? ? ? List<Invoker<T>> finalInvokers = invokers;
? ? ? ? for (Router router : routers) {
? ? ? ? ? ? finalInvokers = router.route(finalInvokers, url, invocation);
? ? ? ? }
? ? ? ? return finalInvokers;
? ? }
? ? /**
? ? * Notify router chain of the initial addresses from registry at the first time.
? ? * Notify whenever addresses in registry change.
? ? */
? ? public void setInvokers(List<Invoker<T>> invokers) {
? ? ? ? //路由鏈監聽更新服務提供者
? ? ? ? this.invokers = (invokers == null ? Collections.emptyList() : invokers);
? ? ? ? routers.forEach(router -> router.notify(this.invokers));
? ? }
}
訂閱服務的時候,將路由鏈注入到RegistryDirectory中;
public class RegistryProtocol implements Protocol {
? ? 。。。
? ? private <T> Invoker<T> doRefer(Cluster cluster, Registry registry, Class<T> type, URL url) {
? ? ? ? 。。。
? ? ? ? // 服務目錄初始化路由鏈
? ? ? ? directory.buildRouterChain(subscribeUrl);
? ? ? ? directory.subscribe(toSubscribeUrl(subscribeUrl));
? ? ? ? 。。。
? ? ? ? return registryInvokerWrapper;
? ? }
? ? 。。。
}
5.2.4 LoadBalance
根據不同的負載均衡策略從可使用的遠端服務實例中選擇一個,負責均衡接口定義如下:
@SPI(RandomLoadBalance.NAME)
public interface LoadBalance {
? ? @Adaptive("loadbalance")
? ? <T> Invoker<T> select(List<Invoker<T>> invokers, URL url, Invocation invocation) throws RpcException;
}
六、監控層
6.1 做什么
監控RPC調用次數和調用時間,以Statistics為中心,擴展接口為 MonitorFactory, Monitor, MonitorService。
6.2 怎么做
監控工廠接口定義,通過SPI方式進行擴展;
@SPI("dubbo")
public interface MonitorFactory {
? ? @Adaptive("protocol")
? ? Monitor getMonitor(URL url);
}
@Adaptive("protocol")
Monitor getMonitor(URL url);
監控服務接口定義如下,定義了一些默認的監控維度和指標項;
public interface MonitorService {
? ? // 監控維度
? ? String APPLICATION = "application";
? ? String INTERFACE = "interface";
? ? String METHOD = "method";
? ? String GROUP = "group";
? ? String VERSION = "version";
? ? String CONSUMER = "consumer";
? ? String PROVIDER = "provider";
? ? String TIMESTAMP = "timestamp";
? ? //監控指標項
? ? String SUCCESS = "success";
? ? String FAILURE = "failure";
? ? String INPUT = INPUT_KEY;
? ? String OUTPUT = OUTPUT_KEY;
? ? String ELAPSED = "elapsed";
? ? String CONCURRENT = "concurrent";
? ? String MAX_INPUT = "max.input";
? ? String MAX_OUTPUT = "max.output";
? ? String MAX_ELAPSED = "max.elapsed";
? ? String MAX_CONCURRENT = "max.concurrent";
? ? void collect(URL statistics);
? ? List<URL> lookup(URL query);
}
6.2.1 MonitorFilter
通過過濾器的方式收集服務的調用次數和調用時間,默認實現:
org.apache.dubbo.monitor.dubbo.DubboMonitor。
七、協議層
7.1 做什么
封裝 RPC 調用,以 Invocation, Result 為中心,擴展接口為 Protocol, Invoker, Exporter。
接下來介紹Dubbo RPC過程中的常用概念:
1)Invocation是請求會話領域模型,每次請求有相應的Invocation實例,負責包裝dubbo方法信息為請求參數;
2)Result是請求結果領域模型,每次請求都有相應的Result實例,負責包裝dubbo方法響應;
3)Invoker是實體域,代表一個可執行實體,有本地、遠程、集群三類;
4)Exporter服務提供者Invoker管理實體;
5)Protocol是服務域,管理Invoker的生命周期,提供服務的暴露和引用入口;
服務初始化流程中,從這一層開始進行遠程服務的暴露和連接引用。
對于CouponServiceViewFacade服務來說,服務提供端會監聽Dubbo端口啟動tcp服務;服務消費端通過注冊中心發現服務提供者信息,啟動tcp服務連接遠端提供者。
7.2 怎么做
協議接口定義如下,統一抽象了不同協議的服務暴露和引用模型,比如InjvmProtocol只需將Exporter,Invoker關聯本地實現。DubboProtocol暴露服務的時候,需要監控本地端口啟動服務;引用服務的時候,需要連接遠端服務。
@SPI("dubbo")
public interface Protocol {
? ? int getDefaultPort();
? ? @Adaptive
? ? <T> Exporter<T> export(Invoker<T> invoker) throws RpcException;
? ? @Adaptive
? ? <T> Invoker<T> refer(Class<T> type, URL url) throws RpcException;
? ? void destroy();
? ? default List<ProtocolServer> getServers() {
? ? ? ? return Collections.emptyList();
? ? }
}
Invoker接口定義
Invocation是RPC調用的會話對象,負責包裝請求參數;Result是RPC調用的結果對象,負責包裝RPC調用的結果對象,包括異常類信息;
public interface Invoker<T> extends Node {
? ? Class<T> getInterface();
? ? Result invoke(Invocation invocation) throws RpcException;
}
7.2.1 服務的暴露和引用
服務暴露的時候,開啟RPC服務端;引用服務的時候,開啟RPC客戶端。
public class DubboProtocol extends AbstractProtocol {
。。。
? ? @Override
? ? public <T> Exporter<T> export(Invoker<T> invoker) throws RpcException {
? ? ? ? 。。。
? ? ? ? // 開啟rpc服務端
? ? ? ? openServer(url);
? ? ? ? optimizeSerialization(url);
? ? ? ? return exporter;
? ? }
? ? @Override
? ? public <T> Invoker<T> protocolBindingRefer(Class<T> serviceType, URL url) throws RpcException {
? ? ? ? optimizeSerialization(url);
? ? ? ? // 創建dubbo invoker,開啟rpc客戶端
? ? ? ? DubboInvoker<T> invoker = new DubboInvoker<T>(serviceType, url, getClients(url), invokers);
? ? ? ? invokers.add(invoker);
? ? ? ? return invoker;
? ? }
。。。
}
7.2.2 服務端響應請求
接收響應請求;
private ExchangeHandler requestHandler = new ExchangeHandlerAdapter() {
? ? ? ? @Override
? ? ? ? public CompletableFuture<Object> reply(ExchangeChannel channel, Object message) throws RemotingException {
? ? ? ? ? ? ? ? ? ? ? ? ? 。。。
? ? ? ? ? ? Invocation inv = (Invocation) message;
? ? ? ? ? ? Invoker<?> invoker = getInvoker(channel, inv);
? ? ? ? ? ? RpcContext.getContext().setRemoteAddress(channel.getRemoteAddress());
? ? ? ? ? ? //調用本地服務
? ? ? ? ? ? Result result = invoker.invoke(inv);
? ? ? ? ? ? return result.thenApply(Function.identity());
? ? ? ? }
? ? ? ? 。。。
? ? };
7.2.3 客戶端發送請求
調用遠程服務;
public class DubboInvoker<T> extends AbstractInvoker<T> {
? ? 。。。
? ? @Override
? ? protected Result doInvoke(final Invocation invocation) throws Throwable {
? ? ? ? 。。。
? ? ? ? ? ? boolean isOneway = RpcUtils.isOneway(getUrl(), invocation);
? ? ? ? ? ? int timeout = calculateTimeout(invocation, methodName);
? ? ? ? ? ? if (isOneway) {
? ? ? ? ? ? ? ? boolean isSent = getUrl().getMethodParameter(methodName, Constants.SENT_KEY, false);
? ? ? ? ? ? ? ? currentClient.send(inv, isSent);
? ? ? ? ? ? ? ? return AsyncRpcResult.newDefaultAsyncResult(invocation);
? ? ? ? ? ? } else {
? ? ? ? ? ? ? ? ExecutorService executor = getCallbackExecutor(getUrl(), inv);
? ? ? ? ? ? ? ? CompletableFuture<AppResponse> appResponseFuture =
? ? ? ? ? ? ? ? ? ? ? ? currentClient.request(inv, timeout, executor).thenApply(obj -> (AppResponse) obj);
? ? ? ? ? ? ? ? FutureContext.getContext().setCompatibleFuture(appResponseFuture);
? ? ? ? ? ? ? ? AsyncRpcResult result = new AsyncRpcResult(appResponseFuture, inv);
? ? ? ? ? ? ? ? result.setExecutor(executor);
? ? ? ? ? ? ? ? return result;
? ? ? ? ? ? }
? ? }
}
八、交換層
8.1 做什么
封裝請求響應模式,同步轉異步,以 Request, Response 為中心,擴展接口為 Exchanger, ExchangeChannel, ExchangeClient, ExchangeServer。
使用request包裝Invocation作為完整的請求對象,使用response包裝result作為完整的響應對象;Request、Response相比Invocation、Result添加了Dubbo的協議頭。
8.2 怎么做
交換器對象接口定義,定義了遠程服務的綁定和連接,使用SPI方式進行擴展;
@SPI(HeaderExchanger.NAME)
public interface Exchanger {
? ? @Adaptive({Constants.EXCHANGER_KEY})
? ? ExchangeServer bind(URL url, ExchangeHandler handler) throws RemotingException;
? ? @Adaptive({Constants.EXCHANGER_KEY})
? ? ExchangeClient connect(URL url, ExchangeHandler handler) throws RemotingException;
}
@Adaptive({Constants.EXCHANGER_KEY})
ExchangeServer bind(URL url, ExchangeHandler handler) throws RemotingException;
@Adaptive({Constants.EXCHANGER_KEY})
ExchangeClient connect(URL url, ExchangeHandler handler) throws RemotingException;
8.2.1 服務提供者
服務提供端接收到請求后,本地執行,發送響應結果;
public class HeaderExchangeHandler implements ChannelHandlerDelegate {
? 。。。
? ? void handleRequest(final ExchangeChannel channel, Request req) throws RemotingException {
? ? ? //封裝響應
? ? ? ? Response res = new Response(req.getId(), req.getVersion());
? 。。。
? ? ? ? Object msg = req.getData();
? ? ? ? try {
? ? ? ? ? ? CompletionStage<Object> future = handler.reply(channel, msg);
? ? ? ? ? ? future.whenComplete((appResult, t) -> {
? ? ? ? ? ? ? ? try {
? ? ? ? ? ? ? ? ? ? if (t == null) {
? ? ? ? ? ? ? ? ? ? ? ? res.setStatus(Response.OK);
? ? ? ? ? ? ? ? ? ? ? ? res.setResult(appResult);
? ? ? ? ? ? ? ? ? ? } else {
? ? ? ? ? ? ? ? ? ? ? ? res.setStatus(Response.SERVICE_ERROR);
? ? ? ? ? ? ? ? ? ? ? ? res.setErrorMessage(StringUtils.toString(t));
? ? ? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? ? ? channel.send(res);
? ? ? ? ? ? ? ? } catch (RemotingException e) {
? ? ? ? ? ? ? ? ? ? logger.warn("Send result to consumer failed, channel is " + channel + ", msg is " + e);
? ? ? ? ? ? ? ? }
? ? ? ? ? ? });
? ? ? ? } catch (Throwable e) {
? ? ? ? ? ? res.setStatus(Response.SERVICE_ERROR);
? ? ? ? ? ? res.setErrorMessage(StringUtils.toString(e));
? ? ? ? ? ? channel.send(res);
? ? ? ? }
? ? }
。。。
}
8.2.2 服務消費者
服務消費端發起請求的封裝,方法執行成功后,返回一個future;
final class HeaderExchangeChannel implements ExchangeChannel {
。。。
? //封裝請求實體
? ? @Override
? ? public CompletableFuture<Object> request(Object request, int timeout, ExecutorService executor) throws RemotingException {
? ? ? 。。。
? ? ? ? // create request.
? ? ? ? Request req = new Request();
? ? ? ? req.setVersion(Version.getProtocolVersion());
? ? ? ? req.setTwoWay(true);
? ? ? ? //RpcInvocation
? ? ? ? req.setData(request);
? ? ? ? DefaultFuture future = DefaultFuture.newFuture(channel, req, timeout, executor);
? ? ? ? try {
? ? ? ? ? ? channel.send(req);
? ? ? ? } catch (RemotingException e) {
? ? ? ? ? ? future.cancel();
? ? ? ? ? ? throw e;
? ? ? ? }
? ? ? ? return future;
? ? }
。。。
}
九、傳輸層
9.1 做什么
抽象傳輸層模型,兼容netty、mina、grizzly等通訊框架。
9.2 怎么做
傳輸器接口定義如下,它與交換器Exchanger接口定義相似,區別在于Exchanger是圍繞Dubbo的Request和Response封裝的操作門面接口,而Transporter更加的底層,Exchanger用于隔離Dubbo協議層和通訊層。
@SPI("netty")
public interface Transporter {
? ? @Adaptive({Constants.SERVER_KEY, Constants.TRANSPORTER_KEY})
? ? RemotingServer bind(URL url, ChannelHandler handler) throws RemotingException;
? ? @Adaptive({Constants.CLIENT_KEY, Constants.TRANSPORTER_KEY})
? ? Client connect(URL url, ChannelHandler handler) throws RemotingException;
}
自定義傳輸層模型
通過SPI的方式,動態選擇具體的傳輸框架,默認是netty;
public class Transporters {
? ? 。。。
? ? public static RemotingServer bind(URL url, ChannelHandler... handlers) throws RemotingException {
? ? ? ? 。。。
? ? ? ? return getTransporter().bind(url, handler);
? ? }
? ? public static Client connect(URL url, ChannelHandler... handlers) throws RemotingException {
? ? ? ? 。。。
? ? ? ? return getTransporter().connect(url, handler);
? ? }
? ? public static Transporter getTransporter() {
? ? ? ? return ExtensionLoader.getExtensionLoader(Transporter.class).getAdaptiveExtension();
? ? }
}
netty框架的channel適配如下,采用裝飾模式,使用netty框架的channel作為Dubbo自定義的channel做實現;
final class NettyChannel extends AbstractChannel {
? ? private NettyChannel(Channel channel, URL url, ChannelHandler handler) {
? ? ? ? super(url, handler);
? ? ? ? if (channel == null) {
? ? ? ? ? ? throw new IllegalArgumentException("netty channel == null;");
? ? ? ? }
? ? ? ? this.channel = channel;
? ? }
}
十、序列化
10.1 做什么
抽象序列化模型,兼容多種序列化框架,包括:fastjson、fst、hessian2、kryo、kryo2、protobuf等,通過序列化支持跨語言的方式,支持跨語言的RPC調用。
10.2 怎么做
定義Serialization擴展點,默認hessian2,支持跨語言。Serialization接口實際是一個工廠接口,通過SPI擴展;實際序列化和反序列化工作由ObjectOutput,ObjectInput完成,通過裝飾模式讓hessian2完成實際工作。
@SPI("hessian2")
public interface Serialization {
? ? byte getContentTypeId();
? ? String getContentType();
? ? @Adaptive
? ? ObjectOutput serialize(URL url, OutputStream output) throws IOException;
? ? @Adaptive
? ? ObjectInput deserialize(URL url, InputStream input) throws IOException;
}
10.2.1 通訊協議設計
下圖出自開發指南-實現細節-遠程通訊細節,描述Dubbo協議頭設計;
0-15bit表示Dubbo協議魔法數字,值:0xdabb;
16bit請求響應標記,Request - 1; Response - 0;
17bit請求模式標記,只有請求消息才會有,1表示需要服務端返回響應;
18bit是事件消息標記,1表示該消息是事件消息,比如心跳消息;
19-23bit是序列化類型標記,hessian序列化id是2,fastjson是6,詳見org.apache.dubbo.common.serialize.Constants;
24-31bit表示狀態,只有響應消息才有用;
32-64bit是RPC請求ID;
96-128bit是會話數據長度;
128是消息體字節序列;
Dubbo將RPC整個過程分成核心的代理層、注冊層、集群層、協議層、傳輸層等,層與層之間的職責邊界明確;核心層都通過接口定義,不依賴具體實現,這些接口串聯起來形成了Dubbo的骨架;這個骨架也可以看作是Dubbo的內核,內核使用SPI 機制加載插件(擴展點),達到高度可擴展。