Network Security Internet Technology Development Database Servers Mobile Phone Android Software Apple Software Computer Software News IT Information

In addition to Weibo, there is also WeChat

Please pay attention

WeChat public account

Shulou

What is the implementation of XA-based and non-XA-based Spring distributed transactions

2025-01-16 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Servers >

Share

Shulou(Shulou.com)06/01 Report--

This article shows you how to implement XA-style, non-XA-style Spring distributed transactions. The content is concise and easy to understand, which will definitely brighten your eyes. I hope you can get something through the detailed introduction of this article.

Several transaction processing mechanisms for Spring applications

Java Transaction API and XA protocols are common distributed transaction mechanisms in Spring, but you can choose other implementations. The ideal implementation depends on what resources your application uses and what tradeoffs you are willing to make in terms of performance, security, system robustness, and data integrity. At this JavaWorld conference, David Syer from SpringSource shared several transaction mechanisms, three XA-style and four non-XA transaction protocols of Spring applications.

The Spring framework supports Java Transaction API (JTA) so that applications can move away from the Java EE container and take advantage of distributed transactions and XA protocols. However, even with such support, XA is expensive, unstable and cumbersome to manage, but some other applications can avoid using the XA protocol.

In order to give you an understanding of the several distributed transactions involved, I will analyze seven transaction modes and give specific code implementations. And from the security or stability display in reverse order, we can see how to ensure the high integrity and atomicity of data in general scenarios from the perspective of security and stability. Of course, with the deepening of the topic, more explanations and restrictions will appear. Patterns can also be displayed in reverse order from runtime overhead. Given that all patterns are structured or academic, which is different from business models, I'm not going to do business use case analysis, but only focus on how a small portion of the code for each pattern works.

Although only the first three modes relate to the XA protocol, they may not meet the requirements from a performance point of view. Considering that these patterns are ubiquitous, I don't want to expand too much, just a simple demonstration of the first one. After reading this article, you can learn what can and cannot be done with distributed transactions, and how, when to avoid using XA, and when it must be used.

Distributed transactions and atomicity

Distributed transactions involve more than one transaction resource. For example, connectors that communicate between relational databases and messaging middleware typically have API like begin (), rollback (), and commit (). Here, a transaction resource is usually a factory product, which is usually provided by the underlying platform: take the database as an example, DataSource provides Connection, or the EntityManager interface of Java Persistence API (JPA); or Session provided by Java Message Service (JMS).

In a typical example, a JMS message triggers a database update. This process can be broken down into a timeline, and a successful interaction sequence is as follows:

Open message transaction

Accept the message

Open a database transaction

Update the database

Commit database transaction

Commit message transaction

If there is a database error, such as a constraint violation during an update, an ideal order would look like this:

Open message transaction

Accept the message

Open a database transaction

Failed to update database

Roll back database transactions

Roll back message transaction

In this case, the message is returned to the middleware after the final rollback occurs, and to some extent the returned message is received by other transactions. Usually this is a good thing, maybe you don't keep a record of your failure. The automatic retry exception handling mechanism is beyond the scope of this article.

The most important feature of the above two timelines is their atomicity, forming a single logical transaction unit, which either succeeds or fails.

So what do you use to make sure the timeline meets in order? Some synchronization must be maintained between transaction resources, and once a data source is committed, it is either committed or rolled back. Otherwise, there is no lack of atomicity in the whole business. The reason why it is a distributed transaction is that there are multiple data sources, and there is no atomicity without synchronization. The core issues of distributed transaction technology and concepts are around the synchronization of resources or the inability to synchronize.

The following discussion of the first three modes is based on the XA protocol. Considering that these three modes are widely distributed, this article will not cover many details. If you are familiar with the XA mode, you may be willing to skip directly to the shared transaction resource mode.

Two-stage submission of the complete XA protocol

If you need near-perfect protection (close-to-bulletproof) to ensure that your application transactions recover after a power outage and the server crashes, full XA is the best choice. Shared resources usually need to do transaction synchronization, in this case, it is a special transaction manager that uses XA protocol to coordinate the processing process. In the Java world, from the developer's point of view, this protocol is exposed through JPA UserTransaction.

Based on the system interface, XA is invisible to most developers as an enabling technology (enabling technology), so they need to know where XA is, what it contributes to, how it is consumed, and how to utilize transactional resources. The transaction manager adopts the two-phase commit (2PC) protocol, which not only ensures that all resources adopt the same transaction result before the end of the transaction, but also brings performance loss.

If Spring facilitates (Spring-enabled), the application will adopt Spring's JtaTransactionManager and Spring declarative transaction management, which hides the details of underlying transaction synchronization. The difference between using XA or not for developers lies in the configuration of factory resources: DataSource instances, and the application's transaction manager. This article will reveal this configuration through an application case (atomikos-db project). Database instances and transaction managers are only XA or JTA-specific application elements.

In order to reveal how this case works, in com.springsource.open.db. Run this unit test under. A simple MulipleDataSourceTests class simply inserts data into two data sources and rolls back the transaction using the features supported by Spring integration, as shown in listing 1:

Listing 1. Transaction rollback

@ Transactional @ Test public void testInsertIntoTwoDataSources () throws Exception {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); / / Changes will roll back after this method exits}

Then verify that both operations are rolled back at the same time, as shown in listing 2:

Listing 2. Rollback validation

@ AfterTransaction public void checkPostConditions () {int count = getJdbcTemplate () .queryForInt ("select count (*) from T_FOOS"); / / This change was rolled back by the test framework assertEquals (0, count); count = getOtherJdbcTemplate () .queryForInt ("select count (*) from T_AUDITS"); / / This rolled back as well because of the XA assertEquals (0, count);}

To learn more about how Spring transaction management works and how to configure it, see the Spring reference guide.

One-stage commit optimizes XA protocol

Many transaction managers use this optimization mode to avoid excessive 2PC overhead under a single transaction resource, and your application server should be able to identify this situation.

Protocol and final resource strategy

Another feature of most XA transaction managers is that transaction managers provide the same recovery guarantee, whether a single XA compatible resource or all resources are XA compatible. They are achieved by sorting resources and voting for non-XA resources so that if the transaction fails to commit, all other resources can be rolled back. The transaction is almost 100% guaranteed, but the disadvantage is that if the transaction fails, it does not leave much information at this time. In other words, if you want to get this information, you need to take some additional steps, such as in some high-level implementation.

Shared transaction resource model

This model is good, all the transaction resources of the system are supported by the same resource, and then the XA is removed, which reduces the complexity of the system and improves the throughput. Of course, it can't be used to handle all use cases, but it's as sturdy as XA, and it's faster. The shared transaction resource model exists as a guarantee in specific platforms and processing scenarios.

A simple and familiar example is Connection that shares a database, which exists between an object relational model (ORM) control and a JDBC control. This is the case with the Spring transaction manager, which supports ORM tools such as Hibernate, EclipseLink, and Java Persistence API (JPA). The same transaction can safely span between ORM and JDBC controls, usually driven by transaction-controlled execution methods in the service layer.

Another feature of this pattern is message-driven individual database updates, such as the simple example at the beginning of this article. Information elimination middleware systems need to store these data, usually relational databases. To implement this pattern, you need to specify the messaging system to the same database used to store business data. This pattern relies on the storage strategy details provided by the message middleware provider so that the message middleware can be configured in the same database and embedded in the same transaction.

Not all vendors provide this pattern, but an alternative way that can be used in almost any database is to take advantage of Apache ActiveMQ's delivery of messages and insert a storage policy into the message broker. Once you know the trick, it's easy to configure, and I'll demonstrate it in the shared-jms-db project case in this article. The code used for this pattern does not need to be concerned; it is declared in the Spring configuration.

In the case called SynchronousMessageTriggerAndRollbackTests, the unit test validates all messages related to the recipient of the synchronous message. The testReceiveMessageUpdateDatabase method accepts two messages and then inserts two records into the database using the message. If this method exits, the test framework rolls back the transaction so that you can verify that messages and database updates are rolled back, as shown in listing 3:

Listing 3. Verify that messages and database updates are rolled back

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

The most important part of the configuration file is the persistence strategy of ActiveMQ, which connects the messaging system and the same data source as business data, and the flag tag of Spring JmsTemplate is used to receive messages. Listing 4 shows how to configure the ActiveMQ persistence policy:

Listing 4. ActiveMQ persistence configuration

Listing 5 shows the flag tag used in Spring JmsTemplate to receive messages:

Listing 5. Setting up the JmsTemplate transaction application

If the sessionTransacted is not true,JMS session transaction API, it cannot be called and the message recipient will not be able to roll back. Most importantly, the embedded agent contains a special async=false parameter and a DataSource package class, which ensures that ActiveMQ has the same transactional JDBC Connection as Spring.

A shared database source can consist of separate individual data sources, especially if these data sources have the same RDBMS platform. Enterprise-level database vendors all provide the notion of synonyms support, and tables can be declared as a synonyms in multiple schema. With this means, JDBC client can agree to Connection transaction management for data distributed on different physical platforms. For example, in a real system, using the ActiveMQ shared resource pattern, it is usually necessary to create the same name for the message and business data.

Performance and JDBCPersistenceAdapter

Some developers in the ActiveMQ community say JDBCPersistenceAdapter can cause performance problems. However, many projects and online systems use ActiveMQ in conjunction with relational databases. In these cases, it is accepted that the log version of the adapter can be used to provide performance, which is, of course, disadvantageous to the shared resource mode, because the log itself is a new transaction resource. However, there are different opinions and opinions on JDBCPersistenceAdapter. Indeed, there is reason to believe that the use of shared resources may improve the performance of log cases. This conclusion comes from research by Sping and a team of AtiveMQ engineers.

Another resource sharing technique for non-messaging scenarios (multiple databases) is to use Oracle databases to link a feature to two database schema at the RDBMS platform level. Perhaps this requires changing the application code, or creating the same name, because the alias of the table points to a linked database that contains the name of the link.

Best effort one-time submission mode

Developers must know that the application of the best effort one-time commit pattern is quite common, but it does not apply in some scenarios. This is a non-XA mode that consists of a single phase commit (single-phase commit) that synchronizes a large number of resources. Participants should be aware of this compromise, and if there is no two-phase commit, the best effort one-time commit mode is not as secure as a XA transaction, but it is also quite good. Many massive data and high-throughput transaction processing systems use the best-effort one-time commit mode to improve performance.

The basic idea is to delay the commit of all resources as much as possible in a single transaction, so that the only things that can go wrong are the underlying components, not business processing errors. Systems that adopt the best effort one-time commit model assume that the underlying components are very unlikely to go wrong, so they can take risks and achieve higher throughput benefits. If the business processing service is also designed as an idempotent, errors are also unlikely to occur. (translator's note: screen equation is a feature of specific operations in mathematics and computer science, and the results will not change after the initialization of the application.)

To help you better understand the failed results of this schema analysis, I will use message-driven database updates as an example to illustrate.

Two resources in this transaction will be counted and calculated. The message transaction starts in front of a database and ends in reverse order. The order of success may be exactly the same as at the beginning of this article:

Open message transaction

Accept the message

Open a database transaction

Update the database

Commit database transaction

Commit message transaction

To be exact, the first four steps in this order are not important, what is important is that the message must be accepted before the database update, and each transaction must be started before the corresponding resource is invoked, so the reasonable order should be as follows:

Open message transaction

Open a database transaction

Accept the message

Update the database

Commit database transaction

Commit message transaction

The key point is that the last two steps are important, and they must be carried out sequentially at the end. The reason why ordering is important is itself a technical problem, but this order needs to be determined by the business. This order tells the developer that one of the transaction resources is special and contains instructions on how to operate on other resources. This is a business order: the system cannot automatically tell where the transaction is going. Even if the message and the database are two resources, transactions often follow this process. Ordering is important because the transaction must handle failure cases. By far the most common failure cases are failed business processes such as bad data, programming errors, and so on. In this case, these two transactions can easily be used to correspond to an exception and roll back. In this way, the completion of the business data is guaranteed, and the timeline is similar to the ideal failure cases listed at the beginning of this article.

The accuracy of the trigger rollback mechanism is not very important, and such a mechanism is better. Most importantly, the commit or rollback must occur in reverse order in the business order in the resource. In one application case, the message transaction must be committed at the end, because the instructions to process the business are included in this resource, because there are few failed cases where the first commit succeeds and the second fails. At this point, all the parts of the design business process have been completed, and the only factor that can cause partial failure may be the underlying problem of message middleware.

Note that if the database resource commit fails, the transaction will eventually be rolled back. So in the non-atomic failure model (failure mode), the first transaction is committed and the second transaction is rolled back. Usually, if there are n resources in a transaction, then there is a failure model such as nMel, which will cause some other resources to be in an inconsistent state after a sub-transaction is rolled back. In the message database use case, the outcome of the failure model is that the message is rolled back, followed by other transactions, even if they are successfully processed; it can be concluded that the worst thing is that duplicate messages (duplicate message) are delivered. What is a repetitive message? In general, the early resources in the transaction are considered to contain messages that contain subsequent resource processing processes, so the result of the failure model can be thought of as duplicate messages.

Some adventurous people think that there is little chance of repetition of news, so they don't bother to predict it. However, in order to be more confident about the accuracy and consistency of business data, it is still necessary to have a clear understanding of this at the business logic level. If you suspect that duplicate messages may occur, you must verify that the business process has processed the data and that nothing has been done before processing the data. This particular description sometimes refers to a business service model such as a curtain (Idempotent Business Service pattern).

Related cases include two examples of synchronous transaction resources using this model, which I will analyze one by one later, as well as some other options.

Spring and message-driven POJO

For the code in the case best-jms-db project, the developer uses the mainstream configuration so that the best effort one-time commit mode can be used. This is done by passing the message to a queue through an asynchronous listener and inserting this data into the database table.

TransactionAwareConnectionFactoryProxy is a storage control of Spring, and it is also the most critical part of this pattern. Instead of using vendor-provided coarse-grained ConnectionFactory,configuration, a ConnectionFactory is wrapped in decorative mode to deal with transaction synchronization problems. The specific configuration is shown in jms-context.xml, as shown in listing 6 below:

Listing 6. Configure a TransactionAwareConnectionFactoryProxy to wrap a vendor-supplied JMS ConnectionFactory

ConnectionFactory does not need to know which transaction manager is synchronized with it, only one transaction is active at a time. These are handled internally by Spring. The transaction driver is done by the DataSourceTransactionManager configured in the data- source-context.xml, and the transaction manager must be monitored by the JMS listener container that polls and accepts messages.

The fooHandler and methods tell the listener container that the specific method of a specific control is called when a message reaches the "asynchronous" queue. Handler is implemented by accepting a String as a parameter message and inserting it into the record as data.

Public void handle (String msg) {jdbcTemplate.update ("INSERT INTO T_FOOS (ID, name, foo_date) values", count.getAndIncrement (), msg, new Date ();}

To simulate the failure, the code uses a FailureSimulator section, which checks whether the message content really failed; as shown in listing 7, before FooHandler ends the transaction, after processing the message, the maybeFail () method is called, so it can affect the outcome of the transaction.

Listing 7. The maybeFail () method

@ AfterReturning ("execution (.. * Handler+.handle (String)) & & args (msg)") public void maybeFail (String msg) {if (msg.contains ("fail")) {if (msg.contains ("partial")) {simulateMessageSystemFailure ();} else {simulateBusinessProcessingFailure ();}

The simulateBusinessProcessingFailure () method throws a DataAccessException, just as database access really fails. Once this method is called, the ideal outcome is that all databases and message transactions can be rolled back. This scenario is tested in the case project AsynchronousMessageTriggerAndRollbackTests unit test.

The simulateMessageSystemFailure () method simulates the message system failure by destroying the underlying JMS Session. The expected result is that the transaction is partially committed: the database is committed, but the message is rolled back. This was verified in the synchronousMessageTriggerAndPartialRollbackTests unit test.

Again, in the AsynchronousMessageTriggerSunnyDayTests class, there is a unit test that all transactions commit successfully.

With the same JMS configuration, the same business logic can also be used in a synchronous environment where messages are received by blocking requests stored in the business logic rather than by the listener container. This method is demonstrated in the best-jms-db case project. Sunny-day case and full transaction rollback are tested in SynchronousMessageTriggerSunnyDayTests and SynchronousMessageTriggerAndRollbackTests, respectively.

Chained transaction manager

In other best effort one-phase commit mode cases, a crude transaction manager implementation simply links a series of other transaction managers together to achieve transaction synchronization. If the business is processed successfully, all transactions will be committed, otherwise they can all be rolled back.

ChainedTransactionManager accepts a series of other transaction managers as injection properties, as shown in listing 8:

Listing 8. Configuration

This simple configuration test simply inserts data into both databases and rolls back while ensuring that both runs are rolled back to the original state. This implementation exists as a unit test in MulipleDataSourceTests, just like the atomikos-db project in the XA case. If the rollback is not synchronized and a transaction is committed, the test fails.

Keep in mind that the order of resources is important, they are nested, and commit or rollback occurs in the reverse order in which they participate. One of the transactions is the most special: if there is a problem, the most important transaction will be rolled back, even if the problem is a resource failure. Similarly, the testInsertWithCheckForDuplicates () test method shows how the screen equality business processing protects the system from partial failures, which is implemented as a business operation defense detection in an inner resource (otherDataSource).

Int count = otherJdbcTemplate.update ("UPDATE T_AUDITS... WHERE id=,...?"); if (count = = 0) {count = otherJdbcTemplate.update ("INSERT into T_AUDITS...",...);}

Update first tries to execute with a where clause, and unsurprisingly, the data from update is inserted into the database. One additional cost of processing the screen equation in this example is the extra query in sunny-day case, which is negligible in a complex business process where multiple queries are executed per transaction.

Other options

The ChainedTransactionManager in this case has the advantage of simplicity, and the extension optimization has been done well. Another way is to use Spring's TransactionSychronization API to register a callback function for the current transaction when the second resource joins. In this way, in the best-jms-db case, the biggest feature is the combination of TransactionAwareConnectionFactory and a DataSourceTransactionManager. With TransactionSynchronizationManager, this particular case can be extended and generalized to resources that contain non-JMS. This theoretically has the advantage that only the resources that join the transaction are supported, not all the resources on the chain. However, the configuration still needs to listen to the resources corresponding to a potential transaction.

Similarly, the team of Spring engineers is considering the best effort one-phase commit transaction manager feature as the core of Spring. You can vote in JJRA issue, and if you like this mode, you want it to be shown in Spring and supported more transparently.

Non-transactional access mode

The non-transactional access mode requires a special business process to make sense. Ideally, sometimes, some of the resources you need to access are marginalized and require no transactions at all. For example, you may need to insert a row of data into an audit table, which is independent and has nothing to do with the success of the business transaction. Just record trying to do something. In a more common scenario, people overestimate how often they need to read and write to one of the resources. in fact, only access is fine. Otherwise, the write operation needs to be well controlled, so if any errors occur, the write operation can be recorded or ignored.

In the above case, the resource properly principles the global transaction, but it still has its own local transaction, which does not need to be synchronized with other events. If you are using Spring, the main transaction is driven by a PlatformTransactionManager, and the edge resource may be a database Connection, which comes from a DataSource that is not controlled by the transaction manager. Each time you access edge resources, you need to set the default environment to autoCommit=true. Updates is invisible to read operations, and the former can occur concurrently with other non-commit transactions, but the impact of write operations is usually immediately visible to other operations.

This model requires more detailed analysis and more confidence to involve business processing, but it is no different from the best effort one-stage commit. When anything goes wrong, a general compensation transaction service is too big for most projects. However, it is common for a simple use case to involve a service, which is a curtain equation that only performs a write operation. These are ideal scenarios for non-transactional strategies.

Wing-and-a-Prayer: an anti-pattern

The last pattern is an anti-pattern that occurs when developers do not understand or realize that they already have a distributed transaction. There is no need to show the call to the underlying resource transaction API, you are not sure whether all resources are in one transaction. If you use a Spring transaction manager instead of JtaTransactionManager, this manager adds a transaction resource to it. This transaction manager will intercept the execution methods that Spring declares transaction management features, such as @ Transactional; other resources will not register with the same transaction. The usual outcome is that everything works fine, but users will soon notice that there is an exception in which one of the resources is not rolled back. A typical error leads to the problem of using a DataSourceTransactionManager and a repository implemented using Hibernate.

Which mode should I use?

I will help you make a choice by analyzing the pros and cons of the model that has been introduced. The first step is to analyze whether your system requires distributed transactions. A necessary but not sufficient condition is that there is a single processing of more than one transaction resource. The sufficient condition is that these resources are all in a separate use case, usually driven by calls from the service layer of the system architecture.

If you don't think this is a distributed transaction, it's best to use Wing-and-a-Prayer mode, and then you'll see that the data should be rolled back but not. Maybe you'll see this effect from failure to its downstream, and it's hard to go back. The use of Wing-and-a-Prayer may also be overlooked by developers who think it is protected by XA but do not actually have anything to do with configuring the underlying resources into the transaction. I once worked on a project where the database was built by other people who turned off XA support during the database installation. There was no problem after running it for a month, and then all kinds of strange failures began to invade the business process, and it took a long time to find the problem.

If it is a simple use case that contains heterogeneous resources, and you can analyze or even do some refactoring, then the non-transactional access mode may be a good choice, especially if one of them is almost read-only, and dual detection ensures write operations. Even if it fails, the data in the non-transactional resource must have meaning in business terminology. Auditing, version control, and even log information can be well cut into this directory, and failures become relatively common-anything in a real transaction can be rolled back at any time, but you need to make sure that there is no negative impact.

For the system, the best effort one-phase commit requires a general failure protection mechanism, but there is not as much overhead as 2PC, and the performance has been greatly improved. Compared with non-transactional resources, its establishment requires more skills, but does not require much analysis, and is usually applied to more general data types. To accomplish some of the features of data consistency, you need to ensure that business processing is an episodic equation for outer resources ("outer" resources: the first committed resource). Message-driven database updates are a perfect example and are well supported in Spring. Unusual scenarios require some additional framework code that will eventually become part of Spring.

The shared resource pattern is a specific example, which usually involves two resources of a specific type and platform, for example, ActiveMQ and any RDBMS or OracleAQ coexist with an Oracle database. The biggest benefit of doing so is considerable flexibility and excellent performance.

Full XA with 2PC is a general model that provides a good worry-free guarantee when dealing with multiple heterogeneous resource transaction failures. The downside is that it is very expensive and needs to follow a specific Igamot O protocol and a specific platform. There are open source JTA implementations that provide a way to get rid of application servers, but most developers still think they are not the best. To be sure, people who spend more time thinking about the transaction boundaries of the system are more likely to use JTA and XA that they don't need so much. At least developers who use Spring do not need to know how transactions are handled in their business logic and do not need to consider the choice of platform for the time being.

The above is what the implementation of XA-style and non-XA-style Spring distributed transactions is like. Have you learned any knowledge or skills? If you want to learn more skills or enrich your knowledge reserve, you are welcome to follow the industry information channel.

Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.

Views: 0

*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.

Share To

Servers

Wechat

© 2024 shulou.com SLNews company. All rights reserved.

12
Report