分布式事务TCC使用手册

1、pom.xml配置

<dependency>
  <groupId>com.jinnjo.txgroupId>
<artifactId>spring-boot-starter-narayana-lraartifactId>
  <version>2.0.9version>
dependency>
<dependency>
  <groupId>org.eclipsegroupId>
  <artifactId>yassonartifactId>
  <version>1.0version>
dependency>
<dependency>
  <groupId>org.glassfishgroupId>
  <artifactId>javax.jsonartifactId>
  <version>1.0.4version>
dependency>

2、修改openfeign依赖

<dependency>
  <groupId>org.springframework.cloudgroupId>
  <artifactId>spring-cloud-starter-openfeignartifactId>
  <exclusions><exclusion>
    <groupId>om.sun.jerseygroupId>
    <artifactId>jersey-clientartifactId>
  exclusion>
    <exclusion><groupId>javax.ws.rsgroupId>
    <artifactId>jsr311-apiartifactId>exclusion>
  exclusions>
dependency>

3、启动类扫描包配置

分布式事务TCC使用手册_第1张图片

示例代码:"com.jinnjo.tx.lra"

引入类配置

分布式事务TCC使用手册_第2张图片

启动类增加事务调度器的配置
/**
 * 事务调度器地址
 */
@Value("${lra.http.host}")
private String lraHost;

/**
 * 事务调度器端口
 */
@Value("${lra.http.port}")
private int lraPort;

@Bean
public LRAClient lraClient() throws URISyntaxException {
    return new NarayanaLRAClient(lraHost,lraPort);
}

Properties配置

##事务调度器地址
lra.http.host=localhost
##事务调度器端口
lra.http.port=8080

4、业务开发

被调用方(创建lra事务处理controller)

示例代码:
/**
 * 卡包消费
 */
@RestController
@Api(tags = "卡包消费", description = "卡包消费(TCC事务处理)")
@Validated
@LRA(LRA.Type.SUPPORTS)
public class CardLraController extends RestControllerBase {
    @Autowired
    private CardPackageService cardPackageService;
    /**
     * 使用卡包消费
     * @param auth 授权码
     * @param amount 金额
     * @param orderNo 订单号
     * @param lraId 事务ID
     * @return
     */
    @RequestMapping(value = "card-lra/notify", method = RequestMethod.POST)
    @ApiOperation(value = "使用卡包消费", notes = "使用卡包消费")
    @LRA(LRA.Type.REQUIRED)
    public ResponseEntity payNotify(@ApiIgnore @AuthenticationPrincipal Authentication auth,
                            @ApiParam(name = "amount", value = "消费金额()", required = true) @RequestParam BigDecimal amount,
                            @ApiParam(name = "orderNo", value = "消费单号", required = true) @RequestParam String orderNo,
                            @RequestHeader(value= LRA_HTTP_HEADER) String lraId){
        UserVO user = UserUtil.getUserForAuthentication(auth);
        if(user!=null && StringUtils.isNotEmpty(user.getUserId())) {
            Boolean result = cardPackageService.payNotify(user.getUserId(), amount, orderNo, lraId);
            if(result){
                return ResponseEntity.ok().build();
            }
        }
        return ResponseEntity.notFound().build();
    }

    /**
     * 使用卡包消费成功后确认
     * @param lraId 事务ID
     * @return
     */
    @RequestMapping(value="card-lra/complete", method = RequestMethod.PUT)
    @ApiOperation(value = "使用卡包消费成功后确认", notes = "使用卡包消费成功后确认", hidden = true)
    @Complete
    public ResponseEntity completeCard(@RequestHeader(LRA_HTTP_HEADER) String lraId){
        Boolean result = cardPackageService.completeCard(lraId);
        if(result){
            return ResponseEntity.ok().build();
        }
        return ResponseEntity.notFound().build();

    }

    /**
     * 使用卡包消费失败后回滚
     * @param lraId 事务ID
     * @return
     */
    @RequestMapping(value="card-lra/compensate", method = RequestMethod.PUT)
    @ApiOperation(value = "使用卡包消费失败后回滚", notes = "使用卡包消费失败后回滚", hidden = true)
    @Compensate
    public ResponseEntity compensateCard(@RequestHeader(LRA_HTTP_HEADER) String lraId){
        Boolean result = cardPackageService.compensateCard(lraId);
        if(result){
            return ResponseEntity.ok().build();
        }
        return ResponseEntity.notFound().build();
    }
}
说明:创建事务处理controller,创建TCC方法(T:try尝试/占有,C:confirm确认/提交,C:cancel补偿/回滚)
      T:尝试(占用)资源,通过事务ID为唯一标识
      C:确认成功(Complete),通过事务ID获取占有的资源进行submit
      C:事务回滚/补偿(Compensate),通过事务ID获取占有的资源进行rowback

调用方(创建lra事务处理controller)

示例代码:
@RestController
@RequestMapping(value="/card-pay")
@LRA(LRA.Type.SUPPORTS)
public class CardPayController {
    static final Logger logger = LoggerFactory.getLogger(CardPayController.class);
    @Autowired
    private OrderInfoService orderInfoService;
    @Autowired
    private LRAClient lraClient;
    @Autowired
    private CardClient cardClient;
    @Autowired
    private ResultPayAssembler resultPayAssembler;
    @Autowired
    private OrderUnifiedRepository orderUnifiedRepository;
    @Autowired
    private OrderInfoRepository orderInfoRepository;

    /**
     * 卡包支付 事务开启接口
     * @param id
     * @param auth
     * @return
     */
    @RequestMapping(value = "/pay/{id}",  method = RequestMethod.PUT, produces = MediaType.APPLICATION_JSON_VALUE)
    @LRA(delayClose = true, join = true)
    @PreAuthorize(OrderConstants.AUTH_SQR_QYG_USER)
    public ResponseEntity paymentOrder(@PathVariable String id, @ApiIgnore @AuthenticationPrincipal Authentication auth) {
        //查询订单信息
        OrderUnified orderUnified = orderUnifiedRepository.findOrderUnifiedByBigOrder(id);
        if (orderUnified == null)
            orderUnified = orderUnifiedRepository.findOrderUnifiedByOrderInfoListContains(orderInfoRepository.findOrderInfoByOrderId(id));
        String lraId = lraClient.getCurrent().toString();
        //预占资源
        try {
            cardClient.saveAmountLRA(orderUnified.getPayAmount().doubleValue(),orderUnified.getBigOrder(),lraId);
        }catch (Exception e){
            e.printStackTrace();
            throw new DataConflictException(HttpStatus.CONFLICT.value(), "金额不足!", new HashSet() {});
        }
        orderInfoService.successOrder(orderUnified.getBigOrder());
            //cardClient.saveAmountLRA(orderUnified.getPayAmount().doubleValue(),orderUnified.getBigOrder(),lraId);
        //修改订单状态
        ResultPayVO vo = new ResultPayVO();
        vo.setMsg(OrderConstants.ORDER_PAY_TYPE_CARDBAGPAY_SUCCESS);
        vo.setCode("0");
        return Optional.ofNullable(vo).map(resultPayAssembler::toResource).map(ResponseEntity::ok).orElse(ResponseEntity.notFound().build());
    }

    /**
     * 事务回滚
     * @param lraId
     * @return
     * @throws MalformedURLException
     */
    @RequestMapping(value = "/{lraId}", method = RequestMethod.DELETE, produces = MediaType.APPLICATION_JSON_VALUE)
    @LRA(LRA.Type.NOT_SUPPORTED)
    public ResponseEntity compensateTrade(@PathVariable(value = "lraId") String lraId) throws MalformedURLException {
        //为了使luraId作为路径参数传递,需要对lraId进行Base64编码,此处相应做解码处理。
        try {
            lraId = URLDecoder.decode(lraId, "utf-8");
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        try {
            String status = lraClient.cancelLRA(new URL(lraId));
            return ResponseEntity.ok(status);
        }catch (GenericLRAException ex){
            if(ex.getStatusCode()==404){
                return ResponseEntity.notFound().build();
            }
            throw ex;
        }
    }

    /**
     * 事务确认
     * @param lraId
     * @return
     * @throws MalformedURLException
     */
    @RequestMapping(value = "/{lraId}", method = RequestMethod.PUT, produces = MediaType.APPLICATION_JSON_VALUE)
    @LRA(LRA.Type.NOT_SUPPORTED)
    public ResponseEntity confirmTrade(@PathVariable(value = "lraId") String lraId) throws MalformedURLException {
        //为了使luraId作为路径参数传递,需要对lraId进行Base64编码,此处相应做解码处理。
        try {
            lraId = new String(Base64.getUrlDecoder().decode(lraId), "utf-8");
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }

        try {
            String status = lraClient.closeLRA(new URL(lraId));
            return ResponseEntity.ok(status);
        }catch (GenericLRAException ex){
            if(ex.getStatusCode()==404){
                return ResponseEntity.notFound().build();
            }
            throw ex;
        }
    }
}
说明:创建事务处理controller,创建TCC方法(T:try尝试/占有,C:confirm确认/提交,C:cancel补偿/回滚)
      T:尝试(占用)资源,通过事务ID为唯一标识
      C:确认成功(Complete),通过事务ID获取占有的资源进行submit
      C:事务回滚/补偿(Compensate),通过事务ID获取占有的资源进行rowback
事务开启接口说明:delayClose如果为true,需要自己调用确认或回滚的方法,适用场景:如果分布式事务中有一方不可控,则需要开发者选择在什么时候调用确认或回滚的方法;如果delayClose设置为false,事务开启接口如果出现异常事务调度器会自动调用回滚操作,如果执行成功则会自动调用确认接口使用场景:所有分布式事务都提供了对应的TCC方法。
cancelOn可设置Response.Status,根据返回code来决定调用确认或回滚的接口。设置示例代码:cancelOn = {INTERNAL_SERVER_ERROR,SERVICE_UNAVAILABLE}

注:通过分布式调用多个模块时,如果有一个返回失败(失败:异常或者返回错误提示),自己程序处理不要再调用其他模块

 

 

你可能感兴趣的:(JavaNotes)