分散トランザクションの原則+プロジェクトの実際の戦闘に対するLCNのソリューションの分析

序文

SpringCloud分散アーキテクチャは、開発の利便性をもたらすと同時に、トランザクション管理の難しさを増します。マイクロサービスが至る所で開花し、ローカルトランザクションが分散の要件を満たすことができなくなったため、分散トランザクションの問題が発生しました。分散トランザクションは世界的な問題と呼ばれています。
  分散トランザクションの詳細については、次の記事を参照してください:誰かが分散トランザクションについて質問し、これを彼に投げます。
  この記事は、分散トランザクションを管理するためのTX-LCN分散トランザクションフレームワークの統合を記録します。使用されるバージョンは5.0.2です。リリース分散トランザクションの背景を
理解
する

分散型が登場する前は、インターネット企業のプロジェクトは従来のモノリシックプロジェクトであり、プロジェクト全体がデータソースを共有していたため、データベースでビジネスを実行した場合、このようなトランザクションの不整合の問題は同じであったため発生しませんでした。 1つのデータソースに対して1つのローカルトランザクション。

しかし、モノリシックアーキテクチャから分散アーキテクチャ、SOAアーキテクチャプロジェクト、今日の会社で採用されているマイクロサービスアーキテクチャへのアーキテクチャの進化に伴い、サービスのビジネスを分割し、データソースも分割しました。一般的にはマイクロサービスプロジェクトです。データソースに対応するサービスです。

次に、このアーキテクチャでは、各サービスに独自の独立したデータソースと独自のローカルトランザクションがあり、分散トランザクションが生成されるため、サービス間でデータの不整合が発生する可能性があります。
さまざまなアーキテクチャシステムでのトランザクションの問題を解決する
モノリシックアーキテクチャ(単一のデータソース)

単一のプロジェクトでは、トランザクション管理のために複数の異なるビジネスロジックが同じデータソースに実装されます。トランザクションマネージャーは同じデータソースの場合に使用されるため、分散トランザクションの問題はありません。これは、各1つのトランザクションに相当します。 managerは1つのデータソースに対応します
モノリシックアーキテクチャ(複数のデータソース)

1つのプロジェクトには、複数の異なるデータソースがあり、各データソースには相互に影響を与えない独自の独立したトランザクションマネージャーがあります。この時点で、複数のデータソースのトランザクション管理もあります:solution jta + Atomikos。
分散/マイクロサービスアーキテクチャ

分散/マイクロサービスアーキテクチャでは、各サービスに独自の独立したデータソースとトランザクションマネージャーがあります。この場合、RPCリモート呼び出しを行う必要があるビジネスがあると、必然的に分散トランザクションが生成されます。現在、主なソリューションは、MQ、LCN、Seataおよびその他のソリューションです。
LCNの紹介と背景

LCN分散トランザクションフレームワーク自体はトランザクションを作成しませんが、トランザクションの一貫性の効果を達成するためのローカルトランザクションの調整に基づいています。
LCNフレームワークの最初のバージョンは2017年6月にリリースされました。1.0の初めから、バージョン5.0に進化しました。

LCN名は、以前のバージョンのLCNフレームワークにちなんで名付けられています。フレームワークの設計開始時の1.0〜2.0バージョンでは、フレームワーク設計の手順は次のとおりです。それぞれ、イニシャルから派生したLCN名を使用します。

フレームワークは5.0以降のLCN、TCC、TXCの3つのトランザクションモードと互換性があるため、LCNモードの区別を避けるために、LCN分散トランザクションはTX-LCN分散トランザクションフレームワークに名前が変更されました。
TX-LCN分散トランザクションフレームワーク、LCNはトランザクションを生成しません、LCNはローカルトランザクションの単なるコーディネーターです、LCNは高性能分散トランザクションフレームワークであり、dubboおよびspringcloudフレームワークと互換性があり、RPCフレームワーク拡張をサポートし、さまざまなORMフレームワークをサポートします。 NoSQL、負荷分散、トランザクション補償の
ポジショニング

TX-LCNは、トランザクション調整フレームワークとして位置付けられています。フレームワーク自体はトランザクションを操作しませんが、トランザクションの一貫性の効果を実現するためのトランザクションの調整に基づいています。(LCNは事件を生み出さず、単なる事件のポーターです...これは農夫山泉のコピーライティングに少し似てい
ますLCNモード

LCNモードは、プロキシ接続を介してローカルトランザクションの操作を実現し、TxManagerによってトランザクションを調整および制御することです。ローカルトランザクションがコミットして接続をロールバックまたは閉じると、偽の操作が実行され、エージェントの接続はLCN接続プールによって管理されます。
このモードの機能:

该模式对代码的嵌入性为低。
该模式仅限于本地存在连接对象且可通过连接对象控制事务的模块。
该模式下的事务提交与回滚是由本地事务方控制,对于数据一致性上有较高的保障。
该模式缺陷在于代理的连接需要随事务发起方一共释放连接,增加了连接占用的时间。

TCCモード

従来のトランザクションメカニズム(X / Open XA 2フェーズコミット)と比較すると、TCCトランザクションメカニズムは、XAのリソースマネージャー(RM)のサポートに依存せず、ビジネスロジック(提供される)を介するという特徴があります。ビジネスシステムによる)分散トランザクションを実現するためのスケジューリング。主に3つのステップで構成されます。試行:ビジネスの実行を試行します。確認:ビジネスの実行を確認します。キャンセル:ビジネスの実行をキャンセルします。

このモデルの特徴:

该模式对代码的嵌入性高,要求每个业务需要写三种步骤的操作。
该模式对有无本地事务控制都可以支持使用面广。
数据一致性控制几乎完全由开发者控制,对业务开发难度要求高。

TXCモード

TXCモードは、淘宝網にちなんで名付けられています。実装の原則は、SQLを実行する前にSQLの影響データをクエリし、実行されたSQLクイックウォーク情報を保存してロックを作成することです。ロールバックが必要な場合、これらの記録されたデータはデータベースをロールバックするために使用されます。現在のロックの実装は、redis分散ロック制御に依存しています。
このモデルの特徴:

该模式同样对代码的嵌入性低。
该模式仅限于对支持SQL方式的模块支持。
该模式由于每次执行SQL之前需要先查询影响数据,因此相比LCN模式消耗资源与时间要多。
该模式不会占用数据库的连接资源。

LCN分散トランザクションの原則(自分で理解し、言葉で理解しやすい)

1)まず、lcnコーディネーター(TM)は、導入されたnettyを介してlcnクライアント(TC)との長い接続(継続的な監視)を維持します。

2)要求側(呼び出し元)がインターフェースサービスに入る前に、AOPテクノロジーを介して@LcnTransactionアノテーションを入力し、LCNコーディネーター側でグローバルトランザクショングループID(groupId)を生成して登録します。

3)イニシエーター(呼び出し元)がrpcを介して参加者(呼び出し先)を呼び出すと、lcnはFeignクライアントを書き換え、ThreadLocalからトランザクショングループID(groupId)を取得し、トランザクショングループIDセットを要求ヘッダーに転送します。

4)参加者(呼び出し先)がリクエストヘッダーでgroupIdを取得すると、lcnはサービスを参加者として識別し、トランザクショングループに参加し、データソースとしてlcnで表されます。その後、サービスビジネスロジックが完了すると、データソースの誤ったクローズは、現在のサービストランザクションを実際にコミットまたはロールバックしません。

5)イニシエーターがすべてのビジネスロジックを実行すると、例外がない場合、lcnコーディネーターに通知され、lcnコーディネーターは、要求チェーンのすべての参加者に送信できることを通知し、実際の送信が実行されます。アウト。イニシエーターが参加者を呼び出した後にエラーを報告すると、lcnコーディネーターにも通知され、lcnコーディネーターはすべての参加者に実際のロールバック操作を実行するよう通知します。これにより、分散トランザクションの問題が解決されます。
コードをすばやくコーディングして
txビルドします-manager
データベース、テーブルを作成します

创建MySQL数据库, 名称为:tx-manager(我们直接选择在我们自己的数据库下面创建表就行了,这里就不创建这个数据库)
创建数据表:t_tx_exception

CREATE TABLE `t_tx_exception`  (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `group_id` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
  `unit_id` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
  `mod_id` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
  `transaction_state` tinyint(4) NULL DEFAULT NULL,
  `registrar` tinyint(4) NULL DEFAULT NULL,
  `remark` varchar(4096) NULL DEFAULT  NULL,
  `ex_state` tinyint(4) NULL DEFAULT NULL COMMENT '0 未解决 1已解决',
  `create_time` datetime NULL DEFAULT NULL,
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic;

https://img2018.cnblogs.com/blog/1353055/201906/1353055-20190620151602501-133032900.png
ソースコードをダウンロードしてコンパイルします

ソースコードのダウンロードアドレス:https://github.com/codingapi/tx-lcn
プロジェクトディレクトリ

txlcn-tmの構成ファイルapplication.propertiesを変更します

####################### 服务 ############################################
 
spring.application.name=TransactionManager
server.port=7970
 
####################### 数据库 ############################################
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://47.92.145.192:3306/scm_transaction?useUnicode=true&characterEncoding=utf-8&allowMultiQueries=true&useSSL=false
spring.datasource.username=db_user2
spring.datasource.password=db_pass
# 验证连接是否有效。此参数必须设置为非空字符串,下面三项设置成true才能生
spring.datasource.validationQuery=SELECT 1
# 指明连接是否被空闲连接回收器(如果有)进行检验.如果检测失败,则连接将被从池中去除.
spring.datasource.testWhileIdle=true
# 指明是否在从池中取出连接前进行检验,如果检验失败,则从池中去除连接并尝试取出另一个
spring.datasource.testOnBorrow=true
# 指明是否在归还到池中前进行检验
spring.datasource.testOnReturn=false
 
# 以下可省略
# 初始化大小,最小,最大
spring.datasource.initialSize=5
spring.datasource.minIdle=10
spring.datasource.maxActive=1000
# 配置获取连接等待超时的时间
spring.datasource.maxWait=60000
#配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
spring.datasource.timeBetweenEvictionRunsMillis=60000
#配置一个连接在池中最小生存的时间,单位是毫秒
spring.datasource.minEvictableIdleTimeMillis=300000
#打开PSCache,并且指定每个连接上PSCache的大小
spring.datasource.poolPreparedStatements=true
spring.datasource.maxPoolPreparedStatementPerConnectionSize=20
#配置监控统计拦截的filters,去掉后监控界面sql无法统计,'wall'用于防火墙
spring.datasource.filters=stat,wall,log4j
#通过connectProperties属性来打开mergeSql功能;慢SQL记录
spring.datasource.connectionProperties=druid.stat.mergeSql=true;druid.stat.slowSqlMillis=1000;druid.stat.logSlowSql=true
#合并多个DruidDataSource的监控数据
spring.datasource.useGlobalDataSourceStat=true
#spring.datasource.WebStatFilter.exclusions="*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*"
#spring.datasource.stat-view-servlet.login-username=admin
#spring.datasource.stat-view-servlet.login-password=admin
 
####################### 数据库方言 ############################################
spring.jpa.database-platform=org.hibernate.dialect.MySQL5InnoDBDialect
# 第一次运行可以设置为: create, 为TM创建持久化数据库表
spring.jpa.hibernate.ddl-auto=update
 
####################### Redis ############################################
spring.redis.host=47.92.145.192
spring.redis.port=6379
spring.redis.password=WZTH@dev123
 
####################### 事务 ############################################
# TM监听IP. 默认为 127.0.0.1
tx-lcn.manager.host=127.0.0.1
# TM监听Socket端口. 默认为 ${server.port} - 100
tx-lcn.manager.port=8070
# 心跳检测时间(ms). 默认为 300000
tx-lcn.manager.heart-time=300000
# 分布式事务执行总时间(ms). 默认为36000
tx-lcn.manager.dtx-time=8000
# 参数延迟删除时间单位ms  默认为dtx-time值
tx-lcn.message.netty.attr-delay-time=${tx-lcn.manager.dtx-time}
# 事务处理并发等级. 默认为机器逻辑核心数5倍
tx-lcn.manager.concurrent-level=160
# TM后台登陆密码,默认值为codingapi
tx-lcn.manager.admin-key=123456
# 分布式事务锁超时时间 默认为-1,当-1时会用tx-lcn.manager.dtx-time的时间
tx-lcn.manager.dtx-lock-time=${tx-lcn.manager.dtx-time}
# 雪花算法的sequence位长度,默认为12位.
tx-lcn.manager.seq-len=12
# 异常回调开关。开启时请制定ex-url
tx-lcn.manager.ex-url-enabled=false
# 事务异常通知(任何http协议地址。未指定协议时,为TM提供内置功能接口)。默认是邮件通知
tx-lcn.manager.ex-url=/provider/email-to/[email protected]

注:実際の状況に応じて、データベース名、ユーザー名、およびパスワードを変更しまし
た。txlcn-tmモジュールを起動します。

起動方法がわからない場合は、Springbootの動作モード
を自分で確認できます。起動後、バックグラウンドアドレスhttp:// localhost:7970を開き、初期パスワードはcodingapiです。ここで123456に変更しました。

ログイン後に
Tx-Clientを設定する

TCターミナルは、ステップバイステップの操作について公式Webサイトを参照します。https://www.txlcn.org/zh-cn/docs/start.htmlTC
は依存関係を導入します

<dependency>
            <groupId>com.codingapi.txlcn</groupId>
            <artifactId>txlcn-tc</artifactId>
            <version>5.0.2.RELEASE</version>
        </dependency>
 
        <dependency>
            <groupId>com.codingapi.txlcn</groupId>
            <artifactId>txlcn-txmsg-netty</artifactId>
            <version>5.0.2.RELEASE</version>
        </dependency>

PS:jdbcドライバーを追加しない場合、起動時にエラーが報告されます

com.codingapi.txlcn.tc.core.transaction.txc.analy.TableStructAnalyserのコンストラクターのパラメーター0には、タイプ 'javax.sql.DataSource'のBeanが必要でしたが見つかりませんでした。

したがって、jdbcの依存関係を追加します

<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-jdbc</artifactId>
        </dependency>

TMアドレスとリスニングポートを構成ファイルに追加します。TMがデフォルトのポート8070であり、TCと同じマシンに展開されている場合は、この構成を無視してログを有効にすることができます。開発フェーズでログを有効にすることをお勧めします。トラブルシューティングの追跡を容易にするために、デバッグレベルに設定します

# 是否启动LCN负载均衡策略(优化选项,开启与否,功能不受影响)
tx-lcn.ribbon.loadbalancer.dtx.enabled=true
# 默认之配置为TM的本机默认端口
tx-lcn.client.manager-address=127.0.0.1:8070
# 开启日志,默认为false
tx-lcn.logger.enabled=true
tx-lcn.logger.driver-class-name=${spring.datasource.driver-class-name}
tx-lcn.logger.jdbc-url=${spring.datasource.url}
tx-lcn.logger.username=${spring.datasource.username}
tx-lcn.logger.password=${spring.datasource.password}
logging.level.com.codingapi.txlcn=DEBUG

スタートアップクラスで@EnableDistributedTransactionを使用します

//省略其他代码...
@EnableDistributedTransaction
public class MyspringbootApplication {
    public static void main(String[] args) {
        SpringApplication.run(MyspringbootApplication.class, args);
    }
}

ローカルトランザクションが送信される場所に@LcnTransactionを追加します。分散トランザクションアノテーション、PS:@LcnTransactionのターゲットはメソッド上にあり、@ Target({ElementType.METHOD})は
デモを示します

以前の2つのプロジェクトmyspringbootとspringdatejpaを選択し、手順に従ってそれらをTCに設定し
、テストインターフェイス
myspringboot-controllerを2つのTCに追加します。

/**
     * 测试分布式事务
     */
    @GetMapping("feign/save")
    Result<UserVo> save(UserVo userVo){
        //模拟数据
        Description description = new Description();
        description.setUserId("111");
        description.setDescription("测试用户描述");
 
        Result<Description> save = descriptionService.save(description);
        System.out.println(save);
        return null;
    }

myspringboot-service

@Override
    @LcnTransaction//分布式事务
    @Transactional //本地事务
    public Result<Description> save(Description description) {
        UserVo userVo = new UserVo();
        userVo.setUsername("huanzi");
        userVo.setPassword("123");
        //调用springdatejpa服务保存userVo
        Result<UserVo>  result = myspringbootFeign.save(userVo);
        System.out.println(result);
 
        //myspringboot本地服务保存description
        Description save = descriptionRepository.save(description);
        System.out.println(save);
        
        //模拟发生异常
        throw new RuntimeException("business code error");
    }

myspringboot-偽物

@FeignClient(name = "springdatejpa", path = "/user/",fallback = MyspringbootFeignFallback.class,fallbackFactory = MyspringbootFeignFallbackFactory.class)
public interface MyspringbootFeign {
 
    @RequestMapping(value = "save")
    Result<UserVo> save(@RequestBody UserVo userVo);
}

springdatejpa

これには元々対応する保存インターフェースがあり、他のコードを投稿せず、UserServiceImplクラスの保存メソッドを書き直し、@ LcnTransactionアノテーションを保存メソッドに追加します

@LcnTransaction//分布式事务
    @Transactional //本地事务
    @Override
    public Result<UserVo> save(UserVo entity) {
        User user = userRepository.save(FastCopy.copy(entity, User.class));
        return Result.of(FastCopy.copy(user, UserVo.class));
    }

デモンストレーション効果

すべてのプロジェクトを開始し、TMおよびRedisサービスを開始することを忘れないでください

TMの背景を確認すると、2つのTCが正常に登録されていることがわかります。

http:// localhost:10010 / myspringboot / feign / saveにアクセスすると、シングルサインオンによってインターセプトされ、ログイン後に通常どおりインターフェイスにジャンプします。これらはもう表示されません。バックグラウンドのデバッグログを直接見てみましょう。
プロセス
myspringbootの呼び出し(A)—> springdatejpa(B)

トランザクションロールバック
myspringboot(A)

springdatejpa(B)

この時点で、springdatejpa(B)はmyspringboot(A)へのユーザーデータに応答し、ロールバック通知を受信しました

トランザクションの
送信トランザクションが通常どのように送信されるかを見てみましょう。シミュレートされた例外に注釈を付け、保存されたデータを返します。

//模拟发生异常
        //throw new RuntimeException("business code error");
        return Result.of(save);

springdatejpa(B)からのデータに応答した直後にmyspringboot(A)を確認します

springdatejpa(B)の
追記

Springbootバージョンがtxlcnバージョンと互換性があるかどうかに注意を払うには、公式Webサイト(https://www.txlcn.org/zh-cn/docs/start.html)のクイックスタートに従い、公式の例を参照してください。 (https:// github.com/codingapi/txlcn-demo)、途中でいくつかの小さな問題が発生しました。要約は次のとおりです。

1. AがBを調整し、Aが例外をスローし、Aトランザクションがロールバックされ、Bトランザクションがロールバックされない

理由:これは、最初にAのコントローラーレイヤーでBを呼び出したためです。これは、Bが別個のトランザクショングループであり、Aが別個のトランザクショングループであることに相当します。

解決策:Aがトランザクションを開いた後にBに電話する
——————————————
著作権表示:この記事は、CC 4.0BY-SA著作権契約に従ったCSDNブロガー「TwilightdeBaixueyan」の元の記事です。 、転載のために元のソースリンクとこのステートメントを添付してください。
元のリンク:https://blog.csdn.net/lucky_love816/article/details/108396683

おすすめ

転載: blog.csdn.net/qq_34117294/article/details/114986818