dubbo的provider端抛出异常的处理逻辑

直入主题,我们都知道dubbo是一个rpc的框架,consumer是通过网络对provider发起方法调用的,provider也要通过网络返回响应。那如果provider端发生了异常而又没有捕获,照理就不会有返回值,consumer端会得到什么样的结果呢?

其实dubbo有一个专门处理异常的Filter,叫ExceptionFilter,针对provider端未被捕获的各种异常做了分别处理,看一下源码:

/**
 * ExceptionInvokerFilter
 * <p>
 * Functions:
 * <ol>
 * <li>unexpected exception will be logged in ERROR level on provider side. Unexpected exception are unchecked
 * exception not declared on the interface</li>
 * <li>Wrap the exception not introduced in API package into RuntimeException. Framework will serialize the outer exception but stringnize its cause in order to avoid of possible serialization problem on client side</li>
 * </ol>
 */
@Activate(group = Constants.PROVIDER)
public class ExceptionFilter implements Filter {

    private final Logger logger;

    public ExceptionFilter() {
        this(LoggerFactory.getLogger(ExceptionFilter.class));
    }

    public ExceptionFilter(Logger logger) {
        this.logger = logger;
    }

    @Override
    public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
        try {
            return invoker.invoke(invocation);
        } catch (RuntimeException e) {
            logger.error("Got unchecked and undeclared exception which called by " + RpcContext.getContext().getRemoteHost()
                    + ". service: " + invoker.getInterface().getName() + ", method: " + invocation.getMethodName()
                    + ", exception: " + e.getClass().getName() + ": " + e.getMessage(), e);
            throw e;
        }
    }

    @Override
    public Result onResponse(Result result, Invoker<?> invoker, Invocation invocation) {
        if (result.hasException() && GenericService.class != invoker.getInterface()) {
            try {
                Throwable exception = result.getException();

                // directly throw if it's checked exception
                if (!(exception instanceof RuntimeException) && (exception instanceof Exception)) {
                    return result;
                }
                // directly throw if the exception appears in the signature
                try {
                    Method method = invoker.getInterface().getMethod(invocation.getMethodName(), invocation.getParameterTypes());
                    Class<?>[] exceptionClassses = method.getExceptionTypes();
                    for (Class<?> exceptionClass : exceptionClassses) {
                        if (exception.getClass().equals(exceptionClass)) {
                            return result;
                        }
                    }
                } catch (NoSuchMethodException e) {
                    return result;
                }

                // for the exception not found in method's signature, print ERROR message in server's log.
                logger.error("Got unchecked and undeclared exception which called by " + RpcContext.getContext().getRemoteHost()
                        + ". service: " + invoker.getInterface().getName() + ", method: " + invocation.getMethodName()
                        + ", exception: " + exception.getClass().getName() + ": " + exception.getMessage(), exception);

                // directly throw if exception class and interface class are in the same jar file.
                String serviceFile = ReflectUtils.getCodeBase(invoker.getInterface());
                String exceptionFile = ReflectUtils.getCodeBase(exception.getClass());
                if (serviceFile == null || exceptionFile == null || serviceFile.equals(exceptionFile)) {
                    return result;
                }
                // directly throw if it's JDK exception
                String className = exception.getClass().getName();
                if (className.startsWith("java.") || className.startsWith("javax.")) {
                    return result;
                }
                // directly throw if it's dubbo exception
                if (exception instanceof RpcException) {
                    return result;
                }

                // otherwise, wrap with RuntimeException and throw back to the client
                return new RpcResult(new RuntimeException(StringUtils.toString(exception)));
            } catch (Throwable e) {
                logger.warn("Fail to ExceptionFilter when called by " + RpcContext.getContext().getRemoteHost()
                        + ". service: " + invoker.getInterface().getName() + ", method: " + invocation.getMethodName()
                        + ", exception: " + e.getClass().getName() + ": " + e.getMessage(), e);
                return result;
            }
        }
        return result;
    }

}

这里的Result封装了rpc调用的返回值,其实代码里各个逻辑已经用英文注释的很清楚了,总结一下(以下说的抛出是指异常会传递给consumer):

  1. 如果是必检异常,则直接抛出;
  2. 如果方法签名声明了该异常,则直接抛出;
  3. 如果异常类和服务api声明在同一个jar包,则直接抛出;
  4. 如果是JDK自带异常(包名以java或者javax开头),则直接抛出;
  5. 如果是dubbo框架异常(RpcException),则直接抛出;
  6. 其他异常,包装成RuntimeException后抛出(为了避免序列化问题会先将异常堆栈转成字符串)。

模拟一下各种情况,看看dubbo源码是否说到做到了O(∩_∩)O哈哈~

provider启动类

public class Application {
   
    public static void main(String[] args) throws Exception {
        ServiceConfig<DemoServiceImpl> service = new ServiceConfig<>();
        service.setApplication(new ApplicationConfig("dubbo-demo-api-provider"));
        service.setRegistry(new RegistryConfig("multicast://224.5.6.7:1234"));
        service.setInterface(DemoService.class);
        service.setRef(new DemoServiceImpl());
        service.export();
        System.in.read();
    }
}

消费者

public class Application {
    public static void main(String[] args) {
        ReferenceConfig<DemoService> reference = new ReferenceConfig<>();
        reference.setApplication(new ApplicationConfig("dubbo-demo-api-consumer"));
        reference.setRegistry(new RegistryConfig("multicast://224.5.6.7:1234"));
        reference.setInterface(DemoService.class);
        DemoService service = reference.get();
        String message = null;
        try {
            message = service.sayHello("dubbo");
        } catch (Exception e) {
            e.printStackTrace();
        }
        System.out.println(message);
    }
}

必检异常 && 方法签名声明

因为必检异常要么捕获,要么你声明,而捕获后就不会往外抛了,所以这里两种场景一起测试

服务接口

public interface DemoService {
    String sayHello(String name) throws IOException;
}

服务实现类

public class DemoServiceImpl implements DemoService {
    private static final Logger logger = LoggerFactory.getLogger(DemoServiceImpl.class);

    @Override
    public String sayHello(String name) throws IOException {
        logger.info("Hello " + name + ", request from consumer: " + RpcContext.getContext().getRemoteAddress());
        throw new IOException();
    }
}

运行后看到,验证没问题

同一个jar包

服务接口

public interface DemoService {
    String sayHello(String name);
}

服务实现类

public class DemoServiceImpl implements DemoService {
    private static final Logger logger = LoggerFactory.getLogger(DemoServiceImpl.class);

    @Override
    public String sayHello(String name) {
        logger.info("Hello " + name + ", request from consumer: " + RpcContext.getContext().getRemoteAddress());
        throw new TestException();
    }
}

自定义一个RuntimeException,放于api相同的jar包中

运行后看到,验证没问题

JDK自带异常

服务接口

public interface DemoService {
    String sayHello(String name);
}

服务实现类

public class DemoServiceImpl implements DemoService {
    private static final Logger logger = LoggerFactory.getLogger(DemoServiceImpl.class);

    @Override
    public String sayHello(String name) {
        logger.info("Hello " + name + ", request from consumer: " + RpcContext.getContext().getRemoteAddress());
        throw new NullPointerException();
    }
}

运行后看到,验证没问题

dubbo框架异常

服务接口

public interface DemoService {
    String sayHello(String name);
}

服务实现类

public class DemoServiceImpl implements DemoService {
    private static final Logger logger = LoggerFactory.getLogger(DemoServiceImpl.class);

    @Override
    public String sayHello(String name) {
        logger.info("Hello " + name + ", request from consumer: " + RpcContext.getContext().getRemoteAddress());
    }
}

打断点构造超时,运行后看到,验证没问题

其他异常

服务接口

public interface DemoService {
    String sayHello(String name);
}

服务实现类

public class DemoServiceImpl implements DemoService {
    private static final Logger logger = LoggerFactory.getLogger(DemoServiceImpl.class);

    @Override
    public String sayHello(String name) {
        logger.info("Hello " + name + ", request from consumer: " + RpcContext.getContext().getRemoteAddress());
        
    }
}

构造一个RuntimeException,放在provider实现的包中

运行后看到,验证没问题

本文由博客一文多发平台 OpenWrite 发布!

内容概要:本书系统讲解了基于RAG(检索增强生成)技术构建知识库与智能问答系统的完整流程。通过使用Cloudflare Workers、Vectorize向量数据库和千问大模型等核心技术,详细阐述了从环境搭建、数据收集、文档向量化、向量存储与检索,到大语言模型集成、答案生成及前界面开发的全过程。书中涵盖RAG原理、架构设计、多语言支持、性能优化与部署监控等关键内容,并提供大量代码示例与最佳实践,帮助开发者构建高效、可扩展的个性化聊天机器人系统。; 适合人群:具备Node.js和TypeScript基础,熟悉命令行操作,对AI应用开发感兴趣的开发者,尤其是希望构建个人知识库问答系统的技术人员;也适合关注内容管理、个人品牌建设的创作者和教育工作者。; 使用场景及目标:①学习如何利用RAG技术解决大模型知识滞后与幻觉问题;②掌握从零搭建智能问答机器人的全流程,包括向量数据库使用、嵌入模型调用、多语言处理与边缘部署;③应用于个人博客、企业知识库、客服系统等场景,实现低成本、高性能的智能检索与生成服务。; 阅读建议:建议结合书中的代码示例与Cloudflare平台动手实践,重点关注数据分块策略、向量维度匹配、元数据设计与检索优化等核心环节。在学习过程中应注重系统可观测性与错误处理机制的构建,并通过测试与用户反馈持续迭代优化。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值