在普通的web项目中,调用接口返回数据,如下,不出错返回一种,出错了,返回另外一种。前端是直接可以拿到返回的信息的。
@GetMapping("decreaseMoney")
@ResponseBody
public void decreaseMoney(Integer id,BigDecimal money){
try {
//do something
return "成功:something";
}catch (Exception ex){
ex.printStackTrace();
return "失败:something";
}
}
但是引入byteTCC框架后,不能这么处理。因为事务是commit还是rollback,就是根据是否有异常来的控制事务走向的,如果try掉了,那事务最终都是会被commit的,就不会再rollback了。
关于这个问题的处理,请教了下byteTCC的维护者,非常耐心的回答了关于这个问题的疑问。这里记录下交流的这个过程,没有格式的是我提问的,有引用格式的是作者的回答:
当调用失败后,我想拿到这个错误堆栈信息,怎么获取呢?我想把错误信息拿到存日志或者是返回
正常情况下,这个修改成功是可以返回到页面,但是调用出错时,这个return没法返回到页面
错误一般都是用异常来表示啊,用字符串表示很少见。你这个是用于显示的,但是SpringCloud更倾向于代表一个服务一个接口
比如我这个,一个服务调用了2个服务,其中一个出错了,我需要给前端一个反馈,但是我在这里没法拿到出错的那个服务的错误信息
那这种一般怎么处理呢
显示的内容更多的是由Web系统来负责的,微服务负责显示个人觉得不太合理,更何况,服务中的异常会改变事务完成方向呢,如果你把异常捕捉了返回一个字符串,事务就总是commit了。 你可以考虑抛出一个异常啊,不过如果ByteTCC在commit/rollback处理过程中也碰到异常,以事务异常优先抛出
现在出现异常时,页面直接就这样,实际开发中,这样处理不妥啊
这是ByteTCC在rollback过程中也碰到异常了,抛出的是SystemException 说错了,是在commit过程中 HTTP接口一般返回500码就能标识错误了,当然,如果你想在应用层面设置自己的业务异常码,可以考虑用Filter拦截这个接口然后转换,直接返回字符串肯定是不可以的
还是有点不懂,我们这习惯在正常时返回一种编码和结果,出错时在catch中返回一种编码和结果。意思是,我说的这种实现,这里是做不了的是吧。我看那个catch中的打印语句执行了,但是return未执行。
那就让前端自己判断状态码?
你这种做法不是不可以,只是说:在参与事务处理的controller中这样做不可以,不参与事务处理的controller中这样做是没问题的。原因也很简单,spring的声明式事务是要根据异常来判断事务是否commit/rollback的,如果业务把异常信息都自己吞掉了,那所有事务就都commit再也没有rollback了。。。 然后,再进一步的说,一般而言,SpringCloud的controller更倾向于就是一个接口(类似dubbo接口那种,提供的是一个服务能力),很大概率是要直接参与事务的,所以不建议这样做。但你要是一定让一个spring cloud接口做成显示的接口,倒也不是不行,那就别让它参与事务就好了
“spring的声明式事务是要根据异常来判断事务是否commit/rollback的,如果业务把异常信息都自己吞掉了,那所有事务就都commit再也没有rollback了”这句话,大概理解了,我try catch后,发现即使出错了,事务也commit了,导致数据变更了
这是spring声明式事务的机制,根据异常来判断是否需要回滚。没有异常时肯定就是提交了。 当然,也并不是说你在controller中抛出异常就只能显示那个500了,你可以考虑在框架层面对其进行处理,构建自己业务系统的业务异常码,只要在全局事务之外就可以
还有2个疑问:我A调用B和C服务,比如这里,bank服务调用user,company服务,那我这个接口中,bank不仅掉了b,c的接口,还调用自己本地的方法,那这个本地方法也是需要tcc逻辑的是吧?2.这个时候,b和c的controller中接口我不返回信息,那A这个接口,是要对页面提供返回值的,这种推荐怎么处理?
我个人比较推荐的做法是,就象你调用dubbo接口一样,如果没有实质的信息需要返回,那就别返回信息了。没异常就表示成功了,有异常consumer就会收到一个异常信息。至于页面显示什么,那是consumer收到成功/错误之后自己决定的,而不应该由provider来决定页面来显示什么 provider端接口返回一个“调用成功”、“调用失败”这中信息,是完全没有意义的。 HTTP接口,成功时200返回码就可以;返回4xx/5xx时就是失败了。在此基础上,你可以细化一下,比如你们希望所有的请求都返回200,但是错误时响应体内有failure-code,比如00000是成功,00001是创建订单失败,00002表示库存不足等等,可以考虑通过Filter在框架层面封装,而不是在controller中做这个事情
comsumer的接口,也不需要显式的返回信息,直接void,没问题就成功了,有问题的话,页面调用这个接口时,会直接拿到某种异常信息,判断下即可。可以这么简单的处理吗?或者,用filter来处理。这2种方式?
一般而言,微服务之间很少需要这样的封装,直接以异常或者HTTP返回码更适合,只有web系统和微服务之间,可能才需要这种方式,当然也不能一概而论,主要还是看业务系统自行规划 你可以参考一下ByteTCC的CompensableCoordinatorController的做法,出错时返回500,然后在header中加上错误的类型 注意,是说你的Filter可以参考CompensableCoordinatorController,业务Controller不能象CompensableCoordinatorController那样写,业务controller有错误需要回滚就要抛异常