> 文档中心 > SpringCloudAlibaba之Seata微服务之间调用(八)

SpringCloudAlibaba之Seata微服务之间调用(八)

上一节讲述了seata分布式事务设计到的业务表、业务表对应数据库需要seata的表以及商品微服务的代码。本节继续上一节未未完成剩余的两个微服务的描述。

用户账户微服务Account

pom.xml、application.yml、application-dev.yml、MybatisConfig、主函数相关代码和微服务Goods几乎一样,不在贴代码列举。

其中application.yml中server.port端口和spring.application.name应用名称根据自己的情况更改,上图是我这边配置。

新增一个AccountController类,增加一个减少账户余额的请求实现。如下:

@PostMapping("/user/reduceBalance")    public String balance(@RequestParam("userid") String userid, @RequestParam("cost") String cost) throws Exception { boolean isSuccess = accountService.reduceBalance(userid,cost); Map map= new HashMap(8); map.put("code",isSuccess?"ok":"fail"); map.put("msg",isSuccess?"成功":"失败"); return jsonUtil.objectToJson(map);    }

accountService的核心代码

public boolean reduceBalance(String userid, String cost) throws Exception { logger.info("事务XID:{}", RootContext.getXID()); UserAccount userAccount = userAccountMapper.selectByPrimaryKey(Long.parseLong(userid)); if (userAccount.getAccount()0 ? true:false;    }

UserAccountMapper.xml中减少账户余额核心的sql

    update user_account    set account = account - #{cost,jdbcType=BIGINT}    where userid = #{userid,jdbcType=BIGINT} AND account > #{cost,jdbcType=BIGINT}  

订单微服务Order

pom.xml、application.yml、application-dev.yml、MybatisConfig、主函数相关代码和微服务Goods几乎一样,不在贴代码列举。

 application.yml中server.port端口和spring.application.name应用名称根据自己的情况更改,上图是我这边配置。

pom.xml在原来的基础上增加openfeign

     org.springframework.cloud     spring-cloud-starter-openfeign 

因openfeign内部使用了ribbon实现,默认超时时间为1秒,为了避免超时的情况,更改超时时间。设置为10000毫秒。application.yml中增加配置如下

如果想了解那里设置了默认时间,请查看RibbonClientConfiguration类,如下:

增加AccountFeign接口,调用用户账户相关请求。如下:

@Service@FeignClient("ACCOUNT")public interface AccountFeign {    @PostMapping("/user/reduceBalance")    public String balance(@RequestParam("userid") String userid, @RequestParam("cost") String cost);    @GetMapping("/user/{id}")    public String userAccount(@PathVariable("id") Long id);}

注意:这里的增加了FeignClient注解,值“ACCOUNT”为用户账户微服务(ACCOUN)中spring.application.name的值。

增加GoodsFeign接口,调用商品相关请求。如下:

@Service@FeignClient("GOODS")public interface GoodsFeign {    @GetMapping("/goods/{id}")    public String goodsInfo(@PathVariable(value = "id") String id);    @PostMapping("/goods/deductStorage")    public String deductStorage(@RequestParam(value="id",defaultValue = "0") String id,@RequestParam(value="count",defaultValue = "0") String count);}

新增一个OrderController的类,增加创建订单请求代码。如下:

@PostMapping("/order/createOrder")    public String createOrder(@RequestParam(value="userId",defaultValue = "") String userId,  @RequestParam(value="commodityCode",defaultValue = "")String commodityCode,  @RequestParam(value="count",defaultValue = "")String count) throws Exception { Order order=orderService.saveOrder(userId,commodityCode,count); return jsonUtil.objectToJson(order);    }
OrderService核心代码
@Resource    OrderMapper orderMapper;    @Resource    GoodsFeign goodsFeign;    @Resource    AccountFeign accountFeign;    @GlobalTransactional(name = "createOrder",timeoutMills = 120000,rollbackFor = Exception.class)    @Override    public Order saveOrder(String userId, String commodityCode, String count) throws Exception { logger.info("------用户下单---------"); logger.info("事务XID:{}", RootContext.getXID()); //保存订单 Order order = new Order(); order.setUserid(Long.parseLong(userId)); order.setCommoditycode(Long.parseLong(commodityCode)); order.setNum(Long.parseLong(count)); //获取商品信息以及库存信息 String jsonGoods=goodsFeign.goodsInfo(commodityCode); Map map=jsonUtil.jsonToMap(jsonGoods); Long price = Long.parseLong(map.getOrDefault("price",0L).toString()); Long money = price*Long.parseLong(count); order.setMoney(money); //更新库存信息 goodsFeign.deductStorage(commodityCode,count); //扣减余额 accountFeign.balance(userId,money.toString()); //初始化订单状态 order.setStatus(1L); int insertRow = orderMapper.insertSelective(order); logger.info("保存订单id:{}{}", order.getOrderid(), insertRow>0?"成功":"失败"); //int updateRow = orderMapper.updateStatus(Long.parseLong(String.valueOf(insertRow)),1L); //logger.info("更新订单id:{} {}",order.getOrderid(),updateRow>0?"成功":"失败"); return order;    }

注意,这个使用了GlobalTransactional注解,增加了全局分布式事务,设置timeoutMills超时时间为120秒(因上文设置ribbon的超时时间为10秒,只要所有openfeign不超过这个时间就行),所有的异常都回滚。

OrderMapper.xml中创建订单核心的sql
    insert into `order`           orderid,             commoditycode,             userId,             num,             money,             status,                     #{orderid,jdbcType=BIGINT},             #{commoditycode,jdbcType=BIGINT},             #{userid,jdbcType=BIGINT},             #{num,jdbcType=BIGINT},             #{money,jdbcType=BIGINT},             #{status,jdbcType=BIGINT},            

启动程序

依次启动商品微服务Goods、用户账户微服务Account和订单微服务Order程序。查看nacos服务列表,有4个服务。

 发送post请求http://localhost:7001/order/createOrder?userId=1&commodityCode=1&count=9

userd为1用户只有100块,余额不足,购买失败,订单表数据为空,事务回滚成功。查看日志如下

2022-04-10 23:39:28.024  INFO 18939 --- [nio-7002-exec-2] c.j.goods.service.impl.GoodsServiceImpl  : 事务XID:192.168.0.105:8091:2565654285484400642022-04-10 23:39:29.757  INFO 18939 --- [ch_RMROLE_1_2_8] i.s.c.r.p.c.RmBranchRollbackProcessor    : rm handle branch rollback process:xid=192.168.0.105:8091:256565428548440064,branchId=256565446432952321,branchType=AT,resourceId=jdbc:mysql://120.25.151.44:3306/seata_order,applicationData=null2022-04-10 23:39:29.761  INFO 18939 --- [ch_RMROLE_1_2_8] io.seata.rm.AbstractRMHandler     : Branch Rollbacking: 192.168.0.105:8091:256565428548440064 256565446432952321 jdbc:mysql://120.25.151.44:3306/seata_order2022-04-10 23:39:30.008  INFO 18939 --- [ch_RMROLE_1_2_8] i.s.r.d.undo.AbstractUndoLogManager      : xid 192.168.0.105:8091:256565428548440064 branch 256565446432952321, undo_log deleted with GlobalFinished2022-04-10 23:39:30.046  INFO 18939 --- [ch_RMROLE_1_2_8] io.seata.rm.AbstractRMHandler     : Branch Rollbacked result: PhaseTwo_Rollbacked2022-04-10 23:39:30.124  INFO 18939 --- [ch_RMROLE_1_3_8] i.s.c.r.p.c.RmBranchRollbackProcessor    : rm handle branch rollback process:xid=192.168.0.105:8091:256565428548440064,branchId=256565444398714880,branchType=AT,resourceId=jdbc:mysql://120.25.151.44:3306/seata_order,applicationData=null2022-04-10 23:39:30.124  INFO 18939 --- [ch_RMROLE_1_3_8] io.seata.rm.AbstractRMHandler     : Branch Rollbacking: 192.168.0.105:8091:256565428548440064 256565444398714880 jdbc:mysql://120.25.151.44:3306/seata_order2022-04-10 23:39:30.207  INFO 18939 --- [ch_RMROLE_1_3_8] i.s.r.d.undo.AbstractUndoLogManager      : xid 192.168.0.105:8091:256565428548440064 branch 256565444398714880, undo_log added with GlobalFinished2022-04-10 23:39:30.246  INFO 18939 --- [ch_RMROLE_1_3_8] io.seata.rm.AbstractRMHandler     : Branch Rollbacked result: PhaseTwo_Rollbacked

请求参数count改为1时,发送请求

http://localhost:7001/order/createOrder?userId=1&commodityCode=1&count=1

下单成功,返回结果