seataを使用して分散トランザクションを解決する


Seata(Fescar)を使用して分散トランザクションを解決し、seataの使用方法
seataはアリオープンソースの分散トランザクションを解決するフレームワークで、元の名前はFescarで、後にseataと名前を変更しました.私たちがよく使うタオバオのように、seataを使って解決した分散トランザクションです.seataは2段階に基づいて提出された2 PCプロトコルである.またseataは2 PCプロトコルに基づいて大きく改善された.1.トランザクションのコミット者が単一のポイントであるという問題を解決しました.2.同期ブロックの問題を解決し、非同期コミット方式を採用し、1つのサービスが完了するとコミットする.3.データが一致しない問題を解決しました.一部のcommitが失敗した場合、fescar-serverは現在のトランザクションモードとブランチトランザクションの戻り状態の結果に基づいて異なる再試行ポリシーを行います.4.単一データベースにしか使用できない問題を解決しました.Fescarは2つのモード、ATとMTを提供しました.ATモードでは、トランザクションリソースはACIDをサポートする任意のデータベースであってもよく、MTモードではトランザクションリソースに制限はなく、キャッシュであってもよく、ファイルであってもよく、他のものであってもよいなどである.もちろんこの2つのモードも混用できます.5.seataを併用したコードへの侵入性は極めて小さい.操作しやすい.
分散トランザクションを解決するためにseataを再マイクロサービスに適用する場合は、いくつかの構成クラスであるマイクロサービスを導入する必要があります.
最初のステップ
プロキシデータベース接続の作成
`/***
     *        
     * @param environment
     * @return
     */
    @Bean
    public DataSource dataSource(Environment environment){
        DruidDataSource dataSource = new DruidDataSource();
        dataSource.setUrl(environment.getProperty("spring.datasource.url"));
        try {
            dataSource.setDriver(DriverManager.getDriver(environment.getProperty("spring.datasource.url")));
        } catch (SQLException e) {
            throw new RuntimeException("can't recognize dataSource Driver");
        }
        dataSource.setUsername(environment.getProperty("spring.datasource.username"));
        dataSource.setPassword(environment.getProperty("spring.datasource.password"));
        return new DataSourceProxy(dataSource);
    }`

第2のステップでは、@GlobalTransactional注記を持つメソッドを解析し、AOPメカニズムを使用してトランザクションを制御するグローバルトランザクションスキャナを確立します.
/***
     *        
     *       @GlobalTransactional     ,    AOP       
     * @param environment
     * @return
     */
 @Bean
    public GlobalTransactionScanner globalTransactionScanner(Environment environment){
        String applicationName = environment.getProperty("spring.application.name");
        String groupName = environment.getProperty("fescar.group.name");
        if(applicationName == null){
            return new GlobalTransactionScanner(groupName == null ? "my_test_tx_group" : groupName);
        }else{
            return new GlobalTransactionScanner(applicationName, groupName == null ? "my_test_tx_group" : groupName);
        }
    }

第3のステップは、マイクロサービスとマイクロサービスとの間でグローバルトランザクションを制御するために相互に呼び出されるたびに、TMがTCに次のトランザクション、すなわち他のマイクロサービスを呼び出すたびに、そのXIDを渡すように要求する.だから、リクエストするたびに、ヘッダのXIDを取得し、次のマイクロサービスにXIDを渡すことができます.
 /***
     *                
     *         ,  TM    TC    XID,         ,                  XID    
     *              ,      XID,  XID         
     * @param restTemplates
     * @return
     */
    @ConditionalOnBean({RestTemplate.class})
    @Bean
    public Object addFescarInterceptor(Collection<RestTemplate> restTemplates){
        restTemplates.stream()
                .forEach(restTemplate -> {
                    List<ClientHttpRequestInterceptor> interceptors = restTemplate.getInterceptors();
                    if(interceptors != null){
                        interceptors.add(fescarRestInterceptor());
                    }
                });
        return new Object();
    }

    @Bean
    public FescarRMRequestFilter fescarRMRequestFilter(){
        return new FescarRMRequestFilter();
    }

    @Bean
    public FescarRestInterceptor fescarRestInterceptor(){
        return new FescarRestInterceptor();
    }

ステップ4スレッド要求ごとにXIDをバインドする
private static final Logger LOGGER = org.slf4j.LoggerFactory.getLogger( FescarRMRequestFilter.class);

    /**
     *            XID
     * @param request
     * @param response
     * @param filterChain
     */
    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        String currentXID = request.getHeader( FescarAutoConfiguration.FESCAR_XID);
        if(!StringUtils.isEmpty(currentXID)){
            RootContext.bind(currentXID);
            LOGGER.info("       XID :" + currentXID);
        }
        try{
            filterChain.doFilter(request, response);
        } finally {
            String unbindXID = RootContext.unbind();
            if(unbindXID != null){
                LOGGER.info("       XID    XID :" + unbindXID);
                if(!currentXID.equals(unbindXID)){
                    LOGGER.info("     XID    ");
                }
            }
            if(currentXID != null){
                LOGGER.info("     XID    ");
            }
        }
    }

ステップ5ブロックを定義し、要求ヘッダを強化し、XIDをヘッダに入れて行を介して渡す
/**
 *
 */
public class FescarRestInterceptor implements RequestInterceptor, ClientHttpRequestInterceptor {

    @Override
    public void apply(RequestTemplate requestTemplate) {
        String xid = RootContext.getXID();
        if(!StringUtils.isEmpty(xid)){
            requestTemplate.header( FescarAutoConfiguration.FESCAR_XID, xid);
        }
    }

    @Override
    public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException {
        String xid = RootContext.getXID();
        if(!StringUtils.isEmpty(xid)){
            HttpHeaders headers = request.getHeaders();
            headers.put( FescarAutoConfiguration.FESCAR_XID, Collections.singletonList(xid));
        }
        return execution.execute(request, body);
    }

最後に、ビジネス・レベルで分散トランザクションを必要とする各メソッドに
 @GlobalTransactional(name="name")

seataはMTおよびハイブリッドモードもサポートしており、現在はATモードのみを説明していますが、一般的には、現在の分散トランザクションを解決するために使用されています.MTモードには、XAプロトコルをサポートする必要がないデータソースを操作するメリットがあります.