activemq支持两种事务,本地事务,和分布式事务
public class TestProducer {
public static void main(String[] args) throws Exception {
ConnectionFactory connectionFactory = new ActiveMQConnectionFactory(
"tcp://localhost:61616");
Connection connection = connectionFactory.createConnection();
connection.start();
Session session = connection.createSession(true,
Session.AUTO_ACKNOWLEDGE);
MessageProducer producer = session.createProducer(session.createTopic("testTopic1"));
producer.setDeliveryMode(DeliveryMode.NON_PERSISTENT);
// MessageProducer producer = session.createProducer(session.createQueue("testQueue1"));
for (int i=0;i<3;i++) {
Message message = session.createTextMessage("22223333333hello everybody!");
producer.send(message);
}
producer.close();
session.commit();
session.close();
connection.close();
}
}
ActiveMQSession类doStartTransaction方法
protected void doStartTransaction() throws JMSException {
if (getTransacted() && !transactionContext.isInXATransaction()) {
transactionContext.begin();
}
}
向broker发送事务开始命令
/**
* Start a local transaction.
* @throws javax.jms.JMSException on internal error
*/
public void begin() throws JMSException {
if (isInXATransaction()) {
throw new TransactionInProgressException("Cannot start local transaction. XA transaction is already in progress.");
}
if (transactionId == null) {
synchronizations = null;
beforeEndIndex = 0;
setRollbackOnly(false);
this.transactionId = new LocalTransactionId(getConnectionId(), localTransactionIdGenerator.getNextSequenceId());
TransactionInfo info = new TransactionInfo(getConnectionId(), transactionId, TransactionInfo.BEGIN);
this.connection.ensureConnectionInfoSent();
this.connection.asyncSendPacket(info);
// Notify the listener that the tx was started.
if (localTransactionEventListener != null) {
localTransactionEventListener.beginEvent();
}
LOG.debug("Begin:{}", transactionId);
}
}
然后进入ActiveMQSession里面的send方法想boker发送消息
/**
* Sends the message for dispatch by the broker.
*
* @param producer - message producer.
* @param destination - message destination.
* @param message - message to be sent.
* @param deliveryMode - JMS message delivery mode.
* @param priority - message priority.
* @param timeToLive - message expiration.
* @param producerWindow
* @param onComplete
* @throws JMSException
*/
protected void send(ActiveMQMessageProducer producer, ActiveMQDestination destination, Message message, int deliveryMode, int priority, long timeToLive,
MemoryUsage producerWindow, int sendTimeout, AsyncCallback onComplete) throws JMSException {
checkClosed();
if (destination.isTemporary() && connection.isDeleted(destination)) {
throw new InvalidDestinationException("Cannot publish to a deleted Destination: " + destination);
}
synchronized (sendMutex) {
// tell the Broker we are about to start a new transaction
doStartTransaction();
TransactionId txid = transactionContext.getTransactionId();
long sequenceNumber = producer.getMessageSequence();
//Set the "JMS" header fields on the original message, see 1.1 spec section 3.4.11
message.setJMSDeliveryMode(deliveryMode);
long expiration = 0L;
if (!producer.getDisableMessageTimestamp()) {
long timeStamp = System.currentTimeMillis();
message.setJMSTimestamp(timeStamp);
if (timeToLive > 0) {
expiration = timeToLive + timeStamp;
}
}
message.setJMSExpiration(expiration);
message.setJMSPriority(priority);
message.setJMSRedelivered(false);
// transform to our own message format here
ActiveMQMessage msg = ActiveMQMessageTransformation.transformMessage(message, connection);
msg.setDestination(destination);
msg.setMessageId(new MessageId(producer.getProducerInfo().getProducerId(), sequenceNumber));
// Set the message id.
if (msg != message) {
message.setJMSMessageID(msg.getMessageId().toString());
// Make sure the JMS destination is set on the foreign messages too.
message.setJMSDestination(destination);
}
//clear the brokerPath in case we are re-sending this message
msg.setBrokerPath(null);
msg.setTransactionId(txid);
if (connection.isCopyMessageOnSend()) {
msg = (ActiveMQMessage)msg.copy();
}
msg.setConnection(connection);
msg.onSend();
msg.setProducerId(msg.getMessageId().getProducerId());
if (LOG.isTraceEnabled()) {
LOG.trace(getSessionId() + " sending message: " + msg);
}
if (onComplete==null && sendTimeout <= 0 && !msg.isResponseRequired() && !connection.isAlwaysSyncSend() && (!msg.isPersistent() || connection.isUseAsyncSend() || txid != null)) {
this.connection.asyncSendPacket(msg);
if (producerWindow != null) {
// Since we defer lots of the marshaling till we hit the
// wire, this might not
// provide and accurate size. We may change over to doing
// more aggressive marshaling,
// to get more accurate sizes.. this is more important once
// users start using producer window
// flow control.
int size = msg.getSize();
producerWindow.increaseUsage(size);
}
} else {
if (sendTimeout > 0 && onComplete==null) {
this.connection.syncSendPacket(msg,sendTimeout);
}else {
this.connection.syncSendPacket(msg, onComplete);
}
}
}
}
在ActiveMQSession上调用commint方法
@Override
public void commit() throws JMSException {
checkClosed();
if (!getTransacted()) {
throw new javax.jms.IllegalStateException("Not a transacted session");
}
if (LOG.isDebugEnabled()) {
LOG.debug(getSessionId() + " Transaction Commit :" + transactionContext.getTransactionId());
}
transactionContext.commit();
}
/**
* Commits all work done in this transaction and releases any locks
* currently held.
*
* @throws JMSException if the JMS provider fails to commit the transaction
* due to some internal error.
* @throws javax.jms.IllegalStateException if the method is not called by a
* transacted session.
*/
public void commit() throws JMSException {
if (isInXATransaction()) {
throw new TransactionInProgressException("Cannot commit() if an XA transaction is already in progress ");
}
try {
beforeEnd();
} catch (JMSException e) {
rollback();
throw e;
}
if (transactionId != null && rollbackOnly) {
final String message = "Commit of " + transactionId + " failed due to rollback only request; typically due to failover with pending acks";
try {
rollback();
} finally {
LOG.warn(message);
throw new TransactionRolledBackException(message);
}
}
// Only send commit if the transaction was started.
if (transactionId != null) {
LOG.debug("Commit: {} syncCount: {}",
transactionId, (synchronizations != null ? synchronizations.size() : 0));
TransactionInfo info = new TransactionInfo(getConnectionId(), transactionId, TransactionInfo.COMMIT_ONE_PHASE);
this.transactionId = null;
// Notify the listener that the tx was committed back
try {
this.connection.syncSendPacket(info);
if (localTransactionEventListener != null) {
localTransactionEventListener.commitEvent();
}
afterCommit();
} catch (JMSException cause) {
LOG.info("commit failed for transaction {}", info.getTransactionId(), cause);
if (localTransactionEventListener != null) {
localTransactionEventListener.rollbackEvent();
}
afterRollback();
throw cause;
}
}
}
最后发送COMMIT_ONE_PHASE命令