ApsaraMQ for RocketMQ, sıralı mesajları katı bir ilk giren ilk çıkar (FIFO) düzeninde iletir ve tüketir. Aşağıdaki örnek kod, Go için HTTP istemcisi SDK'sı kullanarak sıralı mesajların nasıl gönderileceğini ve tüketileceğini gösterir.
Sıralama türleri
Sıralı mesajlar iki kategoriye ayrılır:
| Tür | Davranış | Ne zaman kullanılır |
|---|---|---|
| Genel olarak sıralı | Bir konudaki tüm mesajlar tek bir FIFO dizisini takip eder. | Her mesajın tam gönderim sırasına göre işlenmesi gerektiğinde; örneğin, ardışık komut yürütme senaryolarında. |
| Bölümlere ayrılmış şekilde sıralı | Mesajlar, parçalama anahtarı kullanılarak bölümler arasında dağıtılır. Her bölüm kendi FIFO sırasını korur. | Yalnızca aynı mantıksal grubu paylaşan mesajların (örneğin aynı sipariş kimliği veya kullanıcı kimliği) katı sıralamaya ihtiyacı olduğu durumlarda. |
Bir parçalama anahtarı, bir mesajın hangi bölüme ait olduğunu belirler. Bu, mesaj anahtarından farklıdır; mesaj anahtarı izleme amacıyla kullanılan bir iş tanımlayıcısıyken parçalama anahtarı sıralamayı kontrol eder.
Daha fazla bilgi için bkz. Sıralı mesajlar.
Sıralamanın çalışma şekli
Sıralama, üretici ile tüketicinin koordinasyonuna bağlıdır.
Gönderen taraf: Mesajları göndermek için tek bir üretici ve tek bir iş parçacığı kullanın. Aracı (broker), o üreticiden aldığı mesajların tam sırasını korur.
Tüketen taraf: Mesajları bölüm bölüm çekmek için ConsumeMessageOrderly yöntemini çağırın. Her bölümde, mevcut toplu işteki her mesaj onaylanana kadar sonraki toplu iş teslim edilmez. Aracı, NextConsumeTime son tarihinden önce bir onay almazsa onaylanmamış mesajı yeniden iletir.
Ön koşullar
Örnek kodu çalıştırmadan önce aşağıdaki kurulumları tamamlayın:
ApsaraMQ for RocketMQ konsolunda bir örnek, konu ve tüketici grubu oluşturun
Alibaba Cloud hesabınız için bir AccessKey çifti oluşturun
Sıralı mesajları gönderme
Aracı, tek bir üreticinin veya iş parçacığının mesajları gönderdiği sıraya göre mesaj sırasını belirler. Birden çok üretici veya iş parçacığı eşzamanlı olarak gönderim yaparsa aracıya varış sırası amaçlanan iş sırasından farklı olabilir. Sıralamayı garanti altına almak için tek bir üretici ve tek bir iş parçacığı kullanarak gönderim yapın.
Aşağıdaki kod, bölümlere ayrılmış şekilde sıralı sekiz mesaj gönderir. Parçalama anahtarı i % 2 mesajları iki bölüm arasında dağıtır; bu sayede çift indeksli mesajlar bir bölüme, tek indeksli mesajlar ise diğerine gider.
Kodu çalıştırmadan önce yer tutucuları değiştirin:
| Yer Tutucu | Açıklama | Örnek |
|---|---|---|
${HTTP_ENDPOINT} |
ApsaraMQ for RocketMQ konsolundaki Instance Details sayfasındaki HTTP uç noktası | http://xxx.mqrest.cn-hangzhou.aliyuncs.com |
${TOPIC} |
Konsolda oluşturulan konu | OrderedTestTopic |
${INSTANCE_ID} |
Örnek kimliği. Örneğin bir ad alanı varsa kimliği belirtin. Yoksa bunu boş bir dize olarak ayarlayın. Ad alanı bilgileri için Instance Details sayfasını kontrol edin. | MQ_INST_xxx |
package main
import (
"fmt"
"time"
"strconv"
"os"
"github.com/aliyunmq/mq-http-go-sdk"
)
func main() {
// HTTP endpoint from the Instance Details page in the ApsaraMQ for RocketMQ console.
endpoint := "${HTTP_ENDPOINT}"
// Set the ALIBABA_CLOUD_ACCESS_KEY_ID and ALIBABA_CLOUD_ACCESS_KEY_SECRET
// environment variables before running this code.
accessKey := os.Getenv("ALIBABA_CLOUD_ACCESS_KEY_ID")
secretKey := os.Getenv("ALIBABA_CLOUD_ACCESS_KEY_SECRET")
// Topic created in the ApsaraMQ for RocketMQ console.
topic := "${TOPIC}"
// Instance ID. If the instance has a namespace, specify the ID.
// If not, set this to an empty string.
instanceId := "${INSTANCE_ID}"
client := mq_http_sdk.NewAliyunMQClient(endpoint, accessKey, secretKey, "")
mqProducer := client.GetProducer(instanceId, topic)
// Send 8 messages across 2 partitions.
for i := 0; i < 8; i++ {
msg := mq_http_sdk.PublishMessageRequest{
MessageBody: "hello mq!",
MessageTag: "",
Properties: map[string]string{},
}
msg.MessageKey = "MessageKey"
msg.Properties["a"] = strconv.Itoa(i)
// Sharding key determines the partition.
// Messages with the same sharding key are delivered in FIFO order.
msg.ShardingKey = strconv.Itoa(i % 2)
ret, err := mqProducer.PublishMessage(msg)
if err != nil {
fmt.Println(err)
return
} else {
fmt.Printf("Publish ---->\n\tMessageId:%s, BodyMD5:%s, \n", ret.MessageId, ret.MessageBodyMD5)
}
time.Sleep(time.Duration(100) * time.Millisecond)
}
}
Sıralı mesajları tüketme
Bu kod, uzun yoklama kullanarak sıralı mesajları tüketir. Tüketici, mesajları bölüm bölüm çeker ve sonraki isteği yapmadan önce her toplu işi onaylar.
Yer tutucuları gerçek değerlerinizle değiştirin. Sıralı mesajları gönderme bölümünde listelenenlere ek olarak aşağıdakileri de belirtin:
| Yer Tutucu | Açıklama | Örnek |
|---|---|---|
${GROUP_ID} |
ApsaraMQ for RocketMQ konsolunda oluşturulan tüketici grubu | GID_OrderedTest |
package main
import (
"fmt"
"github.com/gogap/errors"
"strings"
"time"
"os"
"github.com/aliyunmq/mq-http-go-sdk"
)
func main() {
// HTTP endpoint from the Instance Details page in the ApsaraMQ for RocketMQ console.
endpoint := "${HTTP_ENDPOINT}"
// Set the ALIBABA_CLOUD_ACCESS_KEY_ID and ALIBABA_CLOUD_ACCESS_KEY_SECRET
// environment variables before running this code.
accessKey := os.Getenv("ALIBABA_CLOUD_ACCESS_KEY_ID")
secretKey := os.Getenv("ALIBABA_CLOUD_ACCESS_KEY_SECRET")
// Topic created in the ApsaraMQ for RocketMQ console.
topic := "${TOPIC}"
// Instance ID. If the instance has a namespace, specify the ID.
// If not, set this to an empty string.
instanceId := "${INSTANCE_ID}"
// Consumer group created in the ApsaraMQ for RocketMQ console.
groupId := "${GROUP_ID}"
client := mq_http_sdk.NewAliyunMQClient(endpoint, accessKey, secretKey, "")
mqConsumer := client.GetConsumer(instanceId, topic, groupId, "")
for {
endChan := make(chan int)
respChan := make(chan mq_http_sdk.ConsumeMessageResponse)
errChan := make(chan error)
go func() {
select {
case resp := <-respChan:
{
var handles []string
fmt.Printf("Consume %d messages---->\n", len(resp.Messages))
for _, v := range resp.Messages {
handles = append(handles, v.ReceiptHandle)
fmt.Printf("\tMessageID: %s, PublishTime: %d, MessageTag: %s\n"+
"\tConsumedTimes: %d, FirstConsumeTime: %d, NextConsumeTime: %d\n"+
"\tBody: %s\n"+
"\tProps: %s\n"+
"\tShardingKey: %s\n",
v.MessageId, v.PublishTime, v.MessageTag, v.ConsumedTimes,
v.FirstConsumeTime, v.NextConsumeTime, v.MessageBody, v.Properties, v.ShardingKey)
}
// Acknowledge all messages in this batch.
// If the broker does not receive an ACK before NextConsumeTime,
// it redelivers the message.
ackerr := mqConsumer.AckMessage(handles)
if ackerr != nil {
fmt.Println(ackerr)
if errAckItems, ok := ackerr.(errors.ErrCode).Context()["Detail"].([]mq_http_sdk.ErrAckItem); ok {
for _, errAckItem := range errAckItems {
fmt.Printf("\tErrorHandle:%s, ErrorCode:%s, ErrorMsg:%s\n",
errAckItem.ErrorHandle, errAckItem.ErrorCode, errAckItem.ErrorMsg)
}
} else {
fmt.Println("ack err =", ackerr)
}
time.Sleep(time.Duration(3) * time.Second)
} else {
fmt.Printf("Ack ---->\n\t%s\n", handles)
}
endChan <- 1
}
case err := <-errChan:
{
if strings.Contains(err.(errors.ErrCode).Error(), "MessageNotExist") {
fmt.Println("\nNo new message, continue!")
} else {
fmt.Println(err)
time.Sleep(time.Duration(3) * time.Second)
}
endChan <- 1
}
case <-time.After(35 * time.Second):
{
fmt.Println("Timeout of consumer message ??")
endChan <- 1
}
}
}()
// The consumer pulls partitionally ordered messages partition by partition.
// Within each partition, the next batch is delivered only after all messages
// in the current batch are acknowledged.
// Long polling: if no message is available, the request is held on the broker
// for up to the specified polling duration. The network timeout is 35 seconds.
mqConsumer.ConsumeMessageOrderly(respChan, errChan,
3, // Max messages per batch (up to 16).
3, // Long polling duration in seconds (up to 30).
)
<-endChan
}
}
Temel tüketim davranışları
| Davranış | Ayrıntı |
|---|---|
| Bölüm düzeyinde sıralama | Tüketici aynı anda birden çok bölümden çekim yapabilir ancak her bölümdeki mesajlar her zaman gönderim sırasına göre teslim edilir. |
| Toplu iş onay kapısı | Bir bölümden sonraki toplu iş, önceki toplu işteki her mesaj onaylanana kadar teslim edilmez. |
| Otomatik yeniden iletme | Aracı, NextConsumeTime süresinden önce herhangi bir onay almazsa mesajı yeniden iletir. Her yeniden iletme işlemi, benzersiz bir zaman damgasına sahip yeni bir makbuz tanıtıcısı atar. |
| Uzun yoklama | Mesaj mevcut olmadığında aracı, boş bir yanıt döndürmeden önce belirtilen yoklama süresi (bu örnekte 3 saniye) boyunca isteği bekletir. Ağ zaman aşımı 35 saniyedir. |
Sonraki adımlar
Sıralı mesajlar: Sıralama türleri, garantiler ve tasarım hususları
Go HTTP SDK'sını kullanarak diğer mesaj türlerini gönderme ve alma
Ortamı hazırlama: Go geliştirme ortamını kurma