Programmering

Distribuerte transaksjoner om våren, med og uten XA

Selv om det er vanlig å bruke Java Transaction API og XA-protokollen for distribuerte transaksjoner om våren, har du andre alternativer. Den optimale implementeringen avhenger av hvilke ressurser applikasjonen bruker og avveiningene du er villig til å gjøre mellom ytelse, sikkerhet, pålitelighet og dataintegritet. I denne JavaWorld-funksjonen guider SpringSource David Syer deg gjennom syv mønstre for distribuerte transaksjoner i Spring-applikasjoner, tre av dem med XA og fire uten. Nivå: Mellomliggende

Spring Framework's støtte for Java Transaction API (JTA) gjør det mulig for applikasjoner å bruke distribuerte transaksjoner og XA-protokollen uten å kjøre i en Java EE-container. Selv med denne støtten er XA imidlertid dyrt og kan være upålitelig eller tungvint å administrere. Det kan derfor være en velkommen overraskelse at en bestemt klasse applikasjoner kan unngå bruk av XA helt.

For å hjelpe deg med å forstå hensynene som er involvert i ulike tilnærminger til distribuerte transaksjoner, vil jeg analysere syv transaksjonsbehandlingsmønstre og gi kodeprøver for å gjøre dem konkrete. Jeg presenterer mønstrene i omvendt rekkefølge av sikkerhet eller pålitelighet, og begynner med de med den høyeste garantien for dataintegritet og atomicitet under de mest generelle omstendighetene. Når du flytter deg nedover på listen, vil flere forbehold og begrensninger gjelde. Mønstrene er også omtrent i omvendt rekkefølge av kjøretidskostnader (starter med de dyreste). Mønstrene er alle arkitektoniske eller tekniske, i motsetning til forretningsmønstre, så jeg fokuserer ikke på forretningsbruk, bare på den minimale mengden kode for å se hvert mønster fungerer.

Merk at bare de tre første mønstrene involverer XA, og de er kanskje ikke tilgjengelige eller akseptable av ytelsesgrunner. Jeg diskuterer ikke XA-mønstrene så omfattende som de andre fordi de er dekket andre steder, selv om jeg gir en enkel demonstrasjon av den første. Ved å lese denne artikkelen lærer du hva du kan og ikke kan gjøre med distribuerte transaksjoner og hvordan og når du skal unngå bruk av XA - og når ikke.

Distribuerte transaksjoner og atomisitet

EN distribuert transaksjon er en som involverer mer enn en transaksjonsressurs. Eksempler på transaksjonsressurser er kontaktene for å kommunisere med relasjonsdatabaser og meldingsprogramvare. Ofte har en slik ressurs en API som ser ut som begynne(), tilbakeføring (), begå(). I Java-verdenen vises en transaksjonsressurs vanligvis som produktet av en fabrikk levert av den underliggende plattformen: for en database er det en Forbindelse (produsert av Datakilde) eller Java Persistence API (JPA) EntityManager; for Java Message Service (JMS), er det en Økt.

I et typisk eksempel utløser en JMS-melding en databaseoppdatering. Brutt ned i en tidslinje, en vellykket interaksjon går omtrent slik:

  1. Start meldingsoverføring
  2. Motta melding
  3. Start databasetransaksjon
  4. Oppdatere database
  5. Forplikt databasetransaksjon
  6. Forplikt meldingstransaksjon

Hvis det oppstod en databasefeil som et begrensningsbrudd på oppdateringen, ville den ønskelige sekvensen se slik ut:

  1. Start meldingsoverføring
  2. Motta melding
  3. Start databasetransaksjon
  4. Oppdater database, mislykkes!
  5. Rull tilbake databasetransaksjon
  6. Rull tilbake meldingstransaksjonen

I dette tilfellet går meldingen tilbake til mellomvaren etter siste tilbakestilling og returnerer på et tidspunkt for å bli mottatt i en annen transaksjon. Dette er vanligvis en god ting, for ellers har du kanskje ikke registrert at det oppstod en feil. (Mekanismer for å håndtere automatiske forsøk på nytt og håndtering av unntak er utenfor denne artikkelen.)

Det viktige trekket ved begge tidslinjene er at de er det atomisk, danner en enkelt logisk transaksjon som enten lykkes helt eller mislykkes fullstendig.

Men hva garanterer at tidslinjen ser ut som en av disse sekvensene? Noe synkronisering mellom transaksjonsressursene må skje, slik at hvis man forplikter begge gjør det, og omvendt. Ellers er ikke hele transaksjonen atomær. Transaksjonen distribueres fordi flere ressurser er involvert, og uten synkronisering vil den ikke være atomær. De tekniske og konseptuelle vanskelighetene med distribuerte transaksjoner er alle knyttet til synkronisering av ressursene (eller mangel på det).

De tre første mønstrene som er diskutert nedenfor er basert på XA-protokollen. Fordi disse mønstrene har blitt dekket mye, vil jeg ikke gå i detalj om dem her. De som er kjent med XA-mønstre, vil kanskje hoppe videre til Shared Transaction Resource pattern.

Full XA med 2PC

Hvis du trenger nært skuddsikre garantier for at applikasjonens transaksjoner vil komme seg etter et brudd, inkludert et serverkrasj, er Full XA ditt eneste valg. Den delte ressursen som brukes til å synkronisere transaksjonen i dette tilfellet, er en spesiell transaksjonsadministrator som koordinerer informasjon om prosessen ved hjelp av XA-protokollen. I Java, fra utviklerens synspunkt, blir protokollen eksponert gjennom en JTA UserTransaction.

Å være et systemgrensesnitt, er XA en mulig teknologi som de fleste utviklere aldri ser. De trenger å vite er at det er der, hva det muliggjør, hva det koster, og implikasjonene for hvordan de bruker transaksjonsressurser. Kostnaden kommer fra den tofasede commit-protokollen (2PC) som transaksjonslederen bruker for å sikre at alle ressurser er enige om resultatet av en transaksjon før den avsluttes.

Hvis applikasjonen er våraktivert, bruker den våren JtaTransactionManager og vårdeklarativ transaksjonsadministrasjon for å skjule detaljene i den underliggende synkroniseringen. Forskjellen for utvikleren mellom å bruke XA og ikke å bruke XA handler om å konfigurere fabrikkressursene: Datakilde forekomster og transaksjonsansvarlig for applikasjonen. Denne artikkelen inneholder et eksempel på søknad ( atomikos-db prosjekt) som illustrerer denne konfigurasjonen. De Datakilde forekomster og transaksjonsadministrator er de eneste XA- eller JTA-spesifikke elementene i applikasjonen.

For å se prøven fungerer, kjør enhetstestene under com.springsource.open.db. En enkel MulipleDataSourceTests klasse setter bare inn data i to datakilder og bruker deretter støtteintegrasjonsfunksjonene for våren for å rulle tilbake transaksjonen, som vist i liste 1:

Oppføring 1. Transaksjon av tilbakeføring

@Transactional @Test public void testInsertIntoTwoDataSources () kaster unntak {int count = getJdbcTemplate (). Oppdatering ("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); // Endringer kommer tilbake etter at denne metoden er avsluttet}

Deretter MulipleDataSourceTests bekrefter at de to operasjonene begge ble rullet tilbake, som vist i liste 2:

Oppføring 2. Bekrefte tilbakeføring

@AfterTransaction public void checkPostConditions () {int count = getJdbcTemplate (). QueryForInt ("select count (*) from T_FOOS"); // Denne endringen ble rullet tilbake av testrammeverket assertEquals (0, count); count = getOtherJdbcTemplate (). queryForInt ("select count (*) from T_AUDITS"); // Dette rullet også tilbake på grunn av XA assertEquals (0, count); }

For å få en bedre forståelse av hvordan vårstransaksstyring fungerer og hvordan du konfigurerer den generelt, se vårreferanseguiden.

XA med 1PC optimalisering

Dette mønsteret er en optimalisering som mange transaksjonsledere bruker for å unngå overhead for 2PC hvis transaksjonen inkluderer en enkelt ressurs. Du forventer at applikasjonsserveren din kan finne ut av dette.

XA and the Last Resource Gambit

En annen funksjon hos mange XA-transaksjonsledere er at de fremdeles kan gi de samme utvinningsgarantiene når alle bortsett fra en ressurs er XA-kompatible som de kan når de alle er. De gjør dette ved å bestille ressursene og bruke ressursen som ikke er XA som avgivende stemme. Hvis den ikke forplikter seg, kan alle de andre ressursene rulles tilbake. Den er nærmere 100 prosent skuddsikker - men er ikke helt det. Og når den mislykkes, mislykkes den uten å etterlate seg mye spor med mindre ekstra trinn er tatt (som det gjøres i noen av topp implementeringene).

Delt ressursmønster for delt transaksjon

Et flott mønster for å redusere kompleksiteten og øke gjennomstrømningen i noen systemer er å fjerne behovet for XA helt ved å sikre at alle transaksjonsressursene i systemet faktisk støttes av samme ressurs. Dette er tydeligvis ikke mulig i alle behandlingstilfeller, men det er like solid som XA og vanligvis mye raskere. Shared Transaction Resource mønsteret er skuddsikkert, men spesifikt for visse plattformer og behandlingsscenarier.

Et enkelt og kjent (for mange) eksempel på dette mønsteret er deling av en database Forbindelse mellom en komponent som bruker objektrelasjonell kartlegging (ORM) med en komponent som bruker JDBC. Dette er hva som skjer når du bruker Spring-transaksjonsledere som støtter ORM-verktøy som Hibernate, EclipseLink og Java Persistence API (JPA). Den samme transaksjonen kan trygt brukes på tvers av ORM- og JDBC-komponenter, vanligvis drevet ovenfra av en servicekjøring av metodenivå der transaksjonen kontrolleres.

En annen effektiv bruk av dette mønsteret er tilfellet med meldingsdrevet oppdatering av en enkelt database (som i det enkle eksemplet i innledningen til denne artikkelen). Messaging-middleware-systemer må lagre dataene sine et sted, ofte i en relasjonsdatabase. For å implementere dette mønsteret er alt som trengs å peke meldingssystemet på den samme databasen bedriftsdataene går inn i. Dette mønsteret er avhengig av at meldings-mellomvareleverandøren avslører detaljene i lagringsstrategien, slik at den kan konfigureres til å peke på den samme databasen og koble til den samme transaksjonen.

Ikke alle leverandører gjør dette enkelt. Et alternativ, som fungerer i nesten hvilken som helst database, er å bruke Apache ActiveMQ for meldinger og koble en lagringsstrategi til meldingsmegleren. Dette er ganske enkelt å konfigurere når du kjenner trikset. Det er demonstrert i denne artikkelen delt-jms-db prøver prosjekt. Søknadskoden (enhetstester i dette tilfellet) trenger ikke å være klar over at dette mønsteret er i bruk, fordi det hele er aktivert erklærende i vårkonfigurasjon.

En enhetstest i prøven kalt SynchronousMessageTriggerAndRollbackTests bekrefter at alt fungerer med synkron mottak av meldinger. De testReceiveMessageUpdateDatabase metoden mottar to meldinger og bruker dem til å sette inn to poster i databasen. Når denne metoden avsluttes, ruller testrammeverket tilbake transaksjonen, slik at du kan bekrefte at meldingene og databaseoppdateringene er rullet tilbake, som vist i liste 3:

Oppføring 3. Bekrefte tilbakeføring av meldinger og databaseoppdateringer

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

De viktigste funksjonene i konfigurasjonen er ActiveMQ-utholdenhetsstrategien, som knytter meldingssystemet til det samme Datakilde som forretningsdata, og flagget på våren JmsTemplate brukes til å motta meldingene. Oppføring 4 viser hvordan du konfigurerer ActiveMQ-utholdenhetsstrategien:

Oppføring 4. Konfigurering av ActiveMQ-utholdenhet

    ...             

Oppføring 5 viser flagget på våren JmsTemplate som brukes til å motta meldingene:

Oppføring 5. Sette opp JmsTemplate for transaksjonsbruk

 ...   

Uten sessionTransacted = true, vil JMS-sesjonstransaksjon API-anrop aldri bli utført, og mottaket av meldingen kan ikke rulles tilbake. De viktige ingrediensene her er den innebygde megleren med en spesiell asynkronisering = falsk parameter og en innpakning for Datakilde som sammen sørger for at ActiveMQ bruker samme transaksjonelle JDBC Forbindelse som vår.

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