浅谈dubbo的ExceptionFilter异常处理

2023-05-16

背景

我们的项目使用了dubbo进行不同系统之间的调用。
每个项目都有一个全局的异常处理,对于业务异常,我们会抛出自定义的业务异常(继承RuntimeException)。
全局的异常处理会根据不同的异常类型进行不同的处理。
最近我们发现,某个系统调用dubbo请求,provider端(服务提供方)抛出了自定义的业务异常,但consumer端(服务消费方)拿到的并不是自定义的业务异常。
这是为什么呢?还需要从 dubbo的ExceptionFilter说起。

ExceptionFilter

如果Dubbo的  provider端 抛出异常(Throwable),则会被 provider端 的ExceptionFilter拦截到,执行以下invoke方法:
/*
 * Copyright 1999-2011 Alibaba Group.
 *  
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *  
 *      http://www.apache.org/licenses/LICENSE-2.0
 *  
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.alibaba.dubbo.rpc.filter;

import java.lang.reflect.Method;

import com.alibaba.dubbo.common.Constants;
import com.alibaba.dubbo.common.extension.Activate;
import com.alibaba.dubbo.common.logger.Logger;
import com.alibaba.dubbo.common.logger.LoggerFactory;
import com.alibaba.dubbo.common.utils.ReflectUtils;
import com.alibaba.dubbo.common.utils.StringUtils;
import com.alibaba.dubbo.rpc.Filter;
import com.alibaba.dubbo.rpc.Invocation;
import com.alibaba.dubbo.rpc.Invoker;
import com.alibaba.dubbo.rpc.Result;
import com.alibaba.dubbo.rpc.RpcContext;
import com.alibaba.dubbo.rpc.RpcException;
import com.alibaba.dubbo.rpc.RpcResult;
import com.alibaba.dubbo.rpc.service.GenericService;

/**
 * ExceptionInvokerFilter
 * <p>
 * 功能:
 * <ol>
 * <li>不期望的异常打ERROR日志(Provider端)<br>
 *     不期望的日志即是,没有的接口上声明的Unchecked异常。
 * <li>异常不在API包中,则Wrap一层RuntimeException。<br>
 *     RPC对于第一层异常会直接序列化传输(Cause异常会String化),避免异常在Client出不能反序列化问题。
 * </ol>
 * 
 * @author william.liangf
 * @author ding.lid
 */
@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;
    }
    
    public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
        try {
            Result result = invoker.invoke(invocation);
            if (result.hasException() && GenericService.class != invoker.getInterface()) {
                try {
                    Throwable exception = result.getException();

                    // 如果是checked异常,直接抛出
                    if (! (exception instanceof RuntimeException) && (exception instanceof Exception)) {
                        return result;
                    }
                    // 在方法签名上有声明,直接抛出
                    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;
                    }

                    // 未在方法签名上定义的异常,在服务器端打印ERROR日志
                    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);

                    // 异常类和接口类在同一jar包里,直接抛出
                    String serviceFile = ReflectUtils.getCodeBase(invoker.getInterface());
                    String exceptionFile = ReflectUtils.getCodeBase(exception.getClass());
                    if (serviceFile == null || exceptionFile == null || serviceFile.equals(exceptionFile)){
                        return result;
                    }
                    // 是JDK自带的异常,直接抛出
                    String className = exception.getClass().getName();
                    if (className.startsWith("java.") || className.startsWith("javax.")) {
                        return result;
                    }
                    // 是Dubbo本身的异常,直接抛出
                    if (exception instanceof RpcException) {
                        return result;
                    }

                    // 否则,包装成RuntimeException抛给客户端
                    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;
        } 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;
        }
    }

}

代码分析

按逻辑顺序进行分析,满足其中一个即返回,不再继续执行判断。

逻辑0

if (result.hasException() && GenericService.class != invoker.getInterface()) {
    //...
}
return result;
调用结果有异常且未实现GenericService接口,进入后续判断逻辑,否则直接返回结果。
/**
 * 通用服务接口
 * 
 * @author william.liangf
 * @export
 */
public interface GenericService {

    /**
     * 泛化调用
     * 
     * @param method 方法名,如:findPerson,如果有重载方法,需带上参数列表,如:findPerson(java.lang.String)
     * @param parameterTypes 参数类型
     * @param args 参数列表
     * @return 返回值
     * @throws Throwable 方法抛出的异常
     */
    Object $invoke(String method, String[] parameterTypes, Object[] args) throws GenericException;

}
泛接口实现方式主要用于服务器端没有API接口及模型类元的情况,参数及返回值中的所有POJO均用Map表示,通常用于框架集成,比如:实现一个通用的远程服务Mock框架,可通过实现GenericService接口处理所有服务请求。
不适用于此场景,不在此处探讨。

逻辑1

// 如果是checked异常,直接抛出
if (! (exception instanceof RuntimeException) && (exception instanceof Exception)) {
    return result;
}
不是RuntimeException类型的异常,并且是受检异常(继承Exception),直接抛出。
provider端想抛出受检异常,必须在api上明确写明抛出受检异常;consumer端如果要处理受检异常,也必须使用明确写明抛出受检异常的api。
provider端api新增 自定义的 受检异常, 所有的 consumer端api都必须升级,同时修改代码,否则无法处理这个特定异常。

consumer端DecodeableRpcResult的decode方法会对异常进行处理


此处会抛出IOException,上层catch后会做toString处理,放到mErrorMsg属性中:
try {
    decode(channel, inputStream);
} catch (Throwable e) {
    if (log.isWarnEnabled()) {
        log.warn("Decode rpc result failed: " + e.getMessage(), e);
    }
    response.setStatus(Response.CLIENT_ERROR);
    response.setErrorMessage(StringUtils.toString(e));
} finally {
    hasDecoded = true;
}

DefaultFuture判断请求返回的结果,最后抛出RemotingException:
private Object returnFromResponse() throws RemotingException {
    Response res = response;
    if (res == null) {
        throw new IllegalStateException("response cannot be null");
    }
    if (res.getStatus() == Response.OK) {
        return res.getResult();
    }
    if (res.getStatus() == Response.CLIENT_TIMEOUT || res.getStatus() == Response.SERVER_TIMEOUT) {
        throw new TimeoutException(res.getStatus() == Response.SERVER_TIMEOUT, channel, res.getErrorMessage());
    }
    throw new RemotingException(channel, res.getErrorMessage());
}

DubboInvoker捕获RemotingException,抛出RpcException:
try {
    boolean isAsync = RpcUtils.isAsync(getUrl(), invocation);
    boolean isOneway = RpcUtils.isOneway(getUrl(), invocation);
    int timeout = getUrl().getMethodParameter(methodName, Constants.TIMEOUT_KEY,Constants.DEFAULT_TIMEOUT);
    if (isOneway) {
        boolean isSent = getUrl().getMethodParameter(methodName, Constants.SENT_KEY, false);
        currentClient.send(inv, isSent);
        RpcContext.getContext().setFuture(null);
        return new RpcResult();
    } else if (isAsync) {
        ResponseFuture future = currentClient.request(inv, timeout) ;
        RpcContext.getContext().setFuture(new FutureAdapter<Object>(future));
        return new RpcResult();
    } else {
        RpcContext.getContext().setFuture(null);
        return (Result) currentClient.request(inv, timeout).get();
    }
} catch (TimeoutException e) {
    throw new RpcException(RpcException.TIMEOUT_EXCEPTION, "Invoke remote method timeout. method: " + invocation.getMethodName() + ", provider: " + getUrl() + ", cause: " + e.getMessage(), e);
} catch (RemotingException e) {
    throw new RpcException(RpcException.NETWORK_EXCEPTION, "Failed to invoke remote method: " + invocation.getMethodName() + ", provider: " + getUrl() + ", cause: " + e.getMessage(), e);
}

调用栈:
FailOverClusterInvoker.doInvoke -...-> DubboInvoker.doInvoke -> ReferenceCountExchangeClient.request -> HeaderExchangeClient.request -> HeaderExchangeChannel.request -> AbstractPeer.send -> NettyChannel.send -> AbstractChannel.write -> Channels.write --back_to--> DubboInvoker.doInvoke -> DefaultFuture.get -> DefaultFuture.returnFromResponse -> throw new RemotingException
异常示例:
com.alibaba.dubbo.rpc.RpcException: Failed to invoke the method triggerCheckedException in the service com.xxx.api.DemoService. Tried 1 times of the providers [192.168.1.101:20880] (1/1) from the registry 127.0.0.1:2181 on the consumer 192.168.1.101 using the dubbo version 3.1.9. Last error is: Failed to invoke remote method: triggerCheckedException, provider: dubbo://192.168.1.101:20880/com.xxx.api.DemoService?xxx, cause: java.io.IOException: Response data error, expect Throwable, but get {cause=(this Map), detailMessage=null, suppressedExceptions=[], stackTrace=[Ljava.lang.StackTraceElement;@23b84919}
java.io.IOException: Response data error, expect Throwable, but get {cause=(this Map), detailMessage=null, suppressedExceptions=[], stackTrace=[Ljava.lang.StackTraceElement;@23b84919}
	at com.alibaba.dubbo.rpc.protocol.dubbo.DecodeableRpcResult.decode(DecodeableRpcResult.java:94)


逻辑2

// 在方法签名上有声明,直接抛出
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;
}
如果在provider端的api明确写明抛出运行时异常,则会直接被抛出。
如果抛出了这种异常,但是consumer端又没有这种异常,会发生什么呢?
答案是和上面一样,抛出RpcException。

因此如果consumer端不care这种异常,则不需要任何处理;
consumer端有这种异常(路径要完全一致,包名+类名),则不需要任何处理;
没有这种异常,又想进行处理,则需要引入这个异常进行处理(方法有多种,比如升级api,或引入/升级异常所在的包)。

逻辑3

// 异常类和接口类在同一jar包里,直接抛出
String serviceFile = ReflectUtils.getCodeBase(invoker.getInterface());
String exceptionFile = ReflectUtils.getCodeBase(exception.getClass());
if (serviceFile == null || exceptionFile == null || serviceFile.equals(exceptionFile)){
    return result;
}
如果异常类和接口类在同一个jar包中,直接抛出。

逻辑4

// 是JDK自带的异常,直接抛出
String className = exception.getClass().getName();
if (className.startsWith("java.") || className.startsWith("javax.")) {
    return result;
}
以java.或javax.开头的异常直接抛出。

逻辑5

// 是Dubbo本身的异常,直接抛出
if (exception instanceof RpcException) {
    return result;
}
dubbo自身的异常,直接抛出。

逻辑6

// 否则,包装成RuntimeException抛给客户端
return new RpcResult(new RuntimeException(StringUtils.toString(exception)));
不满足上述条件,会做toString处理并被封装成RuntimeException抛出。

核心思想

尽力避免反序列化时失败(只有在jdk版本或api版本不一致时才可能发生)。

如何正确捕获业务异常

了解了ExceptionFilter,解决上面提到的问题就很简单了。
有多种方法可以解决这个问题,每种都有优缺点,这里不做详细分析,仅列出供参考:
1. 将该异常的包名以"java.或者"javax. " 开头
2. 使用受检异常(继承Exception)
3. 不用异常,使用错误码
4. 把异常放到provider-api的jar包中
5. 判断异常message是否以XxxException.class.getName()开头(其中XxxException是自定义的业务异常)
6. provider实现GenericService接口
7. provider的api明确写明throws XxxException,发布provider(其中XxxException是自定义的业务异常)
8. 实现dubbo的filter,自定义provider的异常处理逻辑(方法可参考之前的文章 给dubbo接口添加白名单——dubbo Filter的使用)
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

浅谈dubbo的ExceptionFilter异常处理 的相关文章

  • 使用libcurl发送post请求

    C语言代码如下 xff1a include lt stdio h gt include lt string h gt include lt curl curl h gt int main int argc char argv CURL cu
  • 访问带有用户名、密码的URL

    很简单 xff0c 举例如下 xff1a HttpURLConnection urlConnection 61 HttpURLConnection url span class hljs preprocessor openConnectio
  • ROS下驱动双雷达

    ROS下驱动SLAMETC的A2和A3雷达步骤如下 xff1a 1 连上激光雷达并进入至 dev文件夹中查看ttyUSB xff1f 的编号 2 使用sudo chmod 777 ttyUSB 来更改USB端口权限 3 调整A2和A3激光雷
  • Usart 串口发送数据包 两种方式(遍历结构体,指针数组)

    Usart 串口发送数据包 两种方式 xff08 结构体 xff0c 指针数组 xff09 1 结构体指针 1 1 结构体和联合体 叙述 xff1a 这种直观性比较 xff0c 利用联合体和结构体的形式组成数据包 xff0c 通过结构体指针
  • netfilter 讲解 ,讲的很好

    Netfilter为多种网络协议 xff08 IPv4 IPv6 ARP等 xff09 各提供了一套钩子函数 在IPv4中定义了5个钩子函数 xff0c 这些钩子函数在数据包流经协议栈的5个关键点被调用 这就像有5个钓鱼台 xff0c 在每
  • python字典添加元素和删除元素

    1 添加字典元素 方法一 xff1a 直接添加 xff0c 给定键值对 pycharm aa 61 39 人才 39 60 39 英语 39 39 english 39 39 adress 39 39 here 39 print aa 39
  • Verilog中奇偶校验位的计算方法

    偶校验 xff1a 数据和校验位中1的总数为偶数 xff0c 则认为数据无误 xff0c 否则标识数据有误 xff1b 奇校验 xff1a 数据和校验位中1的总数为奇数 xff0c 则认为数据无误 xff0c 否则标识数据有误 xff1b
  • Ubuntu下进行Socket编程

    龙云尧个人博客 xff0c 转载请注明出处 CSDN地址 xff1a http blog csdn net Michael753951 article details 72553287 个人blog地址 xff1a http yaoyl c
  • add_library,target_link_libraries,set_target_properties,target_link_libraries使用联系

    折腾了半下午 xff0c 终于知道这个货是怎么个关系了 在此写下记录 xff0c 希望遇到同样问题的同学 xff0c 不要再次浪费时间了 首先我是在Linux环境下 先列出我的CmakeLists txt 注意 xff0c 在CmakeLi
  • strpbrk

    头文件 xff1a include lt include h gt strpbrk 函数检索两个字符串中首个相同字符的位置 xff0c 其原型为 xff1a char strpbrk char s1 char s2 参数说明 s1 s2要检
  • Ubuntu中在命令行如何打开图形界面的文件夹、文件、网页等

    方法一 xff08 nautilus open terminal 打开文件夹 xff09 xff1a 安装nautilus open terminal span class token function sudo span span cla
  • 3.4迭代器介绍

    文章目录 使用迭代器迭代器运算符将迭代器从一个元素移动到另外一个元素迭代器类型begin和end运算符结合解引用和成员访问操作某些对vector对象的操作会使迭代器失效 迭代器运算迭代器的算术运算使用迭代器运算 我们已经知道可以使用下标运算
  • CAN报文格式简析

    Date xff1a 2022 5 11 Author xff1a MJQ Meta xff1a CAN CAN基础篇 CAN报文格式简析 1 Motornala和Intel格式 建议直接看图 xff0c 小白也能秒懂 xff01 注 xf
  • 【C++】头文件声明变量总结

    类外变量 span class token comment 1 直接声明定义 span span class token comment 如果被多个 cpp 包含 xff0c 则链接报错 multiple definition span s
  • C语言 如何用http post方式 上传json数据

    What C语言 如何用post方式 上传json数据 Piont C语言Socket实现HTTP协议 xff0c json格式数据组包 1 xff0c C语言Socket实现HTTP POST 43 json格式数据 按照HTTP协议发送
  • UDP组播调试(使用TCP&UDP调试工具)

    如何使用调试工具进行UDP组播调试 一 下载UDP amp TCP调试工具 二 双击打开调试工具并建立端口 2 1 创建组播客户端1 类型 xff1a UDP xff08 组播模式 xff09 xff1b 目标IP xff1a 225 0
  • python字典遍历的几种方法

    xff08 1 xff09 遍历key值 gt gt gt a 39 a 39 39 1 39 39 b 39 39 2 39 39 c 39 39 3 39 gt gt gt for key in a print key 43 39 39
  • 基于寄存器与基于固件库的stm32差异

    一 简介 STM32标准外设库之前的版本也称固件函数库或简称固件库 xff0c 是一个固件函数包 xff0c 它由程序 数据结构和宏组成 xff0c 包括了微控制器所有外设的性能特征 寄存器是中央处理器内的组成部分 寄存器是有限存贮容量的高
  • Ublox F9P通过NTRIP实现实时RTK定位

    现在开一个新坑 xff0c 后面慢慢填 2022 07 23更新 xff1a 测试视频 NTRIP ROS测试 一 前言 首先 xff0c 什么RTK呀 NTRIP呀 xff0c RTKLIB呀 xff0c 我都还是新手不是很懂 xff0c
  • 接口测试工具--Postman安装

    想用汉化版的Postman xff0c 需要有两样东西 xff1a 一是英文版安装包 xff1b 一是汉化包 xff1b 需注意的是中 英文版本号必须一致 xff0c 才可以 但是官网的汉化包 xff0c 现在还没更新到最新版本 xff0c

随机推荐

  • 05c语言——宏定义、带参宏、带参宏函数

    提示 xff1a 文章写完后 xff0c 目录可以自动生成 xff0c 如何生成可参考右边的帮助文档 文章目录 一 宏定义1 使用规则2 注意 二 带参宏1 定义2 注意 三 带参函数1 带参函数的宏与带参宏的区别2 带参宏函数和普通函数区
  • Makefile 和 CMake 入门

    Makefile Reference Makefile 20分钟入门 xff0c 简简单单 xff0c 展示如何使用Makefile管理和编译C 43 43 代码 简介 Makefile 是一个用于构建 xff08 Build xff09
  • cmake使用独立工具链交叉编译可在android中运行的so包

    cmake使用独立工具链交叉编译可在android中运行的so包 在在命令行下用CMake交叉编译可在android中运行的so包一文中说了一下使用NDK自带的工具链来编译so包 xff0c 当时讲到还有另一种方法来编译so包 xff0c
  • RTK原理

    一 实时载波相位差分 我们知道 xff0c 在利用GPS进行定位时 xff0c 会受到各种各样因素的影响 xff0c 为了消除这些误差源 xff0c 必须使用两台以上的GPS接收机同步工作 GPS静态测量的方法是各个接收机独立观测 xff0
  • keil C51脚本编译

    rem 使用bat编译C51项目 64 echo off amp setlocal enabledelayedexpansion set BIN PATH 61 C Keil v5 C51 set path 61 BIN PATH BIN
  • ESP01s连接Arduino uno发送AT指令返回ERROR

    使用ArduinoIDE连接串口之后出现如下情况 xff1a Esp串口返回ERROR 直接放解决方法 xff1a 将串口监视器右下角 换行符 更改为 NL和CR 再次发送AT指令 xff0c 返回OK 解决原理 xff1a Arduino
  • 如何解决python socket server重启后端口被占用的问题

    本文介绍下 xff0c 在solaris 系统下 xff0c python socket server重启后 xff0c 提示端口被占用 xff0c telnet端口失败 这里给出一个解决方法 xff0c 有需要的朋友参考下 在solari
  • 零基础python字符串处理学习心得

    python 类的定义及使用 xff1a 1 数据如下 xff1a stu1 txt 孙同学 2020 5 21 20 39 男 39 77 56 77 76 92 58 91 84 69 91 stu2 txt 赵同学 2020 11 3
  • PaddlePaddle图像分类神经网络构建正则化笔记

    本文主要根据第二次作业进行分析 作业要求 xff1a 补全网络代码 xff0c 并运行手写数字识别项目 以出现最后的图片和预测结果为准 首先导入必要的包 numpy gt python第三方库 xff0c 用于进行科学计算 PIL gt P
  • 使用GitHub.io仓库实现个人网站建站

    使用GitHub io仓库实现个人网站建站 文章目录 使用GitHub io仓库实现个人网站建站 前言一 注册GitHub二 建立储存库1 新建库2 输入名称3 上传文件4 进行测试5 想进入非index网页 总结 前言 总体流程为注册 建
  • 老旧电脑救星,Windows 8.1 原始系统安装

    老旧电脑win8 1系统安装 先上电脑配置开始安装WIN8 1下载镜像制作U盘启动盘进入主板BIOS设置U盘为第一启动项进入安装环节 总结 先上电脑配置 前段时间从店里拉回来一个十年前的电脑 xff0c 装的win7系统 xff0c 从开机
  • orbslam2图优化基础

    图优化 1 优化问题概述 本周的目标是orb slam2中的PoseOptimization函数 xff0c 这个函数算是orb slam2中Traking进程频繁调用且是入门级的优化问题 看代码的时候多次看到g2o库 xff0c 这个库是
  • ORBSLAM2安装与运行实例

    文章目录 安装环境一 安装依赖库二 安装主要库1 Pangolin2 安装Opencv3 安装DBoW2 和 g2o依赖 三 安装ORB SLAM2本体四 运行ORB SLAM2 xff08 单目例子 xff09 总结 安装环境 提示 xf
  • 人体姿态识别综述

    1 人体姿态估计概述 通过图像 视频中的人像信息 xff0c 将人的姿态描述出来 xff0c 重建一个人体的姿态 主流的方法有2D人体姿态估计和3D人体姿态估计 xff0c 都是基于深度学习的方法 2 2D人体姿态识别 单人姿态估计 多人姿
  • HX711压力传感器学习(STM32)

    目录 原理图 xff1a 引脚介绍 xff1a HX711介绍工作原理 程序讲解 xff1a 整套工程 xff1a 原理图 xff1a 引脚介绍 xff1a VCC和GND引脚分别为HX711芯片的电源输入端口 VCC引脚是连接到5V或3
  • 关于STL的理解与学习之路(小白从头学起)

    奔月者不惧黑夜 xff0c 寻芳者不畏荆棘 xff0c 求爱者不避本心 鲁米 写在前面 我只是记录一下我的学习过程 xff0c 算是网络笔记吧 xff0c 都是很基础的东西 xff0c 你要是对STL感兴趣但完全不了解 xff0c 那就继续
  • rosbag转化为.mp4格式视频

    1 把 bag文件放到Python脚本目录下 2 安装依赖 xff08 确保系统下已经安装ros环境 xff09 使用ffmpeg将ros包文件中的图像序列转换为固定帧率的视频文件 xff0c 需要安装ffmpeg sudo apt ins
  • C++各大有名科学计算库

    C 43 43 各大有名科学计算库 C 43 43 各大有名科学计算库 Blitz 43 43 http www oonumerics org blitz POOMA http www codesourcery com pooma poom
  • 给dubbo接口添加白名单——dubbo Filter的使用

    在开发中 xff0c 有时候需要限制访问的权限 xff0c 白名单就是一种方法 对于Java Web应用 xff0c Spring的拦截器可以拦截Web接口的调用 xff1b 而对于dubbo接口 xff0c Spring的拦截器就不管用了
  • 浅谈dubbo的ExceptionFilter异常处理

    背景 我们的项目使用了dubbo进行不同系统之间的调用 每个项目都有一个全局的异常处理 xff0c 对于业务异常 xff0c 我们会抛出自定义的业务异常 xff08 继承RuntimeException xff09 全局的异常处理会根据不同