这篇“nacos+springboot+dubbo2.7.3统一处理异常的方法”文章的知识点大部分人都不太理解,所以小编给大家总结了以下内容,内容详细,步骤清晰,具有一定的借鉴价值,希望大家阅读完这篇文章能有所收获,下面我们一起来看看这篇“nacos+springboot+dubbo2.7.3统一处理异常的方法”文章吧。
1. 为什么要抛异常?
不同开发团队间便于追溯异常的来源以及为了便于定位问题的需要
往往实际开发中的架构是这么一个样子的:
dubbo微服务架构简图
不同层的开发人员都是不同的人或者是不同的几波人马;
无状态的API层(一组Tomcat对Nginx Web层的API暴露)是一组开发团队;
微服务Dubbo层是另一组开发团队;
在调试、测试、上线后我们经常会发生各种Exception,此时这几个不同的开发团队间会互相扯皮、打架,并且大家都要忙于定位这个Exception到底是发生在哪一层,甚至需要追溯Exception发生在哪个点(stackTrace)。
Service层有数据库事务一致性的问题必须抛出异常
我们都知道在spring中的Service层必须抛出Runtime Exception,否则Service层的方法如果有涉及数据库的修改操作是不会回滚的。
2. 给出解决方案
其实解决方案真正的无外乎就2种:
provider向远程consumer层直接抛RuntimeException即可;
provider端把所有的Exception进行统一包装,向consumer端返回json报文体的类似message:xxx,code:500,data{xxx:xxx,xxx:xxx}这样的消息而在provider端进行“logger.error”的记录即可;
本文把这2种实现方式都给实现了,下面开始直接show me the code的方式来说话吧。
3. 两种抛异常的实例解说
环境搭建
nacos1.1.4
我们这边不用dubbo admin,因为dubbo admin太老且使用不方便,缺少了很多管理微服务所需要的基本功能。并且dubbo从2.6开始已经把dubbo admin从它的主工程里分离了出去,同时dubbo2.6开始支持nacos registry了。
目前来说nacos是最方便、效率最高、功能最强大的微服务发现组件(甚至支持spring cloud)。
下载地址在这里(请戳):阿里nacos最新下载地址
下载后直接解压,然后进行nacos配置
编辑这个application.properties文件,我们把nacos自动服务发现管理端连上自己开发环境上的mysql。
# springspring.datasource.platform=mysqlserver.contextPath=/nacosserver.servlet.contextPath=/nacosserver.port=8848db.num=1db.url.0=jdbc:mysql://192.168.56.101:3306/nacos?useUnicode=true&characterEncoding=utf-8&useSSL=falsedb.user=nacosdb.password=111111
配完后直接双击:startup.cmd启动nacos
登录界面中使用nacos/nacos即可进行登录了。
登录后看到nacos管理界面就说明nacos配置和启动成功了。接下来我们就要开始书写dubbo的provider端与consumer端了。
dubbo工程搭建
nacos-parent工程
整个工程我已经放在git上了,地址请戳这里:nacos-dubbo-demo
工程的依赖结构如下:
由于dubbo与springboot结合的项目不多,很多网上有的博客也充斥着乱抄、自己都没有验证过就上代码的,因此大多网友们通过网上之言片语拼凑起来的项目在本地很难运行起来,不是maven包冲突就是少这个、那个包。下面给出工程的parent pom文件。
<project xmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><groupId>org.sky.demo</groupId><artifactId>nacos-parent</artifactId><version>0.0.1-SNAPSHOT</version><packaging>pom</packaging><description>Demo project for Spring Boot Dubbo Nacos</description><modules></modules> <properties><java.version>1.8</java.version><spring-boot.version>1.5.15.RELEASE</spring-boot.version><dubbo.version>2.7.3</dubbo.version><curator-framework.version>4.0.1</curator-framework.version><curator-recipes.version>2.8.0</curator-recipes.version><druid.version>1.1.20</druid.version><guava.version>27.0.1-jre</guava.version><fastjson.version>1.2.59</fastjson.version><dubbo-registry-nacos.version>2.7.3</dubbo-registry-nacos.version><nacos-client.version>1.1.4</nacos-client.version><mysql-connector-java.version>5.1.46</mysql-connector-java.version><disruptor.version>3.4.2</disruptor.version><aspectj.version>1.8.13</aspectj.version><nacos-service.version>0.0.1-SNAPSHOT</nacos-service.version><skycommon.version>0.0.1-SNAPSHOT</skycommon.version><maven.compiler.source>${java.version}</maven.compiler.source><maven.compiler.target>${java.version}</maven.compiler.target><compiler.plugin.version>3.8.1</compiler.plugin.version><war.plugin.version>3.2.3</war.plugin.version><jar.plugin.version>3.1.2</jar.plugin.version><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding><project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding></properties><dependencyManagement><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId><version>${spring-boot.version}</version></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-dependencies</artifactId><version>${spring-boot.version}</version><type>pom</type><scope>import</scope></dependency><dependency><groupId>org.apache.dubbo</groupId><artifactId>dubbo-spring-boot-starter</artifactId><version>${dubbo.version}</version><exclusions><exclusion><groupId>org.slf4j</groupId><artifactId>slf4j-log4j12</artifactId></exclusion></exclusions></dependency><dependency><groupId>org.apache.dubbo</groupId><artifactId>dubbo</artifactId><version>${dubbo.version}</version></dependency><dependency><groupId>org.apache.curator</groupId><artifactId>curator-framework</artifactId><version>${curator-framework.version}</version></dependency> <dependency><groupId>org.apache.curator</groupId><artifactId>curator-recipes</artifactId><version>${curator-recipes.version}</version></dependency><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>${mysql-connector-java.version}</version></dependency><dependency><groupId>com.alibaba</groupId><artifactId>druid</artifactId><version>${druid.version}</version></dependency><dependency><groupId>com.lmax</groupId><artifactId>disruptor</artifactId><version>${disruptor.version}</version></dependency><dependency><groupId>com.google.guava</groupId><artifactId>guava</artifactId><version>${guava.version}</version></dependency><dependency><groupId>com.alibaba</groupId><artifactId>fastjson</artifactId><version>${fastjson.version}</version></dependency><dependency><groupId>org.apache.dubbo</groupId><artifactId>dubbo-registry-nacos</artifactId><version>${dubbo-registry-nacos.version}</version></dependency><dependency><groupId>com.alibaba.nacos</groupId><artifactId>nacos-client</artifactId><version>${nacos-client.version}</version></dependency><dependency><groupId>org.aspectj</groupId><artifactId>aspectjweaver</artifactId><version>${aspectj.version}</version></dependency> </dependencies></dependencyManagement><build><plugins><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-compiler-plugin</artifactId><version>${compiler.plugin.version}</version><configuration><source>${java.version}</source><target>${java.version}</target></configuration></plugin><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-war-plugin</artifactId><version>${war.plugin.version}</version></plugin><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-jar-plugin</artifactId><version>${jar.plugin.version}</version></plugin></plugins></build></project>
演示用数据库(mySQL5.7)建表语句
CREATE TABLE `t_product` ( `product_id` int(11) NOT NULL AUTO_INCREMENT, `product_name` varchar(45) DEFAULT NULL, PRIMARY KEY (`product_id`));CREATE TABLE `t_stock` ( `stock_id` int(11) NOT NULL AUTO_INCREMENT, `stock` int(11) DEFAULT NULL, `product_id` int(11) NOT NULL, PRIMARY KEY (`stock_id`));
它建了两张表,t_product表和t_stock表。这两张表我们会用于演示dubbo provider中对于数据库一致性插入时在碰到Exception时怎么处理回滚的场景。
nacos-service工程搭建说明
先上pom.xml(很重要,这里面的依赖是正确的springboot+dubbo+nacos客户端的完整配置)
<project xmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><groupId>org.sky.demo</groupId><artifactId>nacos-service</artifactId><version>0.0.1-SNAPSHOT</version><name>nacos-service</name><description>服务者 Demo project for Spring Boot dubbo nacos</description><parent><groupId>org.sky.demo</groupId><artifactId>nacos-parent</artifactId><version>0.0.1-SNAPSHOT</version></parent> <dependencies> <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-jdbc</artifactId><exclusions><exclusion><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-logging</artifactId></exclusion></exclusions></dependency><dependency><groupId>org.apache.dubbo</groupId><artifactId>dubbo</artifactId></dependency><dependency><groupId>org.apache.curator</groupId><artifactId>curator-framework</artifactId></dependency><dependency><groupId>org.apache.curator</groupId><artifactId>curator-recipes</artifactId></dependency><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId></dependency><dependency><groupId>com.alibaba</groupId><artifactId>druid</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency><dependency><groupId>org.spockframework</groupId><artifactId>spock-core</artifactId><scope>test</scope></dependency><dependency><groupId>org.spockframework</groupId><artifactId>spock-spring</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-configuration-processor</artifactId><optional>true</optional></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-log4j2</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId><exclusions><exclusion><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-logging</artifactId></exclusion></exclusions><exclusion><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-tomcat</artifactId></exclusion></dependency> <dependency><groupId>org.aspectj</groupId><artifactId>aspectjweaver</artifactId></dependency><dependency><groupId>com.lmax</groupId><artifactId>disruptor</artifactId></dependency><dependency><groupId>redis.clients</groupId><artifactId>jedis</artifactId></dependency><dependency><groupId>com.google.guava</groupId><artifactId>guava</artifactId></dependency><dependency><groupId>com.alibaba</groupId><artifactId>fastjson</artifactId></dependency><!-- Dubbo Registry Nacos --><dependency><groupId>org.apache.dubbo</groupId><artifactId>dubbo-registry-nacos</artifactId></dependency><dependency><groupId>com.alibaba.nacos</groupId><artifactId>nacos-client</artifactId></dependency><dependency><groupId>org.sky.demo</groupId><artifactId>skycommon</artifactId><version>${skycommon.version}</version></dependency></dependencies><build><sourceDirectory>src/main/java</sourceDirectory><testSourceDirectory>src/test/java</testSourceDirectory><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId></plugin></plugins><resources><resource><directory>src/main/resources</directory></resource><resource><directory>src/main/webapp</directory><targetPath>META-INF/resources</targetPath><includes><include>**@AfterThrowing(throwing = "exception", pointcut = "execution(* org.sky.service.*.*(..))")public void afterThrow(Throwable exception) {if (exception instanceof RuntimeException) {logger.error("DemoRpcRuntimeExceptionHandler side->exception occured: " + exception.getMessage(),exception);throw new DemoRpcRunTimeException(exception);}// logger.error("DemoRpcRuntimeExceptionHandler side->exception occured: " +// exception.getMessage(), exception);}}
开始进入核心provider Service端的制作。
ProductService接口
我们把它放置于common工程,这样consumer工程也就可以通过nacos的注册中心找到这个接口名,然后通过spring的invoke来对于远程的用于具体实现service逻辑的xxxServiceImpl类进行调用了。
package org.sky.service; import org.sky.exception.DemoRpcRunTimeException;import org.sky.platform.util.DubboResponse;import org.sky.vo.ProductVO; public interface ProductService {public DubboResponse addProductAndStock(ProductVO prod) throws DemoRpcRunTimeException;}
具体业务逻辑实现类,ProductServiceImpl
该类做这么一件事:
1)插入t_product表数据
2)插入t_stock表数据
插两张表时,只要有一点点错误那么整个插入事务回滚,否则成功。这边需要注意的就是:
springboot service只有接到RuntimeException才会回滚;
要把RuntimeException从provider远程传递到consumer端,包括把stackTrace这些信息也远程传递到consumer端,那么这个exception必须是serializable的;
暴露成dubbo provider service的service方法必须加上@Service注解,这个Service可不是spring annotation的service而是ali dubbo的service,在2.7.3开始变成了org.apache.dubbo包了。它配合着springboot的主启动文件中的@EnableDubbo来启作用,它在启动后会通过application.properties中的dubbo.scan.base-packages中所指的路径把这个路径下所有的类寻找是否带有@Service注解,如有那么就把它通过nacos-registry给注册到nacos中去;
ProductServiceImpl.java
package org.sky.service; import java.sql.Connection;import java.sql.PreparedStatement;import java.sql.SQLException; import org.apache.dubbo.config.annotation.Service;import org.sky.exception.DemoRpcRunTimeException;import org.sky.platform.util.DubboResponse;import org.sky.vo.ProductVO;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.http.HttpStatus;import org.springframework.jdbc.core.JdbcTemplate;import org.springframework.jdbc.core.PreparedStatementCreator;import org.springframework.jdbc.support.GeneratedKeyHolder;import org.springframework.jdbc.support.KeyHolder;import org.springframework.transaction.annotation.Transactional; @Service(version = "1.0.0", interfaceClass = ProductService.class, timeout = 120000)public class ProductServiceImpl extends BaseService implements ProductService {@AutowiredJdbcTemplate jdbcTemplate; @Override@Transactionalpublic DubboResponse<ProductVO> addProductAndStock(ProductVO prod) throws DemoRpcRunTimeException {DubboResponse<ProductVO> response = null;int newProdId = 0;String prodSql = "insert into t_product(product_name)values(?)";String stockSql = "insert into t_stock(product_id,stock)values(?,?)";try {if (prod != null) {KeyHolder keyHolder = new GeneratedKeyHolder(); jdbcTemplate.update(new PreparedStatementCreator() {@Overridepublic PreparedStatement createPreparedStatement(Connection connection) throws SQLException {PreparedStatement ps = connection.prepareStatement(prodSql, new String[] { "id" });ps.setString(1, prod.getProductName());return ps;}}, keyHolder);newProdId = keyHolder.getKey().intValue();logger.info("======>insert into t_product with product_id:" + newProdId);if (newProdId > 0) {jdbcTemplate.update(stockSql, newProdId, prod.getStock());logger.info("======>insert into t_stock with successful");ProductVO returnData = new ProductVO();returnData.setProductId(newProdId);returnData.setProductName(prod.getProductName());returnData.setStock(prod.getStock());response = new DubboResponse(HttpStatus.OK.value(), "success", returnData);//throw new Exception("Mk throwed exception to enforce rollback[insert into t_stock]");return response;} } else {throw new DemoRpcRunTimeException("error occured on ProductVO is null");}} catch (Exception e) {logger.error("error occured on Dubbo Service Side: " + e.getMessage(), e);throw new DemoRpcRunTimeException("error occured on Dubbo Service Side: " + e.getMessage(), e);}return response;} }
这个类目前是正常状态,我们先调用一把正常的provider到service端的过程然后接下来就来演示如何把exception远程传递到consumer端。
nacos-consumer工程搭建说明
先上pom.xml
<project xmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><groupId>org.sky.demo</groupId><artifactId>nacos-consumer</artifactId><version>0.0.1-SNAPSHOT</version><name>nacos-service</name><description>消费者 Demo project for Spring Boot dubbo nacos</description><parent><groupId>org.sky.demo</groupId><artifactId>nacos-parent</artifactId><version>0.0.1-SNAPSHOT</version></parent> <dependencies><dependency><groupId>org.apache.dubbo</groupId><artifactId>dubbo</artifactId></dependency><dependency><groupId>org.apache.curator</groupId><artifactId>curator-framework</artifactId></dependency><dependency><groupId>org.apache.curator</groupId><artifactId>curator-recipes</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency><dependency><groupId>org.spockframework</groupId><artifactId>spock-core</artifactId><scope>test</scope></dependency><dependency><groupId>org.spockframework</groupId><artifactId>spock-spring</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-configuration-processor</artifactId><optional>true</optional></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-log4j2</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId><exclusions><!-- 去掉默认配置 --><exclusion><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-logging</artifactId></exclusion></exclusions></dependency><dependency><groupId>com.lmax</groupId><artifactId>disruptor</artifactId></dependency><dependency><groupId>com.google.guava</groupId><artifactId>guava</artifactId></dependency><dependency><groupId>com.alibaba</groupId><artifactId>fastjson</artifactId></dependency><!-- Dubbo Registry Nacos --><dependency><groupId>org.apache.dubbo</groupId><artifactId>dubbo-registry-nacos</artifactId></dependency><dependency><groupId>com.alibaba.nacos</groupId><artifactId>nacos-client</artifactId></dependency><dependency><groupId>org.aspectj</groupId><artifactId>aspectjweaver</artifactId></dependency><!-- import sky common package --><dependency><groupId>org.sky.demo</groupId><artifactId>skycommon</artifactId><version>${skycommon.version}</version></dependency></dependencies><build><sourceDirectory>src/main/java</sourceDirectory><testSourceDirectory>src/test/java</testSourceDirectory><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId></plugin></plugins><resources><resource><directory>src/main/resources</directory></resource><resource><directory>src/main/webapp</directory><targetPath>META-INF/resources</targetPath><includes><include>**@Pointcut(value = "execution(* org.sky.service.*.*(..))")private void servicePointcut() {} @Pointcut(value = "@annotation(org.springframework.transaction.annotation.Transactional)")private void transactionalPointcut() {} @Around("servicePointcut() && !transactionalPointcut()")public Object doAround(ProceedingJoinPoint pjp) {Object[] args = pjp.getArgs();try {return pjp.proceed();} catch (Exception e) {processException(pjp, args, e);return DubboResponse.error("exception occured on dubbo service side: " + e.getMessage());} catch (Throwable throwable) {processException(pjp, args, throwable);return DubboResponse.error("exception occured on dubbo service side: " + throwable.getMessage());}} @Around("servicePointcut() && transactionalPointcut()")public Object doTransactionalAround(ProceedingJoinPoint pjp) throws Throwable {try {return pjp.proceed();} catch (Exception e) {Object[] args = pjp.getArgs();// dubbo会将异常捕获进行打印,这里就不打印了processException(pjp, args, e);// logger.error("service with @Transactional exception occured on dubbo service// side: " + e.getMessage(), e);throw new RuntimeException(e.getMessage(), e);}} private void processException(final ProceedingJoinPoint joinPoint, final Object[] args, Throwable throwable) {String inputParam = "";if (args != null && args.length > 0) {StringBuilder sb = new StringBuilder();for (Object arg : args) {sb.append(",");sb.append(arg);}inputParam = sb.toString().substring(1);}logger.error("\n 方法: {}\n 入参: {} \n 错误信息: {}", joinPoint.toLongString(), inputParam,Throwables.getStackTraceAsString(throwable));}}
它的作用就是:
把一切Exception使用一个叫DubboResponse的请求体来返回provider端的service报文;
如果provider端出错,那么也把错误的系统code与系统message“包”在DubboResponse内
等等等等。。。。。。出问题了!此处还没全完,为什么?
一切Exception?这样一来那么包完后在Service层岂不是没有Exception被抛出了?如果Service方法涉及到数据库操作没有抛RuntimeException时数据库事务怎么回滚?
这才有了我们在这个handler类中有这么一段内容,它的作用就是对一切有@Transactional注解的Service方法在其出错时,还是照样要抛"RuntimeException",对于其它的就都包成DubboResponse返回给调用者了(如下对于非事务型Service方法的异常的统一包装):
@Around("servicePointcut() && !transactionalPointcut()")public Object doAround(ProceedingJoinPoint pjp) {Object[] args = pjp.getArgs();try {return pjp.proceed();} catch (Exception e) {processException(pjp, args, e);return DubboResponse.error("exception occured on dubbo service side: " + e.getMessage());} catch (Throwable throwable) {processException(pjp, args, throwable);return DubboResponse.error("exception occured on dubbo service side: " + throwable.getMessage());}}
好了,然后我们现在重新启动我们的系统,我们再来看下面的运行示例。。。。。。
等!
忘记一件事,下面我给出位于“common”工程中的ProductVO和DubboResponse这两个类的结构先,我写博文不喜欢“藏”一手。
ProductVO.java
package org.sky.vo; import java.io.Serializable; public class ProductVO implements Serializable { private int stock = 0; public int getStock() {return stock;} public void setStock(int stock) {this.stock = stock;} public String getProductName() {return productName;} public int getProductId() {return productId;} public void setProductId(int productId) {this.productId = productId;} public void setProductName(String productName) {this.productName = productName;} private int productId = 0;private String productName = "";}
DubboResponse.java
package org.sky.platform.util; import java.io.Serializable; import org.springframework.http.HttpStatus; import com.alibaba.fastjson.JSON; public class DubboResponse<T> implements Serializable {private static final long serialVersionUID = 1L; private int code; private String message; private T data; public DubboResponse(int code, String message) {this.code = code;this.message = message;} public DubboResponse(int code, String message, T data) {this.code = code;this.message = message;this.data = data;} public T getData() {return data;} public void setData(T data) {this.data = data;} public static <T> DubboResponse success(String message, T data) {String resultStr = JSON.toJSONString(data);return new DubboResponse(HttpStatus.OK.value(), message, data);} public static DubboResponse success(String message) {return success(message, null);} public static DubboResponse error(String message) {return new DubboResponse(HttpStatus.INTERNAL_SERVER_ERROR.value(), message, null);} public static DubboResponse error(int code, String message) {return new DubboResponse(code, message, null);} public int getCode() {return code;} public void setCode(int code) {this.code = code;} public String getMessage() {return message;} public void setMessage(String message) {this.message = message;}}
“正常”据有@Transactional的Service方法抛异常演示:
现在我们把nacose-service和nacos-consumer运行起来看效果,试图插入一个新的prouct:
得到返回:
再来看nacos-service端、nacos-consumer端以及数据库
可以看到provider与consumer端都正确抛错且数据库中没有插进去值。
“不正常”的不含有Transactional的(普通)Service方法抛异常被封装演示:
我们现在做点小手脚,我们把provider端的“addProductAndStock(ProductVO prod)”方法上的@Transactional拿走来看看效果。
@Overridepublic DubboResponse<ProductVO> addProductAndStock(ProductVO prod) throws DemoRpcRunTimeException {DubboResponse<ProductVO> response = null;int newProdId = 0;String prodSql = "insert into t_product(product_name)values(?)";String stockSql = "insert into t_stock(product_id,stock)values(?,?)";try {if (prod != null) {KeyHolder keyHolder = new GeneratedKeyHolder(); jdbcTemplate.update(new PreparedStatementCreator() {@Override
请像上面这样的代码片端
我们再在nacos-consume端做一个小小的修改,如下所示,让consumer端直接把provider端组装好的{ "message" : "xxxx..."}显示在“最前端”(一切通过 nginx端来访问consumer,consumer再通过provider调用数据库,在这边我们使用的是postman)。
然后我们来运行起来看一下效果:
我们可以看到,这一次在去除了@Transactional注解后,当Service方法抛错时,请求端拿到的是我们经过包装过的DubboResponse内的东西
provider端包装普通Service抛出的异常的核心代码:
@Around("servicePointcut() && !transactionalPointcut()")public Object doAround(ProceedingJoinPoint pjp) {Object[] args = pjp.getArgs();try {return pjp.proceed();} catch (Exception e) {processException(pjp, args, e);return DubboResponse.error("exception occured on dubbo service side: " + e.getMessage());} catch (Throwable throwable) {processException(pjp, args, throwable);return DubboResponse.error("exception occured on dubbo service side: " + throwable.getMessage());}}
我们查看我们的Provider端,它正是通过上述代码catch(Exception e)中的这一段来进行服务端日志的记错和把错误包装后返回给到consumer端的,就是下面这两句:
processException(pjp, args, e);return DubboResponse.error("exception occured on dubbo service side: " + e.getMessage());
来看看nacos-service端的日志输出:
来看看nacos-consumer端的日志输出:
哈哈,这次nacos-consumer端无任何抛错,因为错误已经被provider端包装起来了。
当然,当我们看我们的DB端时,肯定,是有数据插入成功的。
因为前文说了,对于无@Transactional注解的方法,我们的aop handler类会把一切错误 “吃掉”,在后台仅作记录然后包成正常返回结果给到consumer端的,那么provider端的Service方法既无RuntimeException抛出,何来回滚?
当然是插入成功的!
t_product表
t_stock表
以上就是关于“nacos+springboot+dubbo2.7.3统一处理异常的方法”这篇文章的内容,相信大家都有了一定的了解,希望小编分享的内容对大家有帮助,若想了解更多相关的知识内容,请关注编程网行业资讯频道。