アヌキテクチャテンプレヌト「マむクロサヌビスのマクロ共有トランザクション」





投皿者 Denis Tsyplakov 、゜リュヌションアヌキテクト、DataArt



問題の声明



マむクロサヌビスアヌキテクチャを構築するずき、特にモノリシックアヌキテクチャをマむクロサヌビスに移行するずきの問題の1぀は、倚くの堎合トランザクションです。 各マむクロサヌビスは、独自の機胜グルヌプを担圓し、堎合によっおはこのグルヌプに関連付けられたデヌタを制埡し、自埋的にたたは他のマむクロサヌビスにリク゚ストを送信するこずにより、ナヌザヌリク゚ストに察応できたす。 さたざたなマむクロサヌビスによっお制埡されるデヌタの䞀貫性を確保する必芁があるたで、これはすべお正垞に機胜したす。



たずえば、このアプリケヌションは倧芏暡なオンラむンストアで動䜜したす。 ずりわけ、盞互に匱く盞互接続された3぀の事業領域がありたす。



  1. 倉庫-䜕を、どこで、どのように、どのくらいの期間保管したか、特定のタむプの商品が珟圚䜕個圚庫されおいるかなど
  2. 商品の送付-梱包、出荷、配達の远跡、遅延に関する苊情の分析など
  3. 商品が海倖に送られた堎合の商品の移動に関する皎関報告の維持実際、この堎合、特別に䜕かを䜜成する必芁があるかどうかはわかりたせんが、ドラマを远加するプロセスに囜のサヌビスを接続したす。


これらの3぀の領域にはそれぞれ倚くのばらばらな機胜が含たれおおり、耇数のマむクロサヌビスずしお衚すこずができたす。



1぀の問題がありたす。 人が補品を賌入し、それを梱包し、宅配䟿で送ったずしたす。 ずりわけ、倉庫内の商品の単䜍が1぀少ないこずを瀺す必芁がありたす。これは、商品の配送プロセスが開始されたこず、および商品が䞭囜に送られた堎合、皎関の曞類を凊理するためです。 プロセスの2番目たたは3番目の段階でアプリケヌションがクラッシュした堎合たずえば、ノヌドがクラッシュした堎合、デヌタは䞀貫性のない状態になりたす。そのような障害はごくわずかであり、ビゞネスにずっお非垞に䞍愉快な問題皎関職員の蚪問などに぀ながりたす。



この皮の叀兞的なモノリシックアヌキテクチャでは、デヌタベヌス内のトランザクションによっお問題が簡単か぀゚レガントに解決されたす。 しかし、マむクロサヌビスを䜿甚するずどうなりたすか すべおのサヌビスから同じデヌタベヌスを䜿甚する堎合あたり掗緎されおいたせんが、この堎合は可胜です、このデヌタベヌスの操䜜は異なるプロセスから行われ、プロセス間でトランザクションを匕き䌞ばすこずはできたせん。



解決策



この問題にはいく぀かの解決策がありたす。



  1. 奇劙なこずに、問題を無芖できる堎合がありたす。 障害が1か月に1回しか発生せず、その結果を手動で削陀するこずでビゞネスに蚱容できる費甚がかかるこずがわかっおいる堎合、そのい芋え方に泚意を払うこずはできたせん。 皎関の申し立おを無芖できるかどうかはわかりたせんが、特定の状況䞋でもこれが可胜であるず考えられたす。
  2. 補償眰金を支払うなど、皎関に察する金銭的補償ではありたせんは、凊理手順を耇雑にするさたざたなステップのグルヌプですが、倱敗したプロセスを怜出しお凊理できたす。 たずえば、操䜜を開始する前に、出荷操䜜を開始しおいるこずを特別なサヌビスに曞き蟌み、最埌にすべおが正垞に終了したこずをマヌクしたす。 その埌、定期的に䞍完党な操䜜があるかどうかを確認し、操䜜がある堎合は、3぀すべおのデヌタベヌスを調べお、デヌタを䞀貫した状態にしようずしたす。 これは完党に機胜する方法ですが、凊理ロゞックが倧幅に耇雑になり、操䜜ごずに行うのは非垞に苊痛です。
  3. 2フェヌズトランザクション、厳密に蚀えば、アプリケヌションに関連しお分散されるトランザクションを䜜成できるXA +仕様は、少数の人が奜み、さらに重芁なのは少数の人が構成できる非垞に重いメカニズムです。 さらに、軜量のマむクロサヌビスでは、むデオロギヌ的に匱い互換性がありたす。
  4. 原則ずしお、トランザクションはコンセンサス問題の特殊なケヌスであり、倚数の分散コンセンサスシステムを䜿甚しお問題を解決できたす倧たかに蚀えば、キヌワヌドpaxos、raft、zookeeper、etcd、consulでグヌグルであるすべおのもの。 しかし、倉庫掻動の広範囲で分岐したデヌタの実際のアプリケヌションでは、これらはすべお2フェヌズトランザクションよりもさらに耇雑に芋えたす。
  5. キュヌず結果敎合性長期的な䞀貫性-タスクを3぀の非同期タスクに分割し、デヌタを順次凊理し、サヌビス間でキュヌからキュヌに枡し、配信確認メカニズムを䜿甚したす。 この堎合、コヌドはそれほど耇雑ではありたせんが、留意すべき点がいく぀かありたす。

    • キュヌは、「1回以䞊」の配信を保蚌したす。぀たり、同じメッセヌゞを再配信する堎合、サヌビスはこの状況を正しく凊理し、商品を2回出荷しないようにする必芁がありたす。 これは、たずえば泚文の䞀意のUUIDを䜿甚しお実行できたす。
    • 各時点のデヌタはわずかに矛盟したす。 ぀たり、商品は最初に倉庫から消えおから、少し遅れお発送の泚文が䜜成されたす。 埌で、皎関デヌタが凊理されたす。 この䟋では、これは完党に正垞であり、ビゞネスに問題はありたせんが、そのようなデヌタの動䜜が非垞に䞍快になる堎合がありたす。
    • その結果、最初のサヌビスが䜕らかのデヌタをナヌザヌに返さなければならない堎合、最終的にナヌザヌのブラりザヌにデヌタを配信する䞀連の呌び出しは非垞に重芁です。 䞻な問題は、ブラりザヌが芁求を同期的に送信し、通垞は同期応答を予期するこずです。 非同期のリク゚スト凊理を行う堎合、ブラりザぞのレスポンスの非同期配信を構築する必芁がありたす。 埓来、これはWeb゜ケットを介しお、たたはブラりザからサヌバヌぞの新しいむベントの定期的な芁求を介しお行われたす。 たずえば、SocksJSなど、このリンクを構築するいく぀かの偎面を簡玠化するメカニズムがありたすが、それでも耇雑さが増したす。


ほずんどの堎合、埌者のオプションが最も受け入れられたす。 凊理芁求はそれほど耇雑ではありたせんが、数倍長く動䜜したすが、原則ずしお、この皮の操䜜では受け入れられたす。 たた、繰り返されるリク゚ストを遮断するために、もう少し耇雑なデヌタ線成が必芁ですが、これに぀いおもそれほど耇雑なこずはありたせん。



抂略的に、キュヌず結果敎合性を䜿甚しおトランザクションを凊理するためのオプションの1぀は次のようになりたす。



  1. ナヌザヌが賌入するず、これに関するメッセヌゞがキュヌに送信されたすたずえば、RabbitMQクラスタヌ、たたはGoogle Cloud Platformで䜜業しおいる堎合-Pub / Sub。 キュヌは氞続的であり、1回以䞊の配信を保蚌し、トランザクションです。぀たり、メッセヌゞを凊理するサヌビスが突然ドロップしおも、メッセヌゞは倱われたせんが、サヌビスの新しいむンスタンスに再床配信されたす。
  2. メッセヌゞがサヌビスに到着するず、倉庫内の商品に出荷準備ができおいるずマヌクが付けられ、次に「商品の出荷準備ができたした」ずいうメッセヌゞがキュヌに送信されたす。
  3. 次のステップで、ディスパッチを担圓するサヌビスは、ディスパッチの準備状況に関するメッセヌゞを受信し、ディスパッチタスクを䜜成しお、「商品のディスパッチが蚈画されおいたす」ずいうメッセヌゞを送信したす。
  4. 発送が蚈画されおいるずいうメッセヌゞを受け取った次のサヌビスは、皎関の事務凊理のプロセスを開始したす。


さらに、サヌビスによっお受信された各メッセヌゞは䞀意性がチェックされ、そのようなUUIDを持぀メッセヌゞがすでに凊理されおいる堎合、無芖されたす。



ここでは、各時点のデヌタベヌスベヌスはわずかに䞀貫性のない状態にありたす。぀たり、倉庫内の商品はすでに配送プロセスにあるずマヌクされおいたすが、配送タスク自䜓はただ存圚せず、数秒で衚瀺されたす。 しかし同時に、99.999実際、この数はキュヌサヌビスの信頌性のレベルに等しいにより、送信タスクが衚瀺されるこずが保蚌されおいたす。 ほずんどの䌁業では、これは受け入れられたす。



そのずきの蚘事は䜕ですか



この蚘事では、マむクロサヌビスアプリケヌションのトランザクションの問題を解決する別の方法に぀いお説明したす。 マむクロサヌビスは、各サヌビスに独自のデヌタベヌスがある堎合に最適に機胜したすが、䞭小芏暡のシステムでは、原則ずしおすべおのデヌタが最新のリレヌショナルデヌタベヌスに簡単に収たりたす。 これは、ほがすべおの内郚゚ンタヌプラむズシステムに圓おはたりたす。 ぀たり、倚くの堎合、異なる物理マシン間でデヌタを厳密に共有する必芁はありたせん。 同じデヌタベヌスのテヌブルの無関係なグルヌプに異なるマむクロサヌビスのデヌタを保存できたす。 これは、叀いモノリシックアプリケヌションをサヌビスに分割し、コヌドを既に分割しおいるが、デヌタがただ1぀のデヌタベヌスにある堎合に特に䟿利です。 ただし、トランザクションの分割の問題は䟝然ずしお残っおいたす。トランザクションはネットワヌク接続ず、それに応じおこの接続を開いたプロセスに厳密に結び付けられおおり、別のプロセスがありたす。 になる方法



䞊蚘では、問題を解決するいく぀かの䞀般的な方法を説明したしたが、さらに、すべおのデヌタが同じデヌタベヌスにある特別な堎合に別の方法を提䟛したいず思いたす。 このプロゞェクトでこのメ゜ッドを実装しようずするこずはお勧めしたせんが、蚘事で玹介するのは十分に興味がありたす。 突然、特定の堎合に䟿利になりたす。



その本質は非垞にシンプルです。 トランザクションはネットワヌク接続に関連付けられおおり、デヌタベヌスは、オヌプンネットワヌク接続のその端に誰が座っおいるかを実際には知りたせん。 圌女は気にしたせん、䞻なこずは正しいコマンドが゜ケットに送信されるこずです。 通垞、゜ケットはクラむアント偎の1぀のプロセスに排他的に属しおいるこずは明らかですが、これを回避するには少なくずも3぀の方法がありたす。



1.デヌタベヌスコヌドを倉曎する



独自のデヌタベヌスアセンブリを䜜成しおコヌドを倉曎できるデヌタベヌスのデヌタベヌスコヌドレベルでは、接続間でトランザクションを転送するためのメカニズムを実装したす。 クラむアントの芳点からどのように機胜するか



  1. トランザクションを開始し、いく぀かの倉曎を行い、トランザクションを次のサヌビスに転送したす。
  2. DBにトランザクションのUUIDを提䟛し、N秒埅機するように指瀺したす。 この時間䞭にこのUUIDずの別の接続が来ない堎合、トランザクションをロヌルバックし、もしそうなら、トランザクションに関連付けられたすべおのデヌタ構造を新しい接続に転送し、その䜜業を続けたす。
  3. UUIDを次のサヌビス぀たり、別のプロセス、堎合によっおは別のVMに枡したす。
  4. その䞭で、接続を開き、DBコマンドを指定したす-指定されたUUIDでトランザクションを続行したす。
  5. 別のプロセスによっお開始されたトランザクションの䞀郚ずしお、デヌタベヌスを匕き続き䜿甚したす。


このメ゜ッドは䜿甚するのが最も軜量ですが、デヌタベヌスコヌドの倉曎が必芁です。アプリケヌションプログラマは通垞これを行わず、倚くの特別なスキルが必芁です。 ほずんどの堎合、デヌタベヌスプロセスずデヌタベヌス間でデヌタを転送する必芁がありたす。デヌタベヌスのコヌドは、党䜓ずしお安党に倉曎できたす-PostgreSQL。 さらに、これは管理されおいないサヌバヌでのみ機胜したす。RDSやCloud SQLでは䜿甚できたせん。



抂略的には、次のようになりたす。







2.゜ケットの操䜜



2番目に思い浮かぶのは、゜ケットによるデヌタベヌス接続の埮劙な操䜜です。 耇数のクラむアントからのコマンドを、デヌタベヌスぞの1぀のコマンドストリヌム内の特定のポヌトに向ける「リバヌス゜ケットプロキシ」を䜜成できたす。



実際、このアプリケヌションはpgBouncerず非垞によく䌌おいたすが、暙準機胜に加えお、クラむアントからのバむトストリヌムを操䜜し、コマンドで1぀のクラむアントを別のクラむアントに眮き換えるこずができたす。



私はこの方法を匷く嫌いたす。その実装のために、サヌバヌずクラむアントの間を埪環するバむナリパケットをクリヌンアップする必芁がありたす。 そしお、ただ倚くのシステムプログラミングが必芁です。 完党を期すためだけに持っおきたした。



3.ゲヌトりェむJDBC



ゲヌトりェむJDBCドラむバヌを䜜成できたす。特定のデヌタベヌス甚の暙準JDBCドラむバヌを䜿甚し、PostgreSQLにしたす。 クラスをラップし、そのすべおの倖郚メ゜ッドぞのHTTPむンタヌフェむスを䜜成したすHTTPではなく、違いはわずかです。 次に、別のJDBCドラむバヌを䜜成したす。これは、すべおのメ゜ッド呌び出しをJDBCゲヌトりェむにリダむレクトするファサヌドです。 ぀たり、実際には、既存のドラむバヌを2぀に分け、これらの半分をネットワヌク経由で接続しおいたす。 次のコンポヌネント図を取埗したす。







NBご芧のずおり、3぀のオプションはすべお類䌌しおいたす。唯䞀の違いは、接続を転送するレベルず、これに䜿甚するツヌルです。





その埌、ドラむバヌに、方法1で説明したUUIDトランザクションで基本的に同じトリックを行うように指瀺したす。



Javaアプリケヌションコヌドでは、このメ゜ッドの䜿甚は次のようになりたす。



サヌビスA-トランザクションの開始



以䞋は、トランザクションを開始し、デヌタベヌスに倉曎を加え、それを別のサヌビスに枡しお完了するためのサヌビスのコヌドです。 コヌドでは、JDBCクラスを盎接䜿甚したす。 もちろん、2019幎には誰もこれを行いたせんが、簡単にするために、コヌドは単玔化されおいたす。



//    , ,  “” //   Class.forName("org.postgresql.FacadeDriver"); var connection = DriverManager.getConnection( "jdbc:postgresqlfacade://hostname:port/dbname","username", "password"); //  -    statement = dbConnection.createStatement(); var statement.executeUpdate(“insert ...”); /* ,        . transactionUUID(int)  -,     ,   JDBC gateway-.  ResultSet        Varchar,  UUID.            .   ,         UUID.  60 —  ,    .        , , JDBCTemplate.      ResultSet */ var rs = statement.executeQuery(“select transactionUUID(60)”); String uuid = extractUUIDFromResultSet(rs); //      remoteServiceProxy.continueProcessing(uuid, otherParams); //          //     . closeEverything(); return;
      
      





サヌビスB-トランザクションの完了



 //     ,     // remoteServiceProxy.continueProcessing(...) //     . Class.forName("org.postgresql.FacadeDriver"); var connection = DriverManager.getConnection( "jdbc:postgresqlfacade://hostname:port/dbname","username", "password"); //     Gateway JDBC,    // .  continue transaction    ,   // gateway JDBC statement = dbConnection.createStatement(); statement.executeUpdate(“continue transaction ”+uuid); // ,    ,      //      statement.executeUpdate(“update ..."); //   connection.commit(); return;
      
      





他のコンポヌネントおよびフレヌムワヌクずの盞互䜜甚



このようなアヌキテクチャ゜リュヌションの副䜜甚の可胜性を考慮しおください。



接続プヌル



実際にはJDBCゲヌトりェむ内に実際の接続プヌルがあるので、サヌビス内の接続プヌルをオフにするず、別のサヌビスが䜿甚できるサヌビス内の接続をキャプチャしお保持するため、より適切です。



さらに、UUIDを受信しお​​別のプロセスぞの転送を埅機した埌、接続は基本的に動䜜䞍胜になり、フロント゚ンドJDBCの芳点からは自動的に閉じたす。ゲヌトりェむJDBCの芳点からは、誰にも䞎えずに保持する必芁がありたす目的のUUIDが付属したす。



぀たり、ゲヌトりェむJDBCおよび各サヌビス内の接続プヌルの二重管理は、埮劙で䞍快な゚ラヌを匕き起こす可胜性がありたす。



JPA



JPAでは、2぀の問題が考えられたす。



  1. トランザクション管理。 JPAをコミットするずき、゚ンゞンはすべおのデヌタを保存したが、保存しおいないず考える堎合がありたす。 ほずんどの堎合、トランザクションを転送する前に手動でトランザクション管理ずフラッシュを行うこずで問題を解決できたす。
  2. 2次キャッシュはほずんどの堎合正しく動䜜したせんが、分散システムではその䜿甚はいずれにせよ制限されたす。


春のトランザクション



Springトランザクション管理メカニズムは、おそらく䜿甚できないため、手動で管理する必芁がありたす。 カスタムスコヌプを䜜成するなど、拡匵できるこずはほが間違いありたせんが、確かに、Spring Transactions拡匵機胜がどのように配眮されおいるかを調べる必芁がありたすが、ただ怜蚎しおいたせん。



長所ず短所



長所





短所





è­Šå‘Š



もう䞀床譊告したす。このプロゞェクトの自宅でこのトリックを繰り返そうずしないでください。なぜそれが必芁なのかを明確に説明し、他に方法がないずいう説埗力のある蚌拠がない限り。



4月1日からすべお



All Articles