この記事では、ATモードでのクライアントの起動プロセスをソースコードの観点から分析します。いわゆるクライアントは、ビジネスアプリケーション側です。分散トランザクションは、TC、TM、RMの3つのモジュールに分かれています。TCはseata-server側にあり、TMとRMはSDKを介してクライアント側で実行されます。
次の図は、Seataの公式デモの分散トランザクションシナリオを示しています。これは、注文、在庫の控除、および残高の控除の分散トランザクションを共同で実装する次のマイクロサービスに分割されています。
- BusinessService: ビジネスサービス、注文サービスへの入り口
- StorageService: 在庫マイクロサービス。商品在庫を差し引くために使用されます
- OrderService: マイクロサービスを注文し、注文を作成します
- AccountService: アカウントマイクロサービス、ユーザーアカウントの残高を差し引く
上の図から、ATモードでは、Seataクライアントは主に次の3つのモジュールを介して分散トランザクションを実装していることがわかります。
- GlobalTransactionScanner: GlobalTransactionScannerは、TMおよびRMモジュールを初期化し、分散トランザクションアノテーションを追加する方法にインターセプターを追加します。インターセプターは、グローバルトランザクションを開く、コミットする、またはロールバックする役割を果たします。
- DatasourceProxy: DatasourceProxyはDataSourceにインターセプトを追加します。インターセプターはすべてのSQL実行をインターセプトし、RMトランザクションの参加者として分散トランザクションの実行に参加します。
- Rpc Interceptor:分散トランザクションSeataソースコードの解釈に関する 前回の記事で、分散トランザクションのいくつかのコアポイントについて説明しました。そのうちの1つは、分散トランザクションのクロスサービスインスタンス伝播です。Rpc Interceptorの責任は、トランザクションを複数のマイクロサービスに分散させることです。
Seata-Spring-Boot-starter
Seata-allまたはseata-spring-boot-starterに依存して、seata分散トランザクションSDKを参照する方法は2つあります。スターターは上記の3つのモジュールを自動的に挿入するため、seata-spring-boot-starterを使用することをお勧めします。対応する構成を追加し、グローバル分散トランザクション注釈をビジネスコードに追加するだけです。Seata-spring-boot-starterプロジェクトのコードから始めましょう。
次の図は、seata-spring-boot-starterのプロジェクト構造を示しています。
主に次のモジュールに分かれています。
- プロパティ: プロパティディレクトリは、Springbootがseataに適応するすべての関連構成クラスです。つまり、Seata関連パラメーターにはSpringBoot構成を介してアクセスできます。
- プロバイダー: プロバイダーディレクトリ内のクラスは、SpringbootおよびSpringCloudの構成をSeata構成に適合させる役割を果たします。
- リソース: resourcesディレクトリには2つのメインファイルがあります。spring.factoriesはSpringboot自動アセンブリクラスの登録に使用され、ExtConfigurationProviderはSpringbootConfigurationProviderクラスの登録に使用されます。Providerクラスは、SpringBoot関連の構成クラスをSeataに適合させる役割を果たします。
springboot-starterプロジェクトの場合、最初にresources / META-INF /spring.factoriesファイルを確認します。
# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=
io.seata.spring.boot.autoconfigure.SeataAutoConfiguration
あなたは、自動組立クラスがspring.factoriesに設定されていることを見ることができます:SeataAutoConfiguration。2つのインスタンスのGlobalTransactionScannerとseataAutoDataSourceProxyCreatorは主に組み立てクラスに注入されています。コードは次のように表示されます。
@ComponentScan(basePackages = "io.seata.spring.boot.autoconfigure.properties")
@ConditionalOnProperty(prefix = StarterConstants.SEATA_PREFIX, name = "enabled",
havingValue = "true",
matchIfMissing = true)
@Configuration
@EnableConfigurationProperties({SeataProperties.class})
public class SeataAutoConfiguration {
...
// GlobalTransactionScanner负责为添加GlobalTransaction注解的方法添加拦截器,
// 并且负责初始化RM、TM
@Bean
@DependsOn({BEAN_NAME_SPRING_APPLICATION_CONTEXT_PROVIDER, BEAN_NAME_FAILURE_HANDLER})
@ConditionalOnMissingBean(GlobalTransactionScanner.class)
public GlobalTransactionScanner globalTransactionScanner(SeataProperties seataProperties,
FailureHandler failureHandler) {
if (LOGGER.isInfoEnabled()) {
LOGGER.info("Automatically configure Seata");
}
return new GlobalTransactionScanner(seataProperties.getApplicationId(),
seataProperties.getTxServiceGroup(),
failureHandler);
}
// SeataAutoDataSourceProxyCreator负责为Spring中的所有DataSource生成代理对象,
// 从而实现拦截所有SQL的执行
@Bean(BEAN_NAME_SEATA_AUTO_DATA_SOURCE_PROXY_CREATOR)
@ConditionalOnProperty(prefix = StarterConstants.SEATA_PREFIX, name = {
"enableAutoDataSourceProxy", "enable-auto" +
"-data-source-proxy"}, havingValue = "true", matchIfMissing = true)
@ConditionalOnMissingBean(SeataAutoDataSourceProxyCreator.class)
public SeataAutoDataSourceProxyCreator seataAutoDataSourceProxyCreator(SeataProperties seataProperties) {
return new SeataAutoDataSourceProxyCreator(seataProperties.isUseJdkProxy(),
seataProperties.getExcludesForAutoProxying());
}
}
GlobalTransactionScanner
GlobalTransactionScannerはAutoProxyCreatorを継承します。AutoProxyCreatorはSpringでAOPを実装する方法です。これは、Springのすべてのインスタンスをインターセプトし、プロキシが必要かどうかを判断できます。以下に、GlobalTransactionScannerのより重要なフィールドのいくつかと、エージェントをインターセプトするコアメソッドを示します。
public class GlobalTransactionScanner extends AbstractAutoProxyCreator
implements InitializingBean, ApplicationContextAware,
DisposableBean {
...
// interceptor字段是对应一个代理对象的拦截器,
// 可以认为是一个临时变量,有效期是一个被代理对象
private MethodInterceptor interceptor;
// globalTransactionalInterceptor是通用的Interceptor,
// 非TCC事务方式的都使用该Interceptor
private MethodInterceptor globalTransactionalInterceptor;
// PROXYED_SET存储已经代理过的实例,防止重复处理
private static final Set<String> PROXYED_SET = new HashSet<>();
// applicationId是一个服务的唯一标识,
// 对应springcloud项目中的spring.application.name
private final String applicationId;
// 事务的分组标识,参考文章wiki:http://seata.io/zh-cn/docs/user/transaction-group.html
private final String txServiceGroup;
...
// 判断是否需要代理目标对象,如果需要代理,则生成拦截器赋值到类变量interceptor中
@Override
protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
// 判断是否禁用分布式事务
if (disableGlobalTransaction) {
return bean;
}
try {
synchronized (PROXYED_SET) {
if (PROXYED_SET.contains(beanName)) {
return bean;
}
// 每次处理一个被代理对象时先把interceptor置为null,所以interceptor的
// 生命周期是一个被代理对象,由于是在另外一个方法getAdvicesAndAdvisorsForBean
// 中使用interceptor,所以该interceptor要定义为一个类变量
interceptor = null;
// 判断是否是TCC事务模式,判断的主要依据是方法上是否有TwoPhaseBusinessAction注解
if (TCCBeanParserUtils.isTccAutoProxy(bean, beanName,
applicationContext)) {
// 创建一个TCC事务的拦截器
interceptor =
new TccActionInterceptor(TCCBeanParserUtils.getRemotingDesc(beanName));
} else {
// 获取待处理对象的class类型
Class<?> serviceInterface = SpringProxyUtils.findTargetClass(bean);
// 获取待处理对象继承的所有接口
Class<?>[] interfacesIfJdk = SpringProxyUtils.findInterfaces(bean);
// 如果待处理对象的class或者继承的接口上有GlobalTransactional注解,
// 或者待处理对象的class的任一个方法上有GlobalTransactional或者
// GlobalLock注解则返回true,即需要被代理
if (!existsAnnotation(new Class[]{serviceInterface})
&& !existsAnnotation(interfacesIfJdk)) {
return bean;
}
// 如果interceptor为null,即不是TCC模式,
// 则使用globalTransactionalInterceptor作为拦截器
if (interceptor == null) {
// globalTransactionalInterceptor只会被创建一次
if (globalTransactionalInterceptor == null) {
globalTransactionalInterceptor =
new GlobalTransactionalInterceptor(failureHandlerHook);
ConfigurationCache.addConfigListener(
ConfigurationKeys.DISABLE_GLOBAL_TRANSACTION,
(ConfigurationChangeListener) globalTransactionalInterceptor);
}
interceptor = globalTransactionalInterceptor;
}
}
if (!AopUtils.isAopProxy(bean)) {
// 如果bean本身不是Proxy对象,则直接调用父类的wrapIfNecessary生成代理对象即可
// 在父类中会调用getAdvicesAndAdvisorsForBean获取到上面定义的interceptor
bean = super.wrapIfNecessary(bean, beanName, cacheKey);
} else {
// 如果该bean已经是代理对象了,则直接在代理对象的拦截调用链AdvisedSupport
// 上直接添加新的interceptor即可。
AdvisedSupport advised = SpringProxyUtils.getAdvisedSupport(bean);
Advisor[] advisor = buildAdvisors(beanName,
getAdvicesAndAdvisorsForBean(null, null, null));
for (Advisor avr : advisor) {
advised.addAdvisor(0, avr);
}
}
// 标识该beanName已经处理过了
PROXYED_SET.add(beanName);
return bean;
}
} catch (Exception exx) {
throw new RuntimeException(exx);
}
}
// 返回wrapIfNecessary方法中计算出的interceptor对象
@Override
protected Object[] getAdvicesAndAdvisorsForBean(Class beanClass, String beanName,
TargetSource customTargetSource)
throws BeansException {
return new Object[]{interceptor};
}
}
上記の紹介どのようにアノテーションを通じGlobalTransactionScannerインターセプトグローバルトランザクション。特定のインターセプタがTccActionInterceptorとGlobalTransactionalInterceptorとして実装されています。のためにATモード、我々は主に気にGlobalTransactionalInterceptor。その後の記事では、我々はGlobalTransactionalInterceptorの特定の実装を紹介します。
さらに、GloabalTransactionScannerは、initClientメソッドに実装されているTMおよびRMの初期化も担当します。
private void initClient() {
...
//初始化TM
TMClient.init(applicationId, txServiceGroup);
...
//初始化RM
RMClient.init(applicationId, txServiceGroup);
...
// 注册Spring shutdown的回调,用来释放资源
registerSpringShutdownHook();
}
TMClientとRMClientは、どちらもNettyに基づいてSeataによって実装されたRpcフレームワークのクライアントクラスですが、ビジネスロジックは異なります。TMClientは比較的単純なので、RMClientを例としてソースコードを見てみましょう。
public class RMClient {
// RMClient的init是一个static方法,创建了一个RmNettyRemotingClient实例,并调用init方法
public static void init(String applicationId, String transactionServiceGroup) {
RmNettyRemotingClient rmNettyRemotingClient =
RmNettyRemotingClient.getInstance(applicationId, transactionServiceGroup);
rmNettyRemotingClient.setResourceManager(DefaultResourceManager.get());
rmNettyRemotingClient.setTransactionMessageHandler(DefaultRMHandler.get());
rmNettyRemotingClient.init();
}
}
RmNettyRemotingClientの実装は次のとおりです。
@Sharable
public final class RmNettyRemotingClient extends AbstractNettyRemotingClient {
// ResourceManager负责处理事务参与方,支持AT、TCC、Saga三种模式
private ResourceManager resourceManager;
// RmNettyRemotingClient单例
private static volatile RmNettyRemotingClient instance;
private final AtomicBoolean initialized = new AtomicBoolean(false);
// 微服务的唯一标识
private String applicationId;
// 分布式事务分组名称
private String transactionServiceGroup;
// RMClient中init方法会调用该init方法
public void init() {
// 注册Seata自定义Rpc的Processor
registerProcessor();
if (initialized.compareAndSet(false, true)) {
// 调用父类的init方法,在父类中负责Netty的初始化,与Seata-Server建立连接
super.init();
}
}
// 注册Seata自定义Rpc的Processor
private void registerProcessor() {
// 1.注册Seata-Server发起branchCommit的处理Processor
RmBranchCommitProcessor rmBranchCommitProcessor =
new RmBranchCommitProcessor(getTransactionMessageHandler(), this);
super.registerProcessor(MessageType.TYPE_BRANCH_COMMIT, rmBranchCommitProcessor,
messageExecutor);
// 2.注册Seata-Server发起branchRollback的处理Processor
RmBranchRollbackProcessor rmBranchRollbackProcessor =
new RmBranchRollbackProcessor(getTransactionMessageHandler(), this);
super.registerProcessor(MessageType.TYPE_BRANCH_ROLLBACK, rmBranchRollbackProcessor
, messageExecutor);
// 3.注册Seata-Server发起删除undoLog的处理Processor
RmUndoLogProcessor rmUndoLogProcessor =
new RmUndoLogProcessor(getTransactionMessageHandler());
super.registerProcessor(MessageType.TYPE_RM_DELETE_UNDOLOG, rmUndoLogProcessor,
messageExecutor);
// 4.注册Seata-Server返回Response的处理Processor,ClientOnResponseProcessor
// 用于处理由Client主动发起Request,Seata-Server返回的Response。
// ClientOnResponseProcessor负责把Client发送的Request和Seata-Server
// 返回的Response对应起来,从而实现Rpc
ClientOnResponseProcessor onResponseProcessor =
new ClientOnResponseProcessor(mergeMsgMap, super.getFutures(),
getTransactionMessageHandler());
super.registerProcessor(MessageType.TYPE_SEATA_MERGE_RESULT, onResponseProcessor,
null);
super.registerProcessor(MessageType.TYPE_BRANCH_REGISTER_RESULT,
onResponseProcessor, null);
super.registerProcessor(MessageType.TYPE_BRANCH_STATUS_REPORT_RESULT,
onResponseProcessor, null);
super.registerProcessor(MessageType.TYPE_GLOBAL_LOCK_QUERY_RESULT,
onResponseProcessor, null);
super.registerProcessor(MessageType.TYPE_REG_RM_RESULT, onResponseProcessor, null);
// 5. 处理Seata-Server返回的Pong消息
ClientHeartbeatProcessor clientHeartbeatProcessor = new ClientHeartbeatProcessor();
super.registerProcessor(MessageType.TYPE_HEARTBEAT_MSG, clientHeartbeatProcessor,
null);
}
}
上記のロジックはより複雑に見え、さまざまなプロセッサ、さまざまなMessageType、TransactionMessageHandler、ResourceManagerなどの多くの関連クラスがあります。実際、これは本質的にRpc呼び出しであり、Rmアクティブ呼び出しとSeataアクティブ呼び出しに分けられます。
- Rmは 、ブランチの登録、ブランチステータスのレポート、グローバルロックの申請などのメソッドをアクティブに呼び出します。Rmがアクティブに呼び出すメソッドは、Seata-Serverから返された応答をClientOnResponseProcessorで処理する必要があります。
- Seata-Serverは 、ブランチトランザクションのコミット、ブランチトランザクションのロールバック、取り消しログログの削除などのメソッドをアクティブに呼び出します。Seata-Serverはアクティブにメソッドを呼び出し、クライアント側は処理するさまざまなプロセッサに対応し、処理が完了すると、Seata-Serverの処理結果の応答に戻ります。トランザクションのコミットとロールバックのコア実装ロジックは、TransactionMessageHandlerとResourceManagerにあります。
TransactionMessageHandlerとResourceManagerの特定の実装についても、以降の章で詳しく説明します。
次の記事では、SeataAutoDataSourceProxyCreatorとRpcInterceptorを初期化してインターセプトする方法を紹介します。
オリジナル:https://seata.io/zh-cn/blog/seata-sourcecode-client-bootstrap.html
●なぜアリババは90秒で100億に抵抗できるのですか?-サーバー側の高同時分散アーキテクチャの進化
● B2Beコマースプラットフォーム--ChinaPayUnionPay電子決済機能
● Zookeeperの分散ロックを学び、インタビュアーに感心してあなたを見てもらいましょう
● SpringCloudeコマーススパイクマイクロサービス-Redisson分散ロックソリューション
もっと良い記事をチェックして、公式アカウントを入力してください-私にお願いします-過去に素晴らしい
深くソウルフルなパブリックアカウント0.0