RocketMQ 5.0 gRPC 协议 Go SDK 使用

RocketMQ5.0 gRPC 协议go客户端使用,gRPC SDK 仅支持版本大于等于5.0的服务端

gRPC SDK和原来的 Remoting SDK的关系,看下面官网截图

目前gRPC 协议客户端还没有完善,go 只有 SimpleConsumer

目录

1、安装RocketMQ5.0

2、 Go SDK 使用

2.1、普通消息

2.2、顺序消息

2.3、延时消息

2.4、事物消息


1、安装RocketMQ5.0

安装过程可以看笔者的《linux 安装 RocketMQ》,这篇文章中使用的是RocketMQ 4.9.4版本,换成5的版本安装依然是可以的,文章地址:https://blog.csdn.net/wsjzzcbq/article/details/125562966

区别是启动方式改变了,增加了 proxy 的启动,下面是启动方式

进入bin目录

先启动 nameserver

nohup sh mqnamesrv &

然后启动 broker

nohup sh mqbroker -n localhost:9876 --enable-proxy &

这里 gRPC 默认的端口是8081,如果想使用其他端口,需要修改conf目录下的 rmq-proxy.json 配置文件,如将 gRPC端口修改为 8086,需要在rmq-proxy.json文件中添加 grpcServerPort": 8086

grpcServerPort": 8086

如下面截图

rmq-proxy.json 文件

添加端口配置

配置完成后,需要在启动 broker的命令上添加配置文件路径 

nohup sh mqbroker -n localhost:9876 -pc /usr/local/rocketmq/rocketmq-all-5.0.0-bin-release/conf/rmq-proxy.json --enable-proxy &

这样启动后,gRPC端口为 8086

同时注意,如果想让客户端连接服务端,要开放防火墙端口

 firewall-cmd --zone=public --add-port=8081/tcp --permanent
 firewall-cmd --reload

查看防火墙端口

firewall-cmd --zone=public --list-ports

笔者使用默认的8081端口

2、 Go SDK 使用

Go版本要求 1.11+

安装 Go SDK

go get -u github.com/apache/rocketmq-clients/golang/v5

创建 topic

在 bin 目录下

笔者创建的 topic 名称为 demo_topic

sh ./mqadmin updatetopic -n localhost:9876 -c DefaultCluster -t demo_topic 

查看 topic

sh ./mqadmin topicList -n localhost:9876

创建消费者组

在bin目录下

笔者创建名称为 demo_group 的消费者组

sh ./mqadmin updateSubGroup -n localhost:9876 -c DefaultCluster -g demo_group

当然,创建 topic 和消费者组通过 RocketMQ Dashboard 更加方便

2.1、普通消息

消费者

package normal

import (
	"context"
	"fmt"
	"github.com/apache/rocketmq-clients/golang/v5"
	"github.com/apache/rocketmq-clients/golang/v5/credentials"
	"log"
	"os"
	"time"
)

var (
	// maximum waiting time for receive func
	awaitDuration = time.Second * 5
	// maximum number of messages received at one time
	maxMessageNum int32 = 16
	// invisibleDuration should > 20s
	invisibleDuration = time.Second * 20
	// receive messages in a loop
)

func Consumer() {
	// log to console
	os.Setenv("mq.consoleAppender.enabled", "true")
	golang.ResetLogger()
	// In most case, you don't need to create many consumers, singleton pattern is more recommended.
	simpleConsumer, err := golang.NewSimpleConsumer(&golang.Config{
		Endpoint:      Endpoint,
		ConsumerGroup: ConsumerGroup,
		Credentials: &credentials.SessionCredentials{
			AccessKey:    AccessKey,
			AccessSecret: SecretKey,
		},
	},
		golang.WithAwaitDuration(awaitDuration),
		golang.WithSubscriptionExpressions(map[string]*golang.FilterExpression{
			Topic: golang.SUB_ALL,
		}),
	)
	if err != nil {
		log.Fatal(err)
	}
	// start simpleConsumer
	err = simpleConsumer.Start()
	if err != nil {
		log.Fatal(err)
	}
	// graceful stop simpleConsumer
	defer simpleConsumer.GracefulStop()

	go func() {
		for {
			fmt.Println("开始接受消息")
			mvs, err := simpleConsumer.Receive(context.TODO(), maxMessageNum, invisibleDuration)
			if err != nil {
				fmt.Println(err)
			}
			fmt.Println(len(mvs))
			// ack message
			for _, mv := range mvs {
				//消息内容
				fmt.Println(string(mv.GetBody()))
				simpleConsumer.Ack(context.TODO(), mv)
				fmt.Println(mv)
			}
			fmt.Println("wait a moment")
			fmt.Println()
			time.Sleep(time.Second * 3)
		}
	}()
	// run for a while
	time.Sleep(time.Minute)
}

生产者

同步发送消息

package normal

import (
	"context"
	"fmt"
	"github.com/apache/rocketmq-clients/golang/v5"
	"github.com/apache/rocketmq-clients/golang/v5/credentials"
	"log"
	"os"
	"strconv"
	"time"
)

const (
	ConsumerGroup = "demo_group"
	Topic         = "demo_topic"
	Endpoint      = "192.168.5.14:8081"
	AccessKey     = "xxxxxx"
	SecretKey     = "xxxxxx"
)

func ProducerStart() {
	os.Setenv("mq.consoleAppender.enabled", "true")
	golang.ResetLogger()

	// In most case, you don't need to create many producers, singleton pattern is more recommended.
	producer, err := golang.NewProducer(&golang.Config{
		Endpoint: Endpoint,
		Credentials: &credentials.SessionCredentials{
			AccessKey:    AccessKey,
			AccessSecret: SecretKey,
		},
	},
		golang.WithTopics(Topic),
	)
	if err != nil {
		log.Fatal(err)
	}
	// start producer
	err = producer.Start()
	if err != nil {
		log.Fatal(err)
	}
	// graceful stop producer
	defer producer.GracefulStop()

	for i := 0; i < 5; i++ {
		// new a message
		msg := &golang.Message{
			Topic: Topic,
			Body:  []byte("消息内容 窗外下着潇潇雨 : " + strconv.Itoa(i+10)),
		}
		// set keys and tag
		msg.SetKeys("a", "b")
		msg.SetTag("ab")
		// send message in sync
		resp, err := producer.Send(context.TODO(), msg)
		if err != nil {
			log.Fatal(err)
		}
		for i := 0; i < len(resp); i++ {
			fmt.Printf("%#v\n", resp[i])
		}
		// wait a moment
		time.Sleep(time.Second * 1)
	}
}

笔者消费者和生产者代码在同一包下,故 ConsumerGroup、Topic、Endpoint都放在了生产者常量里

下面分别创建生产者和消费者的测试代码

package normal

import "testing"

func TestProducerStart(t *testing.T) {
	ProducerStart()
}
package normal

import "testing"

func Test_consumer(t *testing.T) {
	Consumer()
}

工程结构看下图

先启动消费者建立topic和消费者组的绑定关系,再启动生产者发送消息

运行效果

异步发送消息

生成者

package normal

import (
	"context"
	"fmt"
	"github.com/apache/rocketmq-clients/golang/v5"
	"github.com/apache/rocketmq-clients/golang/v5/credentials"
	"log"
	"os"
	"strconv"
	"time"
)

func AsyncProducer() {

	// log to console
	os.Setenv("mq.consoleAppender.enabled", "true")
	golang.ResetLogger()
	// In most case, you don't need to create many producers, singleton pattern is more recommended.
	producer, err := golang.NewProducer(&golang.Config{
		Endpoint: Endpoint,
		Credentials: &credentials.SessionCredentials{
			AccessKey:    AccessKey,
			AccessSecret: SecretKey,
		},
	},
		golang.WithTopics(Topic),
	)
	if err != nil {
		log.Fatal(err)
	}
	// start producer
	err = producer.Start()
	if err != nil {
		log.Fatal(err)
	}
	// graceful stop producer
	defer producer.GracefulStop()
	for i := 0; i < 5; i++ {
		// new a message
		msg := &golang.Message{
			Topic: Topic,
			Body:  []byte("花月两模糊,隔窗看欲无 : " + strconv.Itoa(i)),
		}
		// set keys and tag
		msg.SetKeys("a", "b")
		msg.SetTag("ab")
		//异步发送消息
		producer.SendAsync(context.TODO(), msg, func(ctx context.Context, resp []*golang.SendReceipt, err error) {
			if err != nil {
				log.Fatal(err)
			}
			for i := 0; i < len(resp); i++ {
				fmt.Println("回调")
				fmt.Printf("%#v\n", resp[i])
			}
		})
		// wait a moment
		time.Sleep(time.Second * 1)
	}

	time.Sleep(time.Minute * 1)
}

 测试代码

package normal

import "testing"

func TestAsyncProducer(t *testing.T) {
	AsyncProducer()
}

测试

2.2、顺序消息

Apache RocketMQ 顺序消息的顺序关系通过消息组(MessageGroup)判定和识别,发送顺序消息时需要为每条消息设置归属的消息组,相同消息组的多条消息之间遵循先进先出的顺序关系,不同消息组、无消息组的消息之间不涉及顺序性

顺序消息仅支持使用MessageType为FIFO的主题,即顺序消息只能发送至类型为顺序消息的主题中,发送的消息的类型必须和主题的类型一致

创建顺序消息的 topic

./mqadmin updateTopic -c DefaultCluster -t FIFO_Demo_Topic -o true -n localhost:9876 -a +message.type=FIFO

创建新的消费者组

sh ./mqadmin updateSubGroup -n localhost:9876 -c DefaultCluster -g fifo_demo_group

生产者

package fifo

import (
	"context"
	"fmt"
	"github.com/apache/rocketmq-clients/golang/v5"
	"github.com/apache/rocketmq-clients/golang/v5/credentials"
	"log"
	"os"
	"strconv"
	"time"
)

const (
	ConsumerGroup = "fifo_demo_group"
	Topic         = "FIFO_Demo_Topic"
	Endpoint      = "192.168.5.14:8081"
	AccessKey     = "xxxxxx"
	SecretKey     = "xxxxxx"
)

func FifoProducer() {
	// log to console
	os.Setenv("mq.consoleAppender.enabled", "true")
	golang.ResetLogger()
	// In most case, you don't need to create many producers, singleton pattern is more recommended.
	producer, err := golang.NewProducer(&golang.Config{
		Endpoint: Endpoint,
		Credentials: &credentials.SessionCredentials{
			AccessKey:    AccessKey,
			AccessSecret: SecretKey,
		},
	},
		golang.WithTopics(Topic),
	)
	if err != nil {
		log.Fatal(err)
	}
	// start producer
	err = producer.Start()
	if err != nil {
		log.Fatal(err)
	}
	// graceful stop producer
	defer producer.GracefulStop()
	for i := 0; i < 5; i++ {
		// new a message
		msg := &golang.Message{
			Topic: Topic,
			Body:  []byte("长相思,长相思。若问相思甚了期,除非相见时: " + strconv.Itoa(i)),
		}
		// set keys and tag
		msg.SetKeys("a", "b")
		msg.SetTag("ab")
		msg.SetMessageGroup("fifo")
		// send message in sync
		resp, err := producer.Send(context.TODO(), msg)
		if err != nil {
			log.Fatal(err)
		}
		for i := 0; i < len(resp); i++ {
			fmt.Printf("%#v\n", resp[i])
		}
		// wait a moment
		time.Sleep(time.Second * 1)
	}
}

消费者

package fifo

import (
	"context"
	"fmt"
	"github.com/apache/rocketmq-clients/golang/v5"
	"github.com/apache/rocketmq-clients/golang/v5/credentials"
	"log"
	"os"
	"time"
)

var (
	// maximum waiting time for receive func
	awaitDuration = time.Second * 5
	// maximum number of messages received at one time
	maxMessageNum int32 = 16
	// invisibleDuration should > 20s
	invisibleDuration = time.Second * 20
	// receive messages in a loop
)

func Consumer() {
	// log to console
	os.Setenv("mq.consoleAppender.enabled", "true")
	golang.ResetLogger()
	// In most case, you don't need to create many consumers, singleton pattern is more recommended
	simpleConsumer, err := golang.NewSimpleConsumer(&golang.Config{
		Endpoint:      Endpoint,
		ConsumerGroup: ConsumerGroup,
		Credentials: &credentials.SessionCredentials{
			AccessKey:    AccessKey,
			AccessSecret: SecretKey,
		},
	},
		golang.WithAwaitDuration(awaitDuration),
		golang.WithSubscriptionExpressions(map[string]*golang.FilterExpression{
			Topic: golang.SUB_ALL,
		}),
	)
	if err != nil {
		log.Fatal(err)
	}
	// start simpleConsumer
	err = simpleConsumer.Start()
	if err != nil {
		log.Fatal(err)
	}
	// graceful stop simpleConsumer
	defer simpleConsumer.GracefulStop()

	go func() {
		for {
			fmt.Println("开始接受消息")
			mvs, err := simpleConsumer.Receive(context.TODO(), maxMessageNum, invisibleDuration)
			if err != nil {
				fmt.Println(err)
			}
			fmt.Println(len(mvs))
			// ack message
			for _, mv := range mvs {
				//消息内容
				fmt.Println(string(mv.GetBody()))
				simpleConsumer.Ack(context.TODO(), mv)
				fmt.Println(mv)
			}
			fmt.Println("wait a moment")
			fmt.Println()
			time.Sleep(time.Second * 3)
		}
	}()
	// run for a while
	time.Sleep(time.Minute)
}

测试代码

package fifo

import "testing"

func TestFifoProducer(t *testing.T) {
	FifoProducer()
}
package fifo

import "testing"

func TestConsumer(t *testing.T) {
	Consumer()
}

运行效果

2.3、延时消息

定时消息仅支持在 MessageType为Delay 的主题内使用,即定时消息只能发送至类型为定时消息的主题中,发送的消息的类型必须和主题的类型一致

创建延迟topic

./mqadmin updateTopic -c DefaultCluster -t Delay_Demo_Topic -n localhost:9876 -a +message.type=DELAY

查看topic

sh ./mqadmin topicList -n localhost:9876

创建新的消费者组

sh ./mqadmin updateSubGroup -n localhost:9876 -c DefaultCluster -g delay_demo_group

生产者

package delay

import (
	"context"
	"fmt"
	"github.com/apache/rocketmq-clients/golang/v5"
	"github.com/apache/rocketmq-clients/golang/v5/credentials"
	"log"
	"os"
	"strconv"
	"time"
)

const (
	ConsumerGroup = "delay_demo_group"
	Topic         = "Delay_Demo_Topic"
	Endpoint      = "192.168.5.14:8081"
	AccessKey     = "xxxxxx"
	SecretKey     = "xxxxxx"
)

func DelayProducer() {
	// log to console
	os.Setenv("mq.consoleAppender.enabled", "true")
	golang.ResetLogger()
	// In most case, you don't need to create many producers, singleton pattern is more recommended.
	producer, err := golang.NewProducer(&golang.Config{
		Endpoint: Endpoint,
		Credentials: &credentials.SessionCredentials{
			AccessKey:    AccessKey,
			AccessSecret: SecretKey,
		},
	},
		golang.WithTopics(Topic),
	)
	if err != nil {
		log.Fatal(err)
	}
	// start producer
	err = producer.Start()
	if err != nil {
		log.Fatal(err)
	}
	// graceful stop producer
	defer producer.GracefulStop()
	for i := 0; i < 1; i++ {
		// new a message
		msg := &golang.Message{
			Topic: Topic,
			Body:  []byte("长相思,长相思。欲把相思说似谁,浅情人不知 : " + strconv.Itoa(i)),
		}
		// set keys and tag
		msg.SetKeys("a", "b")
		msg.SetTag("ab")
		// 设置延时时间 5 秒钟
		msg.SetDelayTimestamp(time.Now().Add(time.Second * 10))
		// send message in sync
		resp, err := producer.Send(context.TODO(), msg)
		log.Println("延时消息发送完成")
		if err != nil {
			log.Fatal(err)
		}
		for i := 0; i < len(resp); i++ {
			fmt.Printf("%#v\n", resp[i])
		}
		// wait a moment
		time.Sleep(time.Second * 1)
	}
}

消费者

package delay

import (
	"context"
	"fmt"
	"github.com/apache/rocketmq-clients/golang/v5"
	"github.com/apache/rocketmq-clients/golang/v5/credentials"
	"log"
	"os"
	"time"
)

var (
	// maximum waiting time for receive func
	awaitDuration = time.Second * 1
	// maximum number of messages received at one time
	maxMessageNum int32 = 16
	// invisibleDuration should > 20s
	invisibleDuration = time.Second * 10
	// receive messages in a loop
)

func Consumer() {
	// log to console
	os.Setenv("mq.consoleAppender.enabled", "true")
	golang.ResetLogger()
	// In most case, you don't need to create many consumers, singleton pattern is more recommended
	simpleConsumer, err := golang.NewSimpleConsumer(&golang.Config{
		Endpoint:      Endpoint,
		ConsumerGroup: ConsumerGroup,
		Credentials: &credentials.SessionCredentials{
			AccessKey:    AccessKey,
			AccessSecret: SecretKey,
		},
	},
		golang.WithAwaitDuration(awaitDuration),
		golang.WithSubscriptionExpressions(map[string]*golang.FilterExpression{
			Topic: golang.SUB_ALL,
		}),
	)
	if err != nil {
		log.Fatal(err)
	}
	// start simpleConsumer
	err = simpleConsumer.Start()
	if err != nil {
		log.Fatal(err)
	}
	// graceful stop simpleConsumer
	defer simpleConsumer.GracefulStop()

	go func() {
		for {
			mvs, err := simpleConsumer.Receive(context.TODO(), maxMessageNum, invisibleDuration)
			if err != nil {
				fmt.Println(err)
			}
			// ack message
			for _, mv := range mvs {
				//消息内容
				log.Println(string(mv.GetBody()))
				simpleConsumer.Ack(context.TODO(), mv)
			}
			//fmt.Println("wait a moment")
			//fmt.Println()
			//time.Sleep(time.Second * 3)
		}
	}()
	// run for a while
	time.Sleep(time.Minute)
}

测试代码

package delay

import "testing"

func TestDelayProducer(t *testing.T) {
	DelayProducer()
}
package delay

import "testing"

func TestConsumer(t *testing.T) {
	Consumer()
}

测试

因为SimpleConsumer有20秒的 invisibleDuration,因此打印出的延时时间是30秒

2.4、事物消息

创建事物topic

./mqadmin updatetopic -n localhost:9876 -t Transaction_Demo_Topic -c DefaultCluster -a +message.type=TRANSACTION

查看topic

sh ./mqadmin topicList -n localhost:9876

创建消费者组

sh ./mqadmin updateSubGroup -n localhost:9876 -c DefaultCluster -g transaction_demo_group

生产者

3、事物消息

创建事物topic

./mqadmin updatetopic -n localhost:9876 -t Transaction_Demo_Topic -c DefaultCluster -a +message.type=TRANSACTION

查看topic

sh ./mqadmin topicList -n localhost:9876

创建新的消费者组

sh ./mqadmin updateSubGroup -n localhost:9876 -c DefaultCluster -g transaction_demo_group

生产者

事物相关内容已经写在代码里

package transaction

import (
	"context"
	"fmt"
	"github.com/apache/rocketmq-clients/golang/v5"
	"github.com/apache/rocketmq-clients/golang/v5/credentials"
	"log"
	"os"
	"strconv"
	"time"
)

const (
	ConsumerGroup = "transaction_demo_group"
	Topic         = "Transaction_Demo_Topic"
	Endpoint      = "192.168.5.14:8081"
	AccessKey     = "xxxxxx"
	SecretKey     = "xxxxxx"
)

func TransactionProducer() {
	// log to console
	os.Setenv("mq.consoleAppender.enabled", "true")
	golang.ResetLogger()
	// In most case, you don't need to create many producers, singleton pattern is more recommended.
	producer, err := golang.NewProducer(&golang.Config{
		Endpoint: Endpoint,
		Credentials: &credentials.SessionCredentials{
			AccessKey:    AccessKey,
			AccessSecret: SecretKey,
		},
	},
		golang.WithTransactionChecker(&golang.TransactionChecker{
			Check: func(msg *golang.MessageView) golang.TransactionResolution {
				fmt.Println("检查本地事物是否已经提交")
				log.Printf("check transaction message: %v", msg)
				return golang.COMMIT
			},
		}),
		golang.WithTopics(Topic),
	)
	if err != nil {
		log.Fatal(err)
	}
	// start producer
	err = producer.Start()
	if err != nil {
		log.Fatal(err)
	}
	// graceful stop producer
	defer producer.GracefulStop()
	for i := 0; i < 1; i++ {
		// new a message
		msg := &golang.Message{
			Topic: Topic,
			Body:  []byte("相思只在,丁香枝上,豆蔻梢头 : " + strconv.Itoa(i)),
		}
		// set keys and tag
		msg.SetKeys("a", "b")
		msg.SetTag("ab")
		// send message in sync
		transaction := producer.BeginTransaction()
		resp, err := producer.SendWithTransaction(context.TODO(), msg, transaction)
		if err != nil {
			log.Fatal(err)
		}
		for i := 0; i < len(resp); i++ {
			fmt.Printf("%#v\n", resp[i])
		}
		// 提交本地事物,如果这里调用RollBack则消息会被取消,消费者不能消费
		//如果调用Commit则消息会被消费者消费
		//如果什么都没调用,相当于UNKNOWN,rocektmq会回调上面 TransactionChecker判断事物状态
		//err = transaction.RollBack()
		err = transaction.Commit()
		if err != nil {
			log.Fatal(err)
		}
		// wait a moment
		time.Sleep(time.Second * 1)
	}

	time.Sleep(time.Minute)
}

消费者

package transaction

import (
	"context"
	"fmt"
	"github.com/apache/rocketmq-clients/golang/v5"
	"github.com/apache/rocketmq-clients/golang/v5/credentials"
	"log"
	"os"
	"time"
)

var (
	// maximum waiting time for receive func
	awaitDuration = time.Second * 1
	// maximum number of messages received at one time
	maxMessageNum int32 = 16
	// invisibleDuration should > 20s
	invisibleDuration = time.Second * 10
	// receive messages in a loop
)

func Consumer() {
	// log to console
	os.Setenv("mq.consoleAppender.enabled", "true")
	golang.ResetLogger()
	// In most case, you don't need to create many consumers, singleton pattern is more recommended
	simpleConsumer, err := golang.NewSimpleConsumer(&golang.Config{
		Endpoint:      Endpoint,
		ConsumerGroup: ConsumerGroup,
		Credentials: &credentials.SessionCredentials{
			AccessKey:    AccessKey,
			AccessSecret: SecretKey,
		},
	},
		golang.WithAwaitDuration(awaitDuration),
		golang.WithSubscriptionExpressions(map[string]*golang.FilterExpression{
			Topic: golang.SUB_ALL,
		}),
	)
	if err != nil {
		log.Fatal(err)
	}
	// start simpleConsumer
	err = simpleConsumer.Start()
	if err != nil {
		log.Fatal(err)
	}
	// graceful stop simpleConsumer
	defer simpleConsumer.GracefulStop()

	go func() {
		for {
			mvs, err := simpleConsumer.Receive(context.TODO(), maxMessageNum, invisibleDuration)
			if err != nil {
				fmt.Println(err)
			}
			// ack message
			for _, mv := range mvs {
				//消息内容
				log.Println(string(mv.GetBody()))
				simpleConsumer.Ack(context.TODO(), mv)
			}
			//fmt.Println("wait a moment")
			//fmt.Println()
			//time.Sleep(time.Second * 3)
		}
	}()
	// run for a while
	time.Sleep(time.Minute)
}

测试代码

package transaction

import "testing"

func TestTransactionProducer(t *testing.T) {
	TransactionProducer()
}
package transaction

import "testing"

func TestConsumer(t *testing.T) {
	Consumer()
}

测试

这里测试本地事物正常提交的情况

下面测试本地事物是未知,rocketmq回调生成者方法检查的情况

修改生成者代码

将事物提交部分注释掉

package transaction

import (
	"context"
	"fmt"
	"github.com/apache/rocketmq-clients/golang/v5"
	"github.com/apache/rocketmq-clients/golang/v5/credentials"
	"log"
	"os"
	"strconv"
	"time"
)

const (
	ConsumerGroup = "transaction_demo_group"
	Topic         = "Transaction_Demo_Topic"
	Endpoint      = "192.168.5.14:8081"
	AccessKey     = "xxxxxx"
	SecretKey     = "xxxxxx"
)

func TransactionProducer() {
	// log to console
	os.Setenv("mq.consoleAppender.enabled", "true")
	golang.ResetLogger()
	// In most case, you don't need to create many producers, singleton pattern is more recommended.
	producer, err := golang.NewProducer(&golang.Config{
		Endpoint: Endpoint,
		Credentials: &credentials.SessionCredentials{
			AccessKey:    AccessKey,
			AccessSecret: SecretKey,
		},
	},
		golang.WithTransactionChecker(&golang.TransactionChecker{
			Check: func(msg *golang.MessageView) golang.TransactionResolution {
				fmt.Println("检查本地事物是否已经提交")
				log.Printf("check transaction message: %v", msg)
				return golang.COMMIT
			},
		}),
		golang.WithTopics(Topic),
	)
	if err != nil {
		log.Fatal(err)
	}
	// start producer
	err = producer.Start()
	if err != nil {
		log.Fatal(err)
	}
	// graceful stop producer
	defer producer.GracefulStop()
	for i := 0; i < 1; i++ {
		// new a message
		msg := &golang.Message{
			Topic: Topic,
			Body:  []byte("相思只在,丁香枝上,豆蔻梢头 : " + strconv.Itoa(i)),
		}
		// set keys and tag
		msg.SetKeys("a", "b")
		msg.SetTag("ab")
		// send message in sync
		transaction := producer.BeginTransaction()
		resp, err := producer.SendWithTransaction(context.TODO(), msg, transaction)
		if err != nil {
			log.Fatal(err)
		}
		for i := 0; i < len(resp); i++ {
			fmt.Printf("%#v\n", resp[i])
		}
		// 提交本地事物,如果这里调用RollBack则消息会被取消,消费者不能消费
		//如果调用Commit则消息会被消费者消费
		//如果什么都没调用,相当于UNKNOWN,rocektmq会回调上面 TransactionChecker判断事物状态
		//err = transaction.RollBack()
		//err = transaction.Commit()
		//if err != nil {
		//	log.Fatal(err)
		//}
		// wait a moment
		time.Sleep(time.Second * 1)
	}

	time.Sleep(time.Minute)
}

测试

至此完

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

悟世君子

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值