Best efforts to notify the Distributed Transaction Solutions

One solution is best to notify the distributed transaction program, below is an example of a recharge:

 

Interactive process:
1, account recharge system call system interface
2, recharge the system to complete the payment process initiated recharge result notification to the account system

If the notification failed, the refill system by repeated notification policy
3, the system receives a recharge account modifications result notification recharge state.
4, account system does not receive notification system will automatically call the recharge interface for recharging the results.
By the example above we summarize best to inform program objectives:
target: to initiate a notification party processing business results through certain mechanisms best efforts to notify the recipient.
Specifically includes:
1, a certain message is repeated notification mechanism.
Since the receiving party may not receive notification of the notification, at this time there should be some mechanism to duplicate notification message.
2, the message proofreading mechanisms.
If you did not make the greatest efforts to inform the recipient, or the recipient post-consumer news to the consumer again, this time by the query message to the recipient actively notified party
information to meet demand.
Best efforts to notify and reliable information consistency What is the difference?
1, solve different scenarios thought
to reliable sources consistency, initiated notifying parties need to ensure that the message sent out, and sends the message to the receiving party notice, the reliability of the key messages to initiate a notification by the
party to be guaranteed.
Best efforts to notify, inform the party launched its best efforts to notify the result of the business process to receive notification party, but may not receive the message, then need to take
to close the initiative to call notifying parties to initiate a notification party interface query processing business results, reliable notification the key receive notification of the party.
2, two different business scenarios
reliable source of concern is the consistency of the transaction consistent with the trading process, asynchronously to complete the transaction.
Best to inform concerned that after the transaction notice Affairs, reliable trading results coming out notice.
3, different technical solutions directions
reliable message sent from the consistency to be Solved by the consistency received message, i.e. the message is sent and received.
Best efforts can not guarantee that the notification message is received from the issue to the consistency, reliability mechanisms only messages received. Reliable mechanism, the elimination of the utmost
interest to the recipient a notification, when a message can not be received by the receiver, the receiver-initiated query message (the processing result).

Solutions
by understanding best to inform, using MQ ack mechanism can be achieved best to notice.
plan 1:

 

 

 

This embodiment is the use of MQ ack notification mechanism to send notification to the receiving party by the MQ, process is as follows:
1. The initiating party notification to notify MQ.
Use common message notification mechanism will send MQ.
Note: If the message is not sent out notification by the receiving party initiative to initiate a notification requesting party business query execution results. (Behind speaks)
2, to receive notifications party monitor MQ.
3, the receiving party receives the notification message, a transaction completion response ack.
4, receive notifications if the party did not respond to repeated ack the MQ notice.
MQ will follow 1min, 5min, 10min, 30min, 1h, 2h, 5h, 10h manner interval, gradually widened notification interval (if MQ employed
rocketMq, can be arranged in the broker) until the time window upper reaches notification request.
5, before receiving a notification message to the consistency of proofreading by the message proofreading interface.
Scheme 2:
This embodiment also uses the MQ ack mechanism, different from the embodiment 1 is the application to receive notification sends a notification, as shown below:

Interactive process is as follows:
1, the party will initiate a notification notification to MQ.
Transaction message using the same scheme to ensure reliable message atomicity local affairs and the message will eventually notice first issued MQ.
2, the notification listens MQ, MQ message received.
Party 1 receives notification scheme direct monitor MQ, MQ listener in Scheme 2 by the notification.

If there is no response to the notification procedure ack the MQ repeated notifications.
3, the program calls a notification via the Internet receive notifications Program Interface interface protocol (e.g., http, webservice), completion notification.
Notification procedure call interface to receive notifications success notification indicating that the program is successful, i.e. a successful consumption MQ message, MQ will not deliver notification message notifies the program
information.
4, before receiving a notification message to the consistency of proofreading by the message proofreading interface.
Differs from Scheme 1 and Scheme 2:
1, scheme 1 receives the notified party and MQ interfaces, i.e., MQ listener receives notification scheme, the main application and notification between this internal application program.
After 2, Scheme 2 by the listener MQ MQ interfaces with the notification program, the program notification received by the MQ message notification procedure calls received through the Internet interface protocol
notification party. This notification scheme mainly applied between the external application, the result of payment such as payment Po, micro message notification.

RocketMQ maximum effort to notify the transaction type
business description
examples of the realization utmost to notice distributed transaction RocketMq middleware, simulation recharge process.
This case has two accounts systems and micro-recharge system services, which account system database is bank1 database, including Joe Smith account. Recharge system
database use bank1_pay database to record the prepaid account records.
Business processes as shown below:

 

Interaction process is as follows:
1, a user request to recharge recharge system.
2, complete recharge recharge system will recharge results to MQ.
3, the system monitor the MQ account, recharge the reception result notification, if the message is not received, the MQ repeatedly sends a notification. The results received notice account recharge system
system to increase the amount of recharge.
4, account inquiry system can also take the initiative to recharge the system to recharge the result of a query interface, increase the amount.

pay achieve the following functions:
1, recharge the interface
2, to complete the recharge notification
3, the recharge result query interface
2) Dao

@Mapper
@Component
public interface AccountPayDao {
    @Insert("insert into account_pay(id,account_no,pay_amount,result) values(#{id},#{accountNo},#{payAmount},#{result})")
    int insertAccountPay(@Param("id") String id, @Param("accountNo") String accountNo, @Param("payAmount") Double pay_amount, @Param("result") String result);

    @Select("select id,account_no accountNo,pay_amount payAmount,result from account_pay where id=#{txNo}")
    AccountPay findByIdTxNo(@Param("txNo") String txNo);



}

3)Service

@Service 
@ SLF4J 
public  class AccountPayServiceImpl the implements AccountPayService { 

    @Autowired 
    AccountPayDao accountPayDao; 

    @Autowired 
    RocketMQTemplate rocketMQTemplate; 

    // insert top recording 
    @Override
     public AccountPay insertAccountPay (AccountPay accountPay) {
         int Success = accountPayDao.insertAccountPay (accountPay.getId (), accountPay. getAccountNo (), accountPay.getPayAmount (), "Success" );
         IF (Success> 0 ) {
             // send a notification, a notification message is sent using common 
            accountPay.setResult ( "Success" );
            rocketMQTemplate.convertAndSend ( "topic_notifymsg" , accountPay);
             return accountPay; 
        } 
        return  null ; 
    } 

    // for recharging recorded reception notification party calls this method to query the recharge result 
    @Override
     public AccountPay getAccountPay (String TXNO) { 
        AccountPay accountPay = accountPayDao. findByIdTxNo (TXNO);
         return accountPay; 
    } 
}

4)Controller

@RestController
public class AccountPayController {

    @Autowired
    AccountPayService accountPayService;

    //充值
    @GetMapping(value = "/paydo")
    public AccountPay pay(AccountPay accountPay){
        //生成事务编号
        String txNo = UUID.randomUUID().toString();
        accountPay.setId(txNo);
        return accountPayService.insertAccountPay(accountPay);
    }

    //查询充值结果
    @GetMapping(value = "/payresult/{txNo}")
    public AccountPay payresult(@PathVariable("txNo") String txNo){
        return accountPayService.getAccountPay(txNo);
    }
}

bank1 achieve the following functions:
1, the MQ listening, receiving recharge result, the amount of modification according to the account to complete the recharge result.
2, the initiative for recharging system, complete account recharge amount modified according to the results.
1) Dao

@Mapper 
@Component 
public  interface AccountInfoDao {
     // modify the amount of accounts 
    @Update ( "the SET Update ACCOUNT_INFO the account_balance the account_balance = {AMOUNT} + # = # {accountNo the WHERE account_no}" )
     int updateAccountBalance (@Param ( "accountNo") String accountNo, @ param ( "AMOUNT" ) Double AMOUNT); 


   // query idempotent recorded for idempotent control 
    @Select ( "SELECT COUNT (. 1) from de_duplication WHERE tx_no = {#} TXNO" )
     int isExistTx (String TXNO); 

    / / add transaction record, for controlling idempotent 
    @Insert ( "INSERT INTO de_duplication values ({#} TXNO, now ());" )
     int addTx (String TXNO); 

}

2)AccountInfoService

@Service
@Slf4j
public class AccountInfoServiceImpl implements AccountInfoService {

    @Autowired
    AccountInfoDao accountInfoDao;

    @Autowired
    PayClient payClient;

    //更新账户金额
    @Override
    @Transactional
    public void updateAccountBalance(AccountChangeEvent accountChange) {
        //幂等校验
        if(accountInfoDao.isExistTx(accountChange.getTxNo())>0){
            return ;
        }
        int i =accountInfoDao.updateAccountBalance (accountChange.getAccountNo (), accountChange.getAmount ());
         // insert transaction record, for controlling idempotent 
        accountInfoDao.addTx (accountChange.getTxNo ()); 
    } 

    // remote call for recharging results 
    @Override
     public queryPayResult AccountPay (String tx_no) { 

        // remote call 
        AccountPay payresult = payClient.payresult (tx_no);
         IF ( "Success" .equals (payresult.getResult ())) {
             // update account the amount 
            accountChangeEvent accountChangeEvent = new new accountChangeEvent (); 
            accountChangeEvent.setAccountNo (payresult.getAccountNo ()); //Account 
            accountChangeEvent.setAmount (payresult.getPayAmount ()); // amount 
            accountChangeEvent.setTxNo (payresult.getId ()); // recharge transaction number 
            updateAccountBalance (accountChangeEvent); 
        } 
        return payresult; 
    } 
}
@FeignClient (value = "DTX-notifymsg-Demo-Pay", fallback = PayFallback. Class )
 public  interface PayClient { 

    // recharge Interface Query result of the remote call refill system of 
    @GetMapping (value = "/ pay / payresult / {txNo} " )
     public AccountPay payresult (@PathVariable (" TXNO " ) String TXNO); 
}

3) monitor MQ

@Component
@Slf4j
@RocketMQMessageListener(topic = "topic_notifymsg",consumerGroup = "consumer_group_notifymsg_bank1")
public class NotifyMsgListener implements RocketMQListener<AccountPay> {

    @Autowired
    AccountInfoService accountInfoService;

    //接收消息
    @Override
    public void onMessage(AccountPay accountPay) {
        log.info("接收到消息:{}", JSON.toJSONString(accountPay));
        if("success".equals(accountPay.getResult())){
            //更新账户金额
            AccountChangeEvent accountChangeEvent = new AccountChangeEvent();
            accountChangeEvent.setAccountNo(accountPay.getAccountNo());
            accountChangeEvent.setAmount(accountPay.getPayAmount());
            accountChangeEvent.setTxNo(accountPay.getId());
            accountInfoService.updateAccountBalance(accountChangeEvent);
        }
        log.info("处理消息完成:{}", JSON.toJSONString(accountPay));
    }
}

 

 4)Controller

@RestController
@Slf4j
public class AccountInfoController {

    @Autowired
    private AccountInfoService accountInfoService;

    //主动查询充值结果
    @GetMapping(value = "/payresult/{txNo}")
    public AccountPay result(@PathVariable("txNo") String txNo){
        AccountPay accountPay = accountInfoService.queryPayResult(txNo);
        return accountPay;
    }
}

testing scenarios

2020-03-12 18:54:21.442 DEBUG 8108 --- [io-9901-exec-10] c.t.m.d.AccountPayDao.insertAccountPay   : ==>  Preparing: insert into account_pay(id,account_no,pay_amount,result) values(?,?,?,?) 
2020-03-12 18:54:21.442 DEBUG 8108 --- [io-9901-exec-10] c.t.m.d.AccountPayDao.insertAccountPay   : ==> Parameters: 4db8776f-fc55-4190-b014-7caa21b77ec0(String), 1(String), 2000.0(Double), success(String)
2020-03-12 18:54:21.455 DEBUG 8108 --- [io-9901-exec-10] c.t.m.d.AccountPayDao.insertAccountPay   : <==    Updates: 1
2020-03-12 18:54:21.464  INFO 20964 --- [MessageThread_3] c.topcheer.eq.message.NotifyMsgListener  : 接收到消息:{"accountNo":"1","id":"4db8776f-fc55-4190-b014-7caa21b77ec0","payAmount":2000.0,"result":"success"}
2020-03-12 18:54:21.466 DEBUG 20964 --- [MessageThread_3] c.t.eq.dao.AccountInfoDao.isExistTx      : ==>  Preparing: select count(1) from de_duplication where tx_no = ? 
2020-03-12 18:54:21.466 DEBUG 20964 --- [MessageThread_3] c.t.eq.dao.AccountInfoDao.isExistTx      : ==> Parameters: 4db8776f-fc55-4190-b014-7caa21b77ec0(String)
2020-03-12 18:54:21.467 DEBUG 20964 --- [MessageThread_3] c.t.eq.dao.AccountInfoDao.isExistTx      : <==      Total: 1
2020-03-12 18:54:21.468 DEBUG 20964 --- [MessageThread_3] c.t.e.d.A.updateAccountBalance           : ==>  Preparing: update account_info set account_balance=account_balance+? where account_no=? 
2020-03-12 18:54:21.468 DEBUG 20964 --- [MessageThread_3] c.t.e.d.A.updateAccountBalance           : ==> Parameters: 2000.0(Double), 1(String)
2020-03-12 18:54:21.470 DEBUG 20964 --- [MessageThread_3] c.t.e.d.A.updateAccountBalance           : <==    Updates: 1
2020-03-12 18:54:21.470 DEBUG 20964 --- [MessageThread_3] c.topcheer.eq.dao.AccountInfoDao.addTx   : ==>  Preparing: insert into de_duplication values(?,now()); 
2020-03-12 18:54:21.470 DEBUG 20964 --- [MessageThread_3] c.topcheer.eq.dao.AccountInfoDao.addTx   : ==> Parameters: 4db8776f-fc55-4190-b014-7caa21b77ec0(String)
2020-03-12 18:54:21.473 DEBUG 20964 --- [MessageThread_3] c.topcheer.eq.dao.AccountInfoDao.addTx   : <==    Updates: 1
2020-03-12 18:54:21.486  INFO 20964 --- [MessageThread_3] c.topcheer.eq.message.NotifyMsgListener  : 处理消息完成:{"accountNo":"1","id":"4db8776f-fc55-4190-b014-7caa21b77ec0","payAmount":2000.0,"result":"success"}
Published 407 original articles · won praise 2 · Views 6790

Guess you like

Origin blog.csdn.net/qq_29860591/article/details/104833043