rocketmq之事物消息

分布式事务常规解决方式有一下几种:

1,采用业务回滚方式:

例如:

	public void updateByPrimaryKey(Pay record) throws Exception {
		 try {
			 //加入在数据库进行了+100
			payMapper.updateByPrimaryKey(record);
			//执行完上步操作后,继续走下面,又去另外一个数据库做修改,结果失败了,那么此时上步如何回滚
		} catch (Exception e) {
			e.printStackTrace();
			//记录一个标示位,如果第一步已经执行成功,那么在此修改回去
			payMapper.updateByPrimaryKey(record);
		}
	}
但是这是回花费大量的代码,而且容易出错.


                 2,采用JTA进行分布式事物的管理   就不做例子了,


                 3,创建中间系统,同事控制数据库,避免两个应用控制两个数据库,进而增加困难度

 4,采用最终一致性消息,来管理分布式事物.


今天给大家推荐的是阿里为淘宝量身定做的RocketMQ消息队列,支持事物消息,顺序消费,批量消费,支持大并发,海量消息.


事务消息原理实现过程:

一阶段:
Producer向Broker发送1条类型为TransactionPreparedType的消息,Broker接收消息保存在CommitLog中,然后返回 消息的queueOffset和MessageId到Producer,MessageId包含有commitLogOffset(即消息在CommitLog中的偏移量, 通过该变量可以直接定位到消息本身),由于该类型的消息在保存的时候,commitLogOffset没有被保存到 consumerQueue中,此时客户端通过consumerQueue取不到commitLogOffset,所以该类型的消息无法被取到,导致 不会被消费。

一阶段的过程中,Broker保存了1条消息。

二阶段:
Producer端的TransactionExecuterImpl执行本地操作,返回本地事务的状态,然后发送一条类型为 TransactionCommitType或者TransactionRollbackType的消息到Broker确认提交或者回滚,Broker通过Request中的 commitLogOffset,获取到上面状态为TransactionPreparedType的消息(简称消息A),然后重新构造一条与消息A内 容相同的消息B,设置状态为TransactionCommitType或者TransactionRollbackType,然后保存。其中 TransactionCommitType类型的,会放commitLogOffset到consumerQueue中,TransactionRollbackType类型的,消 息体设置为空,不会放commitLogOffset到consumerQueue中。



发送事物消息:

	private final String GROUP_NAME = "transaction-pay";
	private final String NAMESRV_ADDR = "192.168.68.129:9876";
	private TransactionMQProducer producer;
	
	public MQProducer() {
		
		this.producer = new TransactionMQProducer(GROUP_NAME);
		this.producer.setNamesrvAddr(NAMESRV_ADDR);	//nameserver服务
		this.producer.setCheckThreadPoolMinSize(5);	// 事务回查最小并发数
		this.producer.setCheckThreadPoolMaxSize(20);	// 事务回查最大并发数
		this.producer.setCheckRequestHoldMax(2000);	// 队列数
		//服务器回调Producer,检查本地事务分支成功还是失败
		this.producer.setTransactionCheckListener(new TransactionCheckListener() {
			public LocalTransactionState checkLocalTransactionState(MessageExt msg) {
				System.out.println("state -- "+ new String(msg.getBody()));
				return LocalTransactionState.COMMIT_MESSAGE;
			}
		});
		try {
			this.producer.start();
		} catch (MQClientException e) {
			e.printStackTrace();
		}	
	}
	
	public QueryResult queryMessage(String topic, String key, int maxNum, long begin, long end) throws Exception {
		return this.producer.queryMessage(topic, key, maxNum, begin, end);
	}
	
	public LocalTransactionState check(MessageExt me){
		LocalTransactionState ls = this.producer.getTransactionCheckListener().checkLocalTransactionState(me);
		return ls;
	}
	
	public void sendTransactionMessage(Message message, LocalTransactionExecuter localTransactionExecuter, Map<String, Object> transactionMapArgs) throws Exception {
		TransactionSendResult tsr = this.producer.sendMessageInTransaction(message, localTransactionExecuter, transactionMapArgs);
		System.out.println("send返回内容:" + tsr.toString());
	}
	
	public void shutdown(){
		Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() {
			public void run() {
				producer.shutdown();
			}
		}));
		System.exit(0);
	}
消费事物消息:


public void queryMessage(String topic) throws MQClientException{
		consumer = new DefaultMQPushConsumer(GROUP_NAME);  
        consumer.setNamesrvAddr(NAMESRV_ADDR);  
        consumer.setConsumeMessageBatchMaxSize(10);  
        /** 
         * 设置Consumer第一次启动是从队列头部开始消费还是队列尾部开始消费<br> 
         * 如果非第一次启动,那么按照上次消费的位置继续消费 
         */  
        consumer.setConsumeFromWhere(ConsumeFromWhere.CONSUME_FROM_FIRST_OFFSET);  
  
        consumer.subscribe(topic, "*");  
  
        consumer.registerMessageListener(new MessageListenerConcurrently() {  
  
            public ConsumeConcurrentlyStatus consumeMessage(List<MessageExt> msgs, ConsumeConcurrentlyContext context) {  
  
                try {  
  
                    for (MessageExt msg : msgs) {  
                        System.out.println(msg + ",内容:" + new String(msg.getBody()));  
                    }  
  
                } catch (Exception e) {  
                    e.printStackTrace();  
  
                    return ConsumeConcurrentlyStatus.RECONSUME_LATER;// 重试  
  
                }  
  
                return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;// 成功  
            }  
        });  
  
        consumer.start();  
  
        System.out.println("transaction_Consumer Started.");    
	}

本地事物执行:

package bhz.mq;

import java.util.Map;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import bhz.entity.Pay;
import bhz.service.PayService;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.rocketmq.client.producer.LocalTransactionExecuter;
import com.alibaba.rocketmq.client.producer.LocalTransactionState;
import com.alibaba.rocketmq.common.message.Message;


/**
 * 执行本地事务,由客户端回调
 */
@Component
public class TransactionExecuterImpl implements LocalTransactionExecuter {
   
	@Autowired
	private PayService payService;
	
	public LocalTransactionState executeLocalTransactionBranch(Message msg, Object arg) {
		try {
			//Message Body
			String a=new String(msg.getBody(), "utf-8");
			JSONObject messageBody = JSON.parseObject(a);
			//Transaction MapArgs
			// --------------------IN PUT---------------------- //
			System.out.println("message body = " + messageBody);
			System.out.println("message tag = " + msg.getTags());
			// --------------------IN PUT---------------------- //
			
			//userid
			Integer userid = messageBody.getInteger("userid");
			//money
			double money =Double.parseDouble(messageBody.getString("money"));
			//mode
			String pay_mode = messageBody.getString("pay_mode");
			//pay
			//Pay pay = payService.selectByPrimaryKey(userid);
			//持久化数据
			Pay pay=new Pay();
			pay.setAmount(11.2);
			pay.setUserid(1);
			payService.updateAmount(pay, pay_mode, money);
			//成功通知MQ消息变更 该消息变为:<确认发送>
			return LocalTransactionState.COMMIT_MESSAGE;
			
		} catch (Exception e) {
			e.printStackTrace();
			//失败则不通知MQ 该消息一直处于:<暂缓发送>
			return LocalTransactionState.ROLLBACK_MESSAGE;
			
		}
		
	}
}

客户端回调:

package bhz.mq;

import java.util.concurrent.atomic.AtomicInteger;

import com.alibaba.rocketmq.client.producer.LocalTransactionState;
import com.alibaba.rocketmq.client.producer.TransactionCheckListener;
import com.alibaba.rocketmq.common.message.MessageExt;

public class TransactionCheckListenerImpl  implements TransactionCheckListener{
	private AtomicInteger transactionIndex = new AtomicInteger(0);
	 
	 
    @Override
    public LocalTransactionState checkLocalTransactionState(MessageExt msg) {
        System.out.println("server checking TrMsg " + msg.toString());
 
        int value = transactionIndex.getAndIncrement();
        if ((value % 6) == 0) {
            throw new RuntimeException("Could not find db");
        }
        else if ((value % 5) == 0) {
            return LocalTransactionState.ROLLBACK_MESSAGE;
        }
        else if ((value % 4) == 0) {
            return LocalTransactionState.COMMIT_MESSAGE;
        }
 
        return LocalTransactionState.UNKNOW;
    }
}

有关rocketmq的顺序消费和filer将在下篇给大家分析

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值