设为首页 加入收藏

TOP

Triple 协议支持 Java 异常回传的设计与实现(一)
2023-07-25 21:39:58 】 浏览:38
Tags:Triple 支持 Java 常回传 计与实

作者:Apache Dubbo Contributor 陈景明

背景

在一些业务场景, 往往需要自定义异常来满足特定的业务, 主流用法是在catch里抛出异常, 例如:

public void deal() {
  try{
   //doSomething   
   ...
  } catch(IGreeterException e) {
      ...
      throw e;
  }   
}

或者通过ExceptionBuilder,把相关的异常对象返回给consumer:

provider.send(new ExceptionBuilders.IGreeterExceptionBuilder()
    .setDescription('异常描述信息'); 

在抛出异常后, 通过捕获和instanceof来判断特定的异常, 然后做相应的业务处理,例如:

try {
    greeterProxy.echo(REQUEST_MSG);
} catch (IGreeterException e) {
    //做相应的处理
    ...
}

在 Dubbo 2.x 版本,可以通过上述方法来捕获 Provider 端的异常。
而随着云原生时代的到来, Dubbo 也开启了 3.0 的里程碑。

Dubbo 3.0 的一个很重要的目标就是全面拥抱云原生,
在 3.0 的许多特性中,很重要的一个改动就是支持新的一代Rpc协议Triple

Triple 协议基于 HTTP 2.0 进行构建,对网关的穿透性强,兼容 gRPC
提供 Request Response、Request Streaming、Response Streaming、
Bi-directional Streaming 等通信模型;
从 Triple 协议开始,Dubbo 还支持基于 IDL 的服务定义。

采用 Triple 协议的用户可以在 provider 端生成用户定义的异常信息,
记录异常产生的堆栈,triple 协议可保证将用户在客户端获取到异常的message。

Triple 的回传异常会在 AbstractInvokerwaitForResultIfSync
中把异常信息堆栈统一封装成 RpcException
所有来自 Provider 端的异常都会被封装成 RpcException 类型并抛出,
这会导致用户无法根据特定的异常类型捕获来自 Provider 的异常,
只能通过捕获 RpcException 异常来返回信息,
且 Provider 携带的异常 message 也无法回传,只能获取打印的堆栈信息:

    try {
        greeterProxy.echo(REQUEST_MSG);
    } catch (RpcException e) {
        e.printStackTrace();
    }

自定义异常信息在社区中的呼声也比较高,
因此本次改动将支持自定义异常的功能, 使得服务端能抛出自定义异常后被客户端捕获到。

Dubbo异常处理简介

我们从Consumer的角度看一下一次Triple协议 Unary请求的大致流程:

Dubbo Consumer 从 Spring 容器中获取 bean 时获取到的是一个代理接口,
在调用接口的方法时会通过代理类远程调用接口并返回结果。

Dubbo提供的代理工厂类是 ProxyFactory,通过 SPI 机制默认实现的是 JavassistProxyFactory
JavassistProxyFactory 创建了一个继承自 AbstractProxyInvoker 类的匿名对象,
并重写了抽象方法 doInvoke
重写后的 doInvoke 只是将调用请求转发给了 Wrapper 类的 invokeMethod 方法,
并生成 invokeMethod 方法代码和其他一些方法代码。

代码生成完毕后,通过 Javassist 生成 Class 对象,
最后再通过反射创建 Wrapper 实例,随后通过 InvokerInvocationHandler -> InvocationUtil -> AbstractInvoker -> 具体实现类发送请求到Provider端。

Provider 进行相应的业务处理后返回相应的结果给 Consumer 端,来自 Provider 端的结果会被封装成 AsyncResult ,在 AbstractInvoker 的具体实现类里,
接受到来自 Provider 的响应之后会调用 appResponserecreate 方法,若 appResponse 里包含异常,
则会抛出给用户,大体流程如下:

1.jpeg

上述的异常处理相关环节是在 Consumer 端,在 Provider 端则是由 org.apache.dubbo.rpc.filter.ExceptionFilter 进行处理,
它是一系列责任链 Filter 中的一环,专门用来处理异常。

Dubbo 在 Provider 端的异常会在封装进 appResponse 中。下面的流程图揭示了 ExceptionFilter 源码的异常处理流程:

2.jpeg

而当 appResponse 回到了 Consumer 端,会在 InvocationUtil 里调用 AppResponserecreate 方法抛出异常,
最终可以在 Consumer 端捕获:

public Object recreate() throws Throwable {
    if (exception != null) {
    try {
        Object stackTrace = exception.getStackTrace();
        if (stackTrace == null) {
            exception.setStackTrace(new StackTraceElement[0]);
        }
    } catch (Exception e) {
        // ignore
    }
    throw exception;
}
return result;
}

Triple 通信原理

在上一节中,我们已经介绍了 Dubbo 在 Consumer 端大致发送数据的流程,
可以看到最终依靠的是 AbstractInvoker 的实现类来发送数据。
在 Triple 协议中,AbstractInvoker 的具体实现类是 TripleInvoker
TripleInvoker 在发送前会启动监听器,监听来自 Provider 端的响应结果,
并调用 ClientCallToObserverAdapteronNext 方法发送消息,
最终会在底层封装成 Netty 请求发送数据。

在正式的请求发起前,TripleServer 会注册 TripleHttp2FrameServerHandler
它继承自 Netty 的 ChannelDuplexHandler
其作用是会在 channelRead 方法中不断读取 Header 和 Data 信息并解析,
经过层层调用,
会在 AbstractServerCallonMessage 方法里把来自 consumer 的信息流进行反序列化,
并最终由交由 ServerCallToObserverAdapterinvoke 方法进行处理。

invoke 方法中,根据 consumer 请求的数据调用服务端相应的方法,并异步等待结果;'
若服务端抛出异常,则调用 onError 方法进行处理,
否则,调用 onReturn 方法返回正常的结果,大致代码逻辑如下:

public void invoke() {
    ...
    try {
        //调用invoke方法请求服务
        final Result response = invoker.invoke(invocation);
        //异步等
首页 上一页 1 2 下一页 尾页 1/2/2
】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
上一篇Windows下jdk安装与卸载-超详细的.. 下一篇增强for循环

最新文章

热门文章

Hot 文章

Python

C 语言

C++基础

大数据基础

linux编程基础

C/C++面试题目