Go RocketMQ事务消息的使用

RocketMQ事务消息

可以认为是一个两阶段提交的消息,来确保分布式系统的最终一致性。事务性消息保证了本地事务的执行和消息的发送能够以原子的方式进行。

事务状态

RocketMQ事务消息有三种状态

  • TransactionStatus.CommitTransaction:提交事务,它意味着允许消费者使用此消息。在go client中对应 primitive.CommitMessageState (本文所指go client是github.com/apache/rocketmq-client-go/v2)
  • TransactionStatus.RollbackTransaction:回滚事务,这意味着将删除消息,并且不允许使用该消息。在go client对应primitive.RollbackMessageState。
  • TransactionStatus.Unknown:未知:中间状态,这意味着需要MQ进行回检以确定状态。在go client对应primitive.UnknowState。

RocketMQ事务消息相关的配置

  • transactionTimeout 在指定时间内没有收到最终确认就引发回查。
  • transactionCheckMax 指定最多回查指定次数,超过后将消息丢弃并记录错误日志,默认15次。
  • transactionCheckInterval 指定设置的多次消息回查的时间间隔,默认60秒。

RocketMQ事务消息的创建

首先创建一个事务消息的producter,然后将事务监听器TransactionListener注册到这个producter上。
事务监听器必须实现两个接口:

  • executeLocalTransaction 用于在发送half message成功时执行本地事务,这个方法的返回值就是上面说的事务状态,根据事务状态决定MQ的后续操作。
  • checkLocalTransaction 用于回查状态,返回结果也是上面说的事务状态。

基于rocketmq-client-go实现RocketMQ事务消息

生产者代码,这里发生4个消息,有2个会进入回查。最终成功两条。

//事务监听器
type Trans struct {
	localTrans       *sync.Map
	transactionIndex int32
}

func NewTrans() *Trans {
	return &Trans{
		localTrans: new(sync.Map),
	}
}

func (tr *Trans) ExecuteLocalTransaction(msg *primitive.Message) primitive.LocalTransactionState {
	switch msg.GetTags() {
	case "taga":
		return primitive.CommitMessageState
	case "tagb":
		return primitive.RollbackMessageState
	case "tagc","tagd":
		return primitive.UnknowState
	}
	return primitive.UnknowState
}

func (tr *Trans) CheckLocalTransaction(msg *primitive.MessageExt) primitive.LocalTransactionState {
	switch msg.GetTags() {
	case "tagc":
		log.Printf("check commit : %v\n", string(msg.Body) )
		return primitive.CommitMessageState
	case "tagd":
		log.Printf("check rollback: %v\n", string(msg.Body))
		return primitive.RollbackMessageState
	}
	return primitive.RollbackMessageState
}

func main() {
	p, _ := rocketmq.NewTransactionProducer(
		NewTrans(),
		producer.WithNsResolver(primitive.NewPassthroughResolver([]string{"127.0.0.1:9876"})),
		producer.WithRetry(1),
	)
	err := p.Start()
	defer p.Shutdown()
	if err != nil {
		log.Printf("start producer error: %s\n", err.Error())
		os.Exit(1)
	}
	tags := [4]string{"taga","tagb","tagc","tagd"}
	topic := "trans"
	for i := 0; i < 4; i++ {
		msg := primitive.NewMessage(topic, []byte("Hello RocketMQ"+strconv.Itoa(i)))
		msg.WithTag(tags[i])
		res, err := p.SendMessageInTransaction(context.Background(), msg)
		if err != nil {
			log.Printf(err.Error())
			return
		} else {
			log.Printf("预投递成功 resid : %s \n",res.MsgID)
		}
	}
	time.Sleep(20 * time.Minute)
}
/**
*
2022/01/25 12:57:00 预投递成功 resid : C0A80165CBC0000000007e5ffd600001
2022/01/25 12:57:00 预投递成功 resid : C0A80165CBC0000000007e5ffd600002
2022/01/25 12:57:00 预投递成功 resid : C0A80165CBC0000000007e5ffd600003
2022/01/25 12:57:00 预投递成功 resid : C0A80165CBC0000000007e5ffd600004
2022/01/25 12:57:07 check rollback: Hello RocketMQ3
2022/01/25 12:57:07 check commit : Hello RocketMQ2
*/

消费者代码

package main

import (
	"context"
	"github.com/apache/rocketmq-client-go/v2"
	"github.com/apache/rocketmq-client-go/v2/consumer"
	"github.com/apache/rocketmq-client-go/v2/primitive"
	"os"
	"testing"
	"time"
	"log"
)

func main() {
	c, _ := rocketmq.NewPushConsumer(
		consumer.WithGroupName("testGroup"),
		consumer.WithNsResolver(primitive.NewPassthroughResolver([]string{"127.0.0.1:9876"})),
	)
	topic := "trans"

	err := c.Subscribe(topic, consumer.MessageSelector{}, func(ctx context.Context,
		msgs ...*primitive.MessageExt) (consumer.ConsumeResult, error) {
		for i := range msgs {
			log.Printf("订阅到消息: body=%v, tag =%v \n", string(msgs[i].Body),msgs[i].GetTags())
		}
		return consumer.ConsumeSuccess, nil
	})
	if err != nil {
		log.Printf(err.Error())
	}
	err = c.Start()
	defer c.Shutdown()
	if err != nil {
		log.Printf(err.Error())
		os.Exit(-1)
	}
	time.Sleep(20*time.Minute)
}
/**
2022/01/25 12:57:00 订阅到消息: body=Hello RocketMQ0, tag =taga
2022/01/25 12:57:07 订阅到消息: body=Hello RocketMQ2, tag =tagc
**/
  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值