Programování

Distribuované transakce na jaře, s nebo bez XA

I když je běžné používat rozhraní Java Transaction API a protokol XA pro distribuované transakce na jaře, máte i jiné možnosti. Optimální implementace závisí na typech prostředků, které vaše aplikace používá, a na kompromisech, které jste ochotni provést mezi výkonem, bezpečností, spolehlivostí a integritou dat. V této funkci JavaWorld vás David Syer ze SpringSource provede sedmi vzory distribuovaných transakcí v jarních aplikacích, tři z nich s XA a čtyři bez. Úroveň: Střední

Podpora rozhraní Spring Transaction API (JTA) Spring Framework umožňuje aplikacím používat distribuované transakce a protokol XA bez spuštění v kontejneru Java EE. I s touto podporou je však XA drahý a jeho správa může být nespolehlivá nebo těžkopádná. Může být tedy vítaným překvapením, že určitá třída aplikací se může úplně vyhnout použití XA.

Abychom vám pomohli porozumět úvahám zapojeným do různých přístupů k distribuovaným transakcím, analyzuji sedm vzorů zpracování transakcí a poskytnu vzorky kódu, aby byly konkrétní. Představím vzorce v opačném pořadí podle bezpečnosti nebo spolehlivosti, počínaje těmi, které mají za nejobecnějších okolností nejvyšší záruku integrity a atomicity dat. Při procházení seznamu se uplatní další upozornění a omezení. Vzory jsou také zhruba v opačném pořadí nákladů za běh (počínaje nejdražšími). Vzory jsou všechny architektonické nebo technické, na rozdíl od obchodních vzorů, takže se nezaměřuji na obchodní případ použití, pouze na minimální množství kódu, aby každý vzor fungoval.

Všimněte si, že pouze první tři vzory zahrnují XA a ty nemusí být dostupné nebo přijatelné z důvodu výkonu. Nemluvím o XA vzorcích tak extenzivně jako o ostatních, protože jsou popsány jinde, i když poskytuji jednoduchou ukázku prvního. Přečtením tohoto článku se dozvíte, co můžete a co nemůžete dělat s distribuovanými transakcemi a jak a kdy se vyhnout použití XA - a kdy ne.

Distribuované transakce a atomicita

A distribuovaná transakce je takový, který zahrnuje více než jeden transakční zdroj. Příklady transakčních prostředků jsou konektory pro komunikaci s relačními databázemi a middlewarem pro zasílání zpráv. Často takový prostředek má API, které vypadá podobně začít(), vrácení zpět (), spáchat(). Ve světě Java se transakční prostředek obvykle zobrazuje jako produkt továrny poskytované základní platformou: pro databázi je to Spojení (produkovaný Zdroj dat) nebo Java Persistence API (JPA) EntityManager; pro Java Message Service (JMS) je to Zasedání.

V typickém příkladu zpráva JMS spustí aktualizaci databáze. V členění na časovou osu probíhá úspěšná interakce asi takto:

  1. Spusťte transakci zasílání zpráv
  2. Přijmout zprávu
  3. Spusťte transakci databáze
  4. Aktualizovat databázi
  5. Potvrdit transakci databáze
  6. Potvrdit transakci zasílání zpráv

Pokud při aktualizaci došlo k chybě databáze, jako je narušení omezení, požadovaná sekvence bude vypadat takto:

  1. Spusťte transakci zasílání zpráv
  2. Přijmout zprávu
  3. Spusťte transakci databáze
  4. Aktualizujte databázi, selhání!
  5. Vrátit zpět transakci databáze
  6. Vrátit zpět zasílání zpráv

V tomto případě se zpráva po posledním vrácení zpět vrátí do middlewaru a v určitém okamžiku se vrátí, aby byla přijata v jiné transakci. To je obvykle dobrá věc, protože jinak byste nemuseli zaznamenávat, že došlo k selhání. (Mechanismy řešení výjimek automatického opakování a zpracování jsou mimo rozsah tohoto článku.)

Důležitým rysem obou časových os je, že jsou atomový, tvořící jedinou logickou transakci, která buď zcela uspěje, nebo zcela selže.

Ale co zaručuje, že časová osa vypadá jako některá z těchto sekvencí? Musí dojít k nějaké synchronizaci mezi transakčními prostředky, takže pokud se jeden zaváže, udělají to oba, a naopak. Jinak celá transakce není atomová. Transakce je distribuována, protože se jedná o více zdrojů, a bez synchronizace nebude atomická. Technické a koncepční potíže s distribuovanými transakcemi se týkají synchronizace zdrojů (nebo jejich nedostatku).

První tři vzory popsané níže jsou založeny na protokolu XA. Vzhledem k tomu, že tyto vzory byly široce pokryty, nebudu zde o nich zacházet příliš podrobně. Ti, kteří znají vzory XA, mohou chtít přeskočit na vzor Sdílené prostředky transakce.

Plná XA s 2PC

Pokud potřebujete téměř neprůstřelné záruky, že se transakce vaší aplikace po výpadku obnoví, včetně pádu serveru, je jedinou volbou Full XA. Sdílený prostředek, který se v tomto případě používá k synchronizaci transakce, je speciální správce transakcí, který koordinuje informace o procesu pomocí protokolu XA. V Javě je z pohledu vývojáře protokol vystaven prostřednictvím JTA UserTransaction.

Protože jde o systémové rozhraní, XA je umožňující technologie, kterou většina vývojářů nikdy nevidí. Musí vědět, že tam je, co umožňuje, co to stojí a důsledky pro to, jak využívají transakční zdroje. Cena pochází z protokolu dvoufázového potvrzení (2PC), který správce transakcí používá k zajištění toho, aby se všechny zdroje dohodly na výsledku transakce před jejím ukončením.

Pokud je aplikace povolena Spring, používá Spring JtaTransactionManager a jarní deklarativní správa transakcí skrýt podrobnosti o základní synchronizaci. Rozdíl mezi vývojářem mezi používáním XA a nepoužíváním XA je o konfiguraci továrních zdrojů: Zdroj dat instance a správce transakcí pro aplikaci. Tento článek obsahuje ukázkovou aplikaci ( atomikos-db projekt), který ilustruje tuto konfiguraci. The Zdroj dat instance a správce transakcí jsou jedinými prvky aplikace specifickými pro XA nebo JTA.

Chcete-li vidět, že vzorek funguje, spusťte testy jednotky pod com.springsource.open.db. Jednoduchý MulipleDataSourceTests třída pouze vloží data do dvou zdrojů dat a poté použije funkce podpory integrace Spring k vrácení transakce zpět, jak je uvedeno v seznamu 1:

Výpis 1. Vrácení transakce zpět

@Transactional @Test public void testInsertIntoTwoDataSources () vyvolá výjimku {int count = getJdbcTemplate (). Update ("INSERT into T_FOOS (id, name, foo_date) values ​​(?,?, Null)", 0, "foo"); assertEquals (1, count); count = getOtherJdbcTemplate () .update ("INSERT into T_AUDITS (id, operation, name, audit_date) values ​​(?,?,?,?)", 0, "INSERT", "foo", new Date ()); assertEquals (1, count); // Změny se vrátí zpět po ukončení této metody}

Pak MulipleDataSourceTests ověří, že obě operace byly odvolány, jak je ukázáno v výpisu 2:

Výpis 2. Ověření vrácení zpět

@AfterTransaction public void checkPostConditions () {int count = getJdbcTemplate (). QueryForInt ("select count (*) from T_FOOS"); // Tato změna byla vrácena zpět testovacím frameworkem assertEquals (0, count); count = getOtherJdbcTemplate (). queryForInt ("select count (*) from T_AUDITS"); // To se také vrátilo kvůli XA assertEquals (0, count); }

Lepší porozumění tomu, jak funguje správa transakcí na jaře a jak ji obecně konfigurovat, najdete v jarní referenční příručce.

XA s optimalizací 1PC

Tento vzor je optimalizace, kterou mnoho správců transakcí používá, aby se zabránilo režii 2PC, pokud transakce zahrnuje jeden prostředek. Očekávali byste, že to váš aplikační server dokáže zjistit.

XA a Gambit posledního zdroje

Dalším rysem mnoha správců transakcí XA je, že mohou stále poskytovat stejné záruky obnovy, pokud všechny prostředky kromě jednoho jsou schopné XA, jako mohou, když jsou všechny. Dělají to objednáním prostředků a použitím jiného zdroje než XA jako rozhodujícího hlasu. Pokud se nepodaří spáchat, pak lze všechny ostatní prostředky vrátit zpět. Je téměř 100% neprůstřelný - ale není to tak úplně. A když selže, selže bez zanechání velké části stopy, pokud nebudou podniknuty další kroky (jak je tomu u některých špičkových implementací).

Vzor zdroje sdílených transakcí

Skvělým vzorem pro snížení složitosti a zvýšení propustnosti v některých systémech je úplné odstranění potřeby XA zajištěním toho, že všechny transakční prostředky v systému jsou skutečně zálohovány stejným prostředkem. To zjevně není možné ve všech případech použití zpracování, ale je to stejně pevné jako XA a obvykle mnohem rychlejší. Vzor zdroje sdílených transakcí je neprůstřelný, ale je specifický pro určité platformy a scénáře zpracování.

Jednoduchým a známým (mnohým) příkladem tohoto vzoru je sdílení databáze Spojení mezi komponentou, která používá objektově-relační mapování (ORM), s komponentou, která používá JDBC. To se stane, když používáte správce transakcí Spring, kteří podporují nástroje ORM, jako jsou Hibernate, EclipseLink a Java Persistence API (JPA). Stejná transakce může být bezpečně použita napříč komponenty ORM a JDBC, obvykle řízena shora provedením metody na úrovni služby, kde je transakce řízena.

Dalším efektivním využitím tohoto vzoru je případ zprávy řízené aktualizace jedné databáze (jako v jednoduchém příkladu v úvodu tohoto článku). Systémy pro zasílání zpráv a middleware musí někde ukládat svá data, často v relační databázi. K implementaci tohoto vzoru je potřeba pouze nasměrovat systém zasílání zpráv do stejné databáze, do které obchodní data vstupují. Tento vzor se spoléhá na to, že prodejce Messaging-Middleware vystavuje podrobnosti své strategie úložiště, takže jej lze nakonfigurovat tak, aby odkazoval na stejnou databázi a připojil se ke stejné transakci.

Ne všichni prodejci to usnadňují. Alternativou, která funguje pro téměř jakoukoli databázi, je použití Apache ActiveMQ pro zasílání zpráv a připojení strategie úložiště do zprostředkovatele zpráv. Konfigurace je poměrně snadná, jakmile tento trik znáte. Je to demonstrováno v tomto článku shared-jms-db ukázkový projekt. Kód aplikace (v tomto případě testy jednotky) si nemusí být vědom toho, že se tento vzor používá, protože je v deklaraci Spring povolen deklarativně.

Volaný test jednotky ve vzorku SynchronousMessageTriggerAndRollbackTests ověřuje, že vše funguje se synchronním příjmem zpráv. The testReceiveMessageUpdateDatabase metoda přijímá dvě zprávy a používá je k vložení dvou záznamů do databáze. Když tato metoda skončí, testovací rozhraní vrátí transakci zpět, takže můžete ověřit, že zprávy i aktualizace databáze jsou vráceny zpět, jak je znázorněno v seznamu 3:

Výpis 3. Ověření vrácení zpráv a aktualizací databáze

@AfterTransaction public void checkPostConditions () {assertEquals (0, SimpleJdbcTestUtils.countRowsInTable (jdbcTemplate, "T_FOOS")); Seznam seznamu = getMessages (); assertEquals (2, list.size ()); }

Nejdůležitější funkce konfigurace jsou strategie vytrvalosti ActiveMQ, která propojuje systém zpráv se stejným Zdroj dat jako obchodní data a vlajka na jaře JmsTemplate slouží k příjmu zpráv. Výpis 4 ukazuje, jak nakonfigurovat strategii perzistence ActiveMQ:

Výpis 4. Konfigurace perzistence ActiveMQ

    ...             

Výpis 5 zobrazuje vlajku na jaře JmsTemplate který se používá pro příjem zpráv:

Výpis 5. Nastavení JmsTemplate pro transakční použití

 ...   

Bez sessionTransacted = true, volání rozhraní API transakce relace JMS nebudou nikdy provedena a příjem zpráv nebude možné vrátit zpět. Důležité ingredience zde jsou vložený makléř se speciálem async = false parametr a obal pro Zdroj dat které společně zajišťují, že ActiveMQ používá stejné transakční JDBC Spojení jako jaro.

$config[zx-auto] not found$config[zx-overlay] not found