/* * Copyright (c) 2018-2020 ActionTech. * License: http://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0 or higher. */ package com.actionsky.txle.grpc.controller; import com.actionsky.txle.grpc.*; import com.actionsky.txle.grpc.service.PrimaryCustomService; import com.actionsky.txle.grpc.service.SecondaryCustomService; import io.grpc.ManagedChannel; import io.grpc.ManagedChannelBuilder; import io.grpc.stub.StreamObserver; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.client.RestTemplate; import javax.annotation.PostConstruct; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.ObjectOutputStream; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; /** * @author Gannalyo * @since 2020/2/11 */ @RestController public class IntegrateTxleController { private static final Logger LOG = LoggerFactory.getLogger(IntegrateTxleController.class); private TxleTransactionServiceGrpc.TxleTransactionServiceStub stubService; private TxleTransactionServiceGrpc.TxleTransactionServiceBlockingStub stubBlockingService; private TxleGrpcServerStreamObserver serverStreamObserver; private StreamObserver<TxleGrpcClientStream> clientStreamObserver; private String primaryDBSchema; private String secondaryDBSchema; private Map<String, String> dbMD5InfoMap = new HashMap<>(); private int globalTxIndex = 0; @Value("${alpha.cluster.address:127.0.0.1:8080}") private String txleGrpcServerAddress; @Autowired private PrimaryCustomService primaryCustomService; @Autowired private SecondaryCustomService secondaryCustomService; @Autowired private RestTemplate restTemplate; @PostConstruct void init() { ManagedChannel channel = ManagedChannelBuilder.forTarget(txleGrpcServerAddress).usePlaintext()/*.maxInboundMessageSize(10 * 1024 * 1024)*/.build(); this.stubService = TxleTransactionServiceGrpc.newStub(channel); this.stubBlockingService = TxleTransactionServiceGrpc.newBlockingStub(channel); this.serverStreamObserver = new TxleGrpcServerStreamObserver(this); // 正式环境请设置正确的服务名称、IP和类别 TxleClientConfig clientConfig = TxleClientConfig.newBuilder().setServiceName("actiontech-dble").setServiceIP("0.0.0.0").setServiceCategory("").build(); stubService.onInitialize(clientConfig, new TxleServerConfigStreamObserver(this)); this.onInitialize(); this.initDBSchema(); new Thread(() -> this.onSynDatabaseFullDose()).start(); } private void onInitialize() { this.clientStreamObserver = stubService.onBuildBidirectionalStream(this.serverStreamObserver); serverStreamObserver.setClientStreamObserver(clientStreamObserver); } private void initDBSchema() { List list = this.primaryCustomService.executeQuery("select database()"); if (list != null && !list.isEmpty()) { this.primaryDBSchema = list.get(0).toString(); } list = this.secondaryCustomService.executeQuery("select database()"); if (list != null && !list.isEmpty()) { this.secondaryDBSchema = list.get(0).toString(); } } public String getTxleGrpcServerAddress() { return txleGrpcServerAddress; } public PrimaryCustomService getPrimaryCustomService() { return this.primaryCustomService; } public SecondaryCustomService getSecondaryCustomService() { return this.secondaryCustomService; } public String getPrimaryDBSchema() { return this.primaryDBSchema; } public String getSecondaryDBSchema() { return this.secondaryDBSchema; } public void onReconnect() { this.onInitialize(); } @GetMapping("/startTransaction/{scene}") public void onStartTransaction(@PathVariable int scene) { onStartTransaction(scene, "txle-global-transaction-id-00" + globalTxIndex++); } // 全局事务下的子事务可能分批次启动,非第一批次的需带上第一批次的全局事务标识 @PostMapping("/startTransaction/{scene}/{globalTxId}") public void onStartTransaction(@PathVariable int scene, @PathVariable String globalTxId) { TxleTransactionStart.Builder transaction = TxleTransactionStart.newBuilder().setServiceName("actiontech-dble").setServiceIP("0.0.0.0").setGlobalTxId(globalTxId).setTimeout(0); boolean isStartedTx = false; // 场景:1-正常,2-异常,3-重试且成功,4-重试不成功,5-多批次子事务(执行1后再执行5),6-超时,7-一个子事务内两条一样的操作,8-无限重试和超时,9-暂停 switch (scene) { case 1: normalSubTx(transaction); break; case 2: abnormalSubTx(transaction); break; case 3: // 该场景测试说明:在测试前需破坏网络或数据库,使数据库secondaryDBSchema的sql“update txle_sample_merchant set balance = balance + 1 where id = 1”执行失败, // 在执行结束事件前,恢复网络或数据库,即可重试成功 retrySuccessfulSubTx(transaction); break; case 4: retryFailedSubTx(transaction); break; case 5: normalSubTxForFirstTime(transaction); break; case 55: normalSubTxForSecondTime(transaction); break; case 6: timeoutAfterEndingAllSubTx(transaction); break; case 61: timeoutBeforeEndingAllSubTx001(transaction); break; case 62: timeoutBeforeEndingAllSubTx002(transaction); break; case 63: timeoutBeforeEndingAllSubTx003(transaction); break; case 7: twoSameOperationSubTx(transaction); break; case 8: retryFailedAndTimeoutSubTx(transaction); break; case 9: normalSubTx(transaction); break; default: return; } try { TxleTxStartAck startAck = stubBlockingService.onStartTransaction(transaction.build()); if (TxleTxEndAck.TransactionStatus.ABORTED.ordinal() == startAck.getStatus().ordinal()) { throw new RuntimeException("Occur an exception when starting global transaction [" + transaction.getGlobalTxId() + "]."); } else { isStartedTx = true; LOG.info("Successfully started global transaction [" + globalTxId + "]."); startAck.getSubTxSqlList().forEach(subSql -> { if (primaryDBSchema.equals(subSql.getDbSchema())) { primaryCustomService.executeSubTxSqls(subSql.getSubTxSqlList()); } else if (secondaryDBSchema.equals(subSql.getDbSchema())) { secondaryCustomService.executeSubTxSqls(subSql.getSubTxSqlList()); } }); } } catch (Exception e) { LOG.error("Occur an exception when starting global transaction [{}].", transaction.getGlobalTxId(), e); } finally { try { if (scene == 6 || scene == 62) { // 模拟超时场景 Thread.sleep(3000); } else if (scene == 9) { // 暂停20秒,此时需尽快触发暂停操作 Thread.sleep(20000); } } catch (InterruptedException e) { } // 只有事务开启成功,才执行事务结束,不用担心不会结束全局事务,因为在服务端若开启事务失败,会自动结束全局事务 if (isStartedTx) { this.onEndTransaction(scene, transaction.getGlobalTxId()); } } } private void normalSubTx(TxleTransactionStart.Builder transaction) { int currentGlobalTxIdIndex = Integer.parseInt(transaction.getGlobalTxId().substring(29)); // Map<Integer, String> sqlMap = new HashMap<>(); // sqlMap.put(1, "insert into txle_sample_user (name, balance) values ('xiongjiujiu', 1000000)"); // sqlMap.put(2, "update txle_sample_user set balance = balance - 1 where id = 2"); // sqlMap.put(3, "delete from txle_sample_user where id = 2"); List<TxleSubTransactionStart> subTxList = new ArrayList<>(); TxleSubTransactionStart subTx1 = TxleSubTransactionStart.newBuilder().setSql("update txle_sample_user set balance = balance - 1 where id = 1") .setLocalTxId("sub-tx-" + currentGlobalTxIdIndex + "-001").setDbNodeId("10.186.62.75").setDbSchema(primaryDBSchema).setTimeout(0).setRetries(0).setOrder(1).build(); // 第二个SQL故意编写异常 TxleSubTransactionStart subTx2 = TxleSubTransactionStart.newBuilder().setSql("update txle_sample_merchant set balance = balance + 1 where id = 1") .setLocalTxId("sub-tx-" + currentGlobalTxIdIndex + "-002").setDbNodeId("10.186.62.75").setDbSchema(secondaryDBSchema).setTimeout(0).setRetries(0).setOrder(2).build(); subTxList.add(subTx1); subTxList.add(subTx2); transaction.addAllSubTxInfo(subTxList); } private void abnormalSubTx(TxleTransactionStart.Builder transaction) { int currentGlobalTxIdIndex = Integer.parseInt(transaction.getGlobalTxId().substring(29)); List<TxleSubTransactionStart> subTxList = new ArrayList<>(); TxleSubTransactionStart subTx1 = TxleSubTransactionStart.newBuilder().setSql("update txle_sample_user set balance = balance - 1 where id = 1") .setLocalTxId("sub-tx-" + currentGlobalTxIdIndex + "-001").setDbNodeId("10.186.62.75").setDbSchema(primaryDBSchema).setTimeout(0).setRetries(0).setOrder(1).build(); // 第二个SQL故意编写异常 TxleSubTransactionStart subTx2 = TxleSubTransactionStart.newBuilder().setSql("update txle_sample_merchant set balance = balance + 1 where iddddddd = 1") .setLocalTxId("sub-tx-" + currentGlobalTxIdIndex + "-002").setDbNodeId("10.186.62.75").setDbSchema(secondaryDBSchema).setTimeout(0).setRetries(0).setOrder(2).build(); subTxList.add(subTx1); subTxList.add(subTx2); transaction.addAllSubTxInfo(subTxList); } private void retrySuccessfulSubTx(TxleTransactionStart.Builder transaction) { int currentGlobalTxIdIndex = Integer.parseInt(transaction.getGlobalTxId().substring(29)); List<TxleSubTransactionStart> subTxList = new ArrayList<>(); TxleSubTransactionStart subTx1 = TxleSubTransactionStart.newBuilder().setSql("update txle_sample_user set balance = balance - 1 where id = 1") .setLocalTxId("sub-tx-" + currentGlobalTxIdIndex + "-001").setDbNodeId("10.186.62.75").setDbSchema(primaryDBSchema).setTimeout(0).setRetries(3).setOrder(1).build(); // 第二个SQL故意编写异常 TxleSubTransactionStart subTx2 = TxleSubTransactionStart.newBuilder().setSql("update txle_sample_merchant set balance = balance + 1 where id = 1") .setLocalTxId("sub-tx-" + currentGlobalTxIdIndex + "-002").setDbNodeId("10.186.62.75").setDbSchema(secondaryDBSchema).setTimeout(0).setRetries(3).setOrder(2).build(); subTxList.add(subTx1); subTxList.add(subTx2); transaction.addAllSubTxInfo(subTxList); } private void retryFailedSubTx(TxleTransactionStart.Builder transaction) { int currentGlobalTxIdIndex = Integer.parseInt(transaction.getGlobalTxId().substring(29)); List<TxleSubTransactionStart> subTxList = new ArrayList<>(); TxleSubTransactionStart subTx1 = TxleSubTransactionStart.newBuilder().setSql("update txle_sample_user set balance = balance - 1 where id = 1") .setLocalTxId("sub-tx-" + currentGlobalTxIdIndex + "-001").setDbNodeId("10.186.62.75").setDbSchema(primaryDBSchema).setTimeout(0).setRetries(3).setOrder(1).build(); // 第二个SQL故意编写异常 TxleSubTransactionStart subTx2 = TxleSubTransactionStart.newBuilder().setSql("update txle_sample_merchant set balance = balance + 1 where iddddddd = 1") .setLocalTxId("sub-tx-" + currentGlobalTxIdIndex + "-002").setDbNodeId("10.186.62.75").setDbSchema(secondaryDBSchema).setTimeout(0).setRetries(3).setOrder(2).build(); subTxList.add(subTx1); subTxList.add(subTx2); transaction.addAllSubTxInfo(subTxList); } private void normalSubTxForFirstTime(TxleTransactionStart.Builder transaction) { int currentGlobalTxIdIndex = Integer.parseInt(transaction.getGlobalTxId().substring(29)); List<TxleSubTransactionStart> subTxList = new ArrayList<>(); TxleSubTransactionStart subTx1 = TxleSubTransactionStart.newBuilder().setSql("update txle_sample_user set balance = balance - 1 where id = 1") .setLocalTxId("sub-tx-" + currentGlobalTxIdIndex + "-001").setDbNodeId("10.186.62.75").setDbSchema(primaryDBSchema).setTimeout(0).setRetries(0).setOrder(3).build(); TxleSubTransactionStart subTx2 = TxleSubTransactionStart.newBuilder().setSql("update txle_sample_merchant set balance = balance + 1 where id = 1") .setLocalTxId("sub-tx-" + currentGlobalTxIdIndex + "-002").setDbNodeId("10.186.62.75").setDbSchema(secondaryDBSchema).setTimeout(0).setRetries(0).setOrder(4).build(); subTxList.add(subTx2); subTxList.add(subTx1); transaction.setTimeout(0).addAllSubTxInfo(subTxList); new Thread(() -> restTemplate.postForObject("http://127.0.0.1:8008/startTransaction/{scene}/{globalTxId}", null, String.class, 55, transaction.getGlobalTxId())).start(); } private void normalSubTxForSecondTime(TxleTransactionStart.Builder transaction) { int currentGlobalTxIdIndex = Integer.parseInt(transaction.getGlobalTxId().substring(29)); List<TxleSubTransactionStart> subTxList = new ArrayList<>(); TxleSubTransactionStart subTx1 = TxleSubTransactionStart.newBuilder().setSql("update txle_sample_user set balance = balance - 1 where id = 1") .setLocalTxId("sub-tx-" + currentGlobalTxIdIndex + "-003").setDbNodeId("10.186.62.75").setDbSchema(primaryDBSchema).setTimeout(0).setRetries(0).setOrder(3).build(); TxleSubTransactionStart subTx2 = TxleSubTransactionStart.newBuilder().setSql("update txle_sample_merchant set balance = balance + 1 where id = 1") .setLocalTxId("sub-tx-" + currentGlobalTxIdIndex + "-004").setDbNodeId("10.186.62.75").setDbSchema(secondaryDBSchema).setTimeout(0).setRetries(0).setOrder(4).build(); subTxList.add(subTx2); subTxList.add(subTx1); transaction.setTimeout(0).addAllSubTxInfo(subTxList); } private void timeoutAfterEndingAllSubTx(TxleTransactionStart.Builder transaction) { int currentGlobalTxIdIndex = Integer.parseInt(transaction.getGlobalTxId().substring(29)); List<TxleSubTransactionStart> subTxList = new ArrayList<>(); TxleSubTransactionStart subTx1 = TxleSubTransactionStart.newBuilder().setSql("update txle_sample_user set balance = balance - 1 where id = 1") .setLocalTxId("sub-tx-" + currentGlobalTxIdIndex + "-001").setDbNodeId("10.186.62.75").setDbSchema(primaryDBSchema).setTimeout(0).setRetries(0).setOrder(1).build(); // 第二个SQL故意编写异常 TxleSubTransactionStart subTx2 = TxleSubTransactionStart.newBuilder().setSql("update txle_sample_merchant set balance = balance + 1 where id = 1") .setLocalTxId("sub-tx-" + currentGlobalTxIdIndex + "-002").setDbNodeId("10.186.62.75").setDbSchema(secondaryDBSchema).setTimeout(3).setRetries(0).setOrder(2).build(); subTxList.add(subTx1); subTxList.add(subTx2); transaction.addAllSubTxInfo(subTxList); } private void timeoutBeforeEndingAllSubTx001(TxleTransactionStart.Builder transaction) { int currentGlobalTxIdIndex = Integer.parseInt(transaction.getGlobalTxId().substring(29)); List<TxleSubTransactionStart> subTxList = new ArrayList<>(); TxleSubTransactionStart subTx1 = TxleSubTransactionStart.newBuilder().setSql("update txle_sample_user set balance = balance - 1 where id = 1") .setLocalTxId("sub-tx-" + currentGlobalTxIdIndex + "-001").setDbNodeId("10.186.62.75").setDbSchema(primaryDBSchema).setTimeout(0).setRetries(0).setOrder(1).build(); subTxList.add(subTx1); transaction.addAllSubTxInfo(subTxList); } private void timeoutBeforeEndingAllSubTx002(TxleTransactionStart.Builder transaction) { int currentGlobalTxIdIndex = Integer.parseInt(transaction.getGlobalTxId().substring(29)); List<TxleSubTransactionStart> subTxList = new ArrayList<>(); TxleSubTransactionStart subTx2 = TxleSubTransactionStart.newBuilder().setSql("update txle_sample_merchant set balance = balance + 1 where id = 1") .setLocalTxId("sub-tx-" + currentGlobalTxIdIndex + "-002").setDbNodeId("10.186.62.75").setDbSchema(secondaryDBSchema).setTimeout(3).setRetries(0).setOrder(2).build(); subTxList.add(subTx2); transaction.addAllSubTxInfo(subTxList); } private void timeoutBeforeEndingAllSubTx003(TxleTransactionStart.Builder transaction) { int currentGlobalTxIdIndex = Integer.parseInt(transaction.getGlobalTxId().substring(29)); List<TxleSubTransactionStart> subTxList = new ArrayList<>(); TxleSubTransactionStart subTx2 = TxleSubTransactionStart.newBuilder().setSql("update txle_sample_merchant set balance = balance + 1 where id = 1") .setLocalTxId("sub-tx-" + currentGlobalTxIdIndex + "-003").setDbNodeId("10.186.62.75").setDbSchema(secondaryDBSchema).setTimeout(0).setRetries(0).setOrder(2).build(); subTxList.add(subTx2); transaction.addAllSubTxInfo(subTxList); } private void twoSameOperationSubTx(TxleTransactionStart.Builder transaction) { int currentGlobalTxIdIndex = Integer.parseInt(transaction.getGlobalTxId().substring(29)); List<TxleSubTransactionStart> subTxList = new ArrayList<>(); TxleSubTransactionStart subTx1 = TxleSubTransactionStart.newBuilder().setSql("update txle_sample_user set balance = balance - 1 where id = 1") .setLocalTxId("sub-tx-" + currentGlobalTxIdIndex + "-001").setDbNodeId("10.186.62.75").setDbSchema(primaryDBSchema).setTimeout(0).setRetries(0).setOrder(1).build(); // 第二个SQL故意编写异常 TxleSubTransactionStart subTx2 = TxleSubTransactionStart.newBuilder().setSql("update txle_sample_user set balance = balance - 1 where id = 1") .setLocalTxId("sub-tx-" + currentGlobalTxIdIndex + "-002").setDbNodeId("10.186.62.75").setDbSchema(primaryDBSchema).setTimeout(0).setRetries(0).setOrder(2).build(); TxleSubTransactionStart subTx3 = TxleSubTransactionStart.newBuilder().setSql("update txle_sample_merchant set balance = balance + 1 where id = 1") .setLocalTxId("sub-tx-" + currentGlobalTxIdIndex + "-003").setDbNodeId("10.186.62.75").setDbSchema(secondaryDBSchema).setTimeout(0).setRetries(0).setOrder(3).build(); TxleSubTransactionStart subTx4 = TxleSubTransactionStart.newBuilder().setSql("update txle_sample_merchant set balance = balance + 1 where id = 1") .setLocalTxId("sub-tx-" + currentGlobalTxIdIndex + "-004").setDbNodeId("10.186.62.75").setDbSchema(secondaryDBSchema).setTimeout(0).setRetries(0).setOrder(4).build(); subTxList.add(subTx1); subTxList.add(subTx2); subTxList.add(subTx3); subTxList.add(subTx4); transaction.addAllSubTxInfo(subTxList); } private void retryFailedAndTimeoutSubTx(TxleTransactionStart.Builder transaction) { int currentGlobalTxIdIndex = Integer.parseInt(transaction.getGlobalTxId().substring(29)); List<TxleSubTransactionStart> subTxList = new ArrayList<>(); TxleSubTransactionStart subTx1 = TxleSubTransactionStart.newBuilder().setSql("update txle_sample_user set balance = balance - 1 where id = 1") .setLocalTxId("sub-tx-" + currentGlobalTxIdIndex + "-001").setDbNodeId("10.186.62.75").setDbSchema(primaryDBSchema).setTimeout(0).setRetries(0).setOrder(1).build(); // 第二个SQL故意编写异常 TxleSubTransactionStart subTx2 = TxleSubTransactionStart.newBuilder().setSql("update txle_sample_merchant set balance = balance + 1 where iddddddd = 1") .setLocalTxId("sub-tx-" + currentGlobalTxIdIndex + "-002").setDbNodeId("10.186.62.75").setDbSchema(secondaryDBSchema).setTimeout(30).setRetries(-1).setOrder(2).build(); subTxList.add(subTx1); subTxList.add(subTx2); transaction.addAllSubTxInfo(subTxList); } public void onEndTransaction(@PathVariable int scene, @PathVariable String globalTxId) { try { int currentGlobalTxIdIndex = Integer.parseInt(globalTxId.substring(29)); TxleTransactionEnd.Builder transaction = TxleTransactionEnd.newBuilder().setIsCanOver(true).setGlobalTxId(globalTxId); // 场景:1-正常,2-异常,3-重试且成功,4-重试不成功,5-多批次子事务(执行1后再执行5),6-超时 switch (scene) { case 1: transaction.addSubTxInfo(TxleSubTransactionEnd.newBuilder().setLocalTxId("sub-tx-" + currentGlobalTxIdIndex + "-001").setIsSuccessful(true).build()); transaction.addSubTxInfo(TxleSubTransactionEnd.newBuilder().setLocalTxId("sub-tx-" + currentGlobalTxIdIndex + "-002").setIsSuccessful(true).build()); break; case 2: transaction.addSubTxInfo(TxleSubTransactionEnd.newBuilder().setLocalTxId("sub-tx-" + currentGlobalTxIdIndex + "-001").setIsSuccessful(true).build()); transaction.addSubTxInfo(TxleSubTransactionEnd.newBuilder().setLocalTxId("sub-tx-" + currentGlobalTxIdIndex + "-002").setIsSuccessful(false).build()); break; case 3: // 该场景测试说明:在测试前需破坏网络或数据库,使数据库secondaryDBSchema的sql“update txle_sample_merchant set balance = balance + 1 where id = 1”执行失败, // 在执行结束事件前,恢复网络或数据库,即可重试成功 transaction.addSubTxInfo(TxleSubTransactionEnd.newBuilder().setLocalTxId("sub-tx-" + currentGlobalTxIdIndex + "-001").setIsSuccessful(true).build()); transaction.addSubTxInfo(TxleSubTransactionEnd.newBuilder().setLocalTxId("sub-tx-" + currentGlobalTxIdIndex + "-002") .setIsSuccessful(false).build()); // 重试成功场景:此处特意设置成功结果为false,触发重试,但由于实际【"update txle_sample_merchant set balance = balance + 1 where id = 1"】执行成功,故需执行其反向SQL以保证数据一致性 this.secondaryCustomService.executeUpdate("update txle_sample_merchant set balance = balance - 1 where id = 1"); break; case 4: transaction.addSubTxInfo(TxleSubTransactionEnd.newBuilder().setLocalTxId("sub-tx-" + currentGlobalTxIdIndex + "-001").setIsSuccessful(true).build()); transaction.addSubTxInfo(TxleSubTransactionEnd.newBuilder().setLocalTxId("sub-tx-" + currentGlobalTxIdIndex + "-002").setIsSuccessful(false).build()); break; case 5: transaction.addSubTxInfo(TxleSubTransactionEnd.newBuilder().setLocalTxId("sub-tx-" + currentGlobalTxIdIndex + "-001").setIsSuccessful(true).build()); transaction.addSubTxInfo(TxleSubTransactionEnd.newBuilder().setLocalTxId("sub-tx-" + currentGlobalTxIdIndex + "-002").setIsSuccessful(true).build()); transaction.addSubTxInfo(TxleSubTransactionEnd.newBuilder().setLocalTxId("sub-tx-" + currentGlobalTxIdIndex + "-003").setIsSuccessful(true).build()); transaction.addSubTxInfo(TxleSubTransactionEnd.newBuilder().setLocalTxId("sub-tx-" + currentGlobalTxIdIndex + "-004").setIsSuccessful(true).build()); break; case 6: transaction.addSubTxInfo(TxleSubTransactionEnd.newBuilder().setLocalTxId("sub-tx-" + currentGlobalTxIdIndex + "-001").setIsSuccessful(true).build()); transaction.addSubTxInfo(TxleSubTransactionEnd.newBuilder().setLocalTxId("sub-tx-" + currentGlobalTxIdIndex + "-002").setIsSuccessful(true).build()); break; case 61: transaction.addSubTxInfo(TxleSubTransactionEnd.newBuilder().setLocalTxId("sub-tx-" + currentGlobalTxIdIndex + "-001").setIsSuccessful(true).build()).setIsCanOver(false); break; case 62: transaction.addSubTxInfo(TxleSubTransactionEnd.newBuilder().setLocalTxId("sub-tx-" + currentGlobalTxIdIndex + "-002").setIsSuccessful(true).build()).setIsCanOver(false); break; case 63: transaction.addSubTxInfo(TxleSubTransactionEnd.newBuilder().setLocalTxId("sub-tx-" + currentGlobalTxIdIndex + "-003").setIsSuccessful(true).build()).setIsCanOver(true); break; case 7: transaction.addSubTxInfo(TxleSubTransactionEnd.newBuilder().setLocalTxId("sub-tx-" + currentGlobalTxIdIndex + "-001").setIsSuccessful(true).build()); transaction.addSubTxInfo(TxleSubTransactionEnd.newBuilder().setLocalTxId("sub-tx-" + currentGlobalTxIdIndex + "-002").setIsSuccessful(true).build()); transaction.addSubTxInfo(TxleSubTransactionEnd.newBuilder().setLocalTxId("sub-tx-" + currentGlobalTxIdIndex + "-003").setIsSuccessful(true).build()); transaction.addSubTxInfo(TxleSubTransactionEnd.newBuilder().setLocalTxId("sub-tx-" + currentGlobalTxIdIndex + "-004").setIsSuccessful(true).build()); break; case 8: transaction.addSubTxInfo(TxleSubTransactionEnd.newBuilder().setLocalTxId("sub-tx-" + currentGlobalTxIdIndex + "-001").setIsSuccessful(true).build()); transaction.addSubTxInfo(TxleSubTransactionEnd.newBuilder().setLocalTxId("sub-tx-" + currentGlobalTxIdIndex + "-002").setIsSuccessful(false).build()); break; case 9: transaction.addSubTxInfo(TxleSubTransactionEnd.newBuilder().setLocalTxId("sub-tx-" + currentGlobalTxIdIndex + "-001").setIsSuccessful(true).build()); transaction.addSubTxInfo(TxleSubTransactionEnd.newBuilder().setLocalTxId("sub-tx-" + currentGlobalTxIdIndex + "-002").setIsSuccessful(true).build()); break; default: return; } TxleTxEndAck txEndAck = stubBlockingService.onEndTransaction(transaction.build()); if (TxleTxEndAck.TransactionStatus.ABORTED.equals(txEndAck.getStatus())) { LOG.error("Occur an exception when ending global transaction [" + globalTxId + "]."); } else { LOG.info("Successfully started global transaction [" + globalTxId + "]."); } if (scene == 61) { restTemplate.postForObject("http://127.0.0.1:8008/startTransaction/{scene}/{globalTxId}", null, String.class, 62, transaction.getGlobalTxId()); } else if (scene == 62) { restTemplate.postForObject("http://127.0.0.1:8008/startTransaction/{scene}/{globalTxId}", null, String.class, 63, transaction.getGlobalTxId()); } } catch (Exception e) { LOG.error("Occur an exception when ending global transaction [{}].", globalTxId, e); } } public void onSynDatabaseFullDose() { try { long timestamp = System.currentTimeMillis(); this.synDatabase(true, timestamp, primaryDBSchema, "txle_sample_user", "primary"); this.synDatabase(true, timestamp, secondaryDBSchema, "txle_sample_merchant", "second"); // 全量同步成功后,才启动增量同步 this.onSynDatabase(); } catch (RuntimeException e) { LOG.error("Failed to synchronize the full-dose data.", e); throw new RuntimeException(e); } } public void onSynDatabase() { while (true) { try { Thread.sleep(30000); } catch (InterruptedException e) { e.printStackTrace(); } long timestamp = System.currentTimeMillis(); this.synDatabase(false, timestamp, primaryDBSchema, "txle_sample_user", "primary"); this.synDatabase(false, timestamp, secondaryDBSchema, "txle_sample_merchant", "second"); } } private void synDatabase(boolean isFullDose, long timestamp, String dbSchema, String tableName, String dataSource) { // 模拟第三方简易程序,此处仅检测两张表,而非整库 String tempMD5Info = getMD5Digest(dbSchema, tableName), tableNameWithSchema = dbSchema + "." + tableName; String dbMD5Info = dbMD5InfoMap.get(tableNameWithSchema); if (dbMD5Info == null || !dbMD5Info.equals(tempMD5Info)) { System.err.println("数据库表" + dbSchema + "." + tableName + "表结构发生变化。。。。" + System.currentTimeMillis()); TxleBusinessDBInfo.Builder databaseSetBuilder = TxleBusinessDBInfo.newBuilder(); TxleBusinessTable.Builder table = TxleBusinessTable.newBuilder().setName(tableName); List columnList; String sql = "SELECT column_name, data_type, column_key FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = ? AND TABLE_NAME = ?"; if ("primary".equals(dataSource)) { columnList = this.primaryCustomService.executeQuery(sql, dbSchema, tableName); } else { columnList = this.secondaryCustomService.executeQuery(sql, dbSchema, tableName); } if (columnList != null && !columnList.isEmpty()) { columnList.forEach(column -> { Object[] columns = (Object[]) column; table.addField(TxleBusinessField.newBuilder().setName(columns[0].toString()).setType(columns[1].toString()).setIsPrimaryKey("PRI".equals(columns[2])).build()); }); } TxleBusinessDatabase database0 = TxleBusinessDatabase.newBuilder().setName(dbSchema).addTable(0, table.build()).build(); TxleBusinessNode node = TxleBusinessNode.newBuilder().setId("10.186.62.75").addDatabase(0, database0).build(); databaseSetBuilder.addNode(node); databaseSetBuilder.setTimestamp(timestamp); databaseSetBuilder.setIsFullDose(isFullDose); TxleBasicAck txleBasicAck = stubBlockingService.onSynDatabase(databaseSetBuilder.build()); System.err.println("syn " + dbSchema + " - txleBasicAck: received = " + txleBasicAck.getIsReceived() + ", result = " + txleBasicAck.getIsSuccessful()); // txle端同步成功才赋值,若同步不成功,则不赋值即后续还会尝试本次同步 if (txleBasicAck.getIsSuccessful()) { dbMD5InfoMap.put(tableNameWithSchema, tempMD5Info); } } } private String getMD5Digest(String dbSchema, String tableName) { try { MessageDigest md5 = MessageDigest.getInstance("MD5"); List dbInfoList = primaryCustomService.executeQuery("SELECT table_name, column_name, column_default, is_nullable, column_type, extra FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = '" + dbSchema + "' AND TABLE_NAME = '" + tableName + "'"); md5.update(convertToByteFromList(dbInfoList)); byte[] digest = md5.digest(); StringBuilder hex = new StringBuilder(); for (int i = 0; i < digest.length; i++) { int v = digest[i] & 0xFF; if (v < 16) { hex.append(0); } hex.append(Integer.toString(v, 16)); } return hex.toString(); } catch (NoSuchAlgorithmException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } throw new RuntimeException("Failed to get MD5 info for database. schema = " + dbSchema); } private byte[] convertToByteFromList(List list) throws IOException { ByteArrayOutputStream byteOut = new ByteArrayOutputStream(); ObjectOutputStream out = null; try { out = new ObjectOutputStream(byteOut); for (Object obj : list) { out.writeObject(obj); } return byteOut.toByteArray(); } finally { if (out != null) { try { out.close(); } catch (IOException e) { e.printStackTrace(); } } try { byteOut.close(); } catch (IOException e) { e.printStackTrace(); } } } }