Ausführliche Erklärung von Pulsar 4 – Erweiterte Funktionen von Pulsar (Beispiel: Go-Sprache)

Ausführliche Erläuterung des Pulsar-Indexverzeichnisses

1. Partitionsthema

Kurzbeschreibung:
Pulsar ist ein verteiltes Nachrichtensystem, das eine zuverlässige Zustellung von Nachrichten zwischen verschiedenen Knoten und Clustern ermöglicht.
Partitionierte Themen sind eine der wichtigsten Funktionen in Pulsar, die zur Verbesserung der Skalierbarkeit und Leistung verwendet werden.

1.1 Was ist ein Partitionsthema?

In Pulsar kann ein Thema in mehrere Partitionen unterteilt werden. Jede Partition ist eine unabhängige Nachrichtenwarteschlange mit eigenem Nachrichtenspeicher- und -verarbeitungsmechanismus. Partitionierte Themen ermöglichen eine bessere Parallelverarbeitung und einen besseren Lastausgleich zwischen Erzeugern und Verbrauchern von Nachrichten.

1.2 Partitionsstrategie

Partitionierungsrichtlinien sind Regeln, die bestimmen, wie Nachrichten an verschiedene Partitionen verteilt werden. Pulsar bietet mehrere integrierte Partitionierungsstrategien und unterstützt auch benutzerdefinierte Partitionierungsstrategien.

Hier sind einige gängige integrierte Partitionierungsstrategien:

  • Round-Robin-Partitionierungsstrategie: Verteilen Sie Nachrichten nacheinander auf verschiedene Partitionen, um eine gleichmäßige Verteilung der Nachrichten sicherzustellen.

  • Hash-Partitionierungsstrategie: Durch die Verwendung eines Hash-Werts des Nachrichteninhalts zur Bestimmung der Partition, zu der eine Nachricht gehört, wird sichergestellt, dass eine bestimmte Nachricht immer in dieselbe Partition gelangt.

  • Bereichspartitionierungsstrategie: Ordnen Sie Nachrichten basierend auf ihrem Schlüsselwertbereich verschiedenen Partitionen zu. Dies ist sehr nützlich für die Abfrage von Nachrichten nach Bereich.

1.3 Partitionierung und Leistung

Die durch Partitionsthemen erzielten Leistungsverbesserungen spiegeln sich in den folgenden Aspekten wider:

  • Parallelität: Ein höherer Grad an Parallelität kann durch die Aufteilung des Themas in mehrere Partitionen erreicht werden. Mehrere Produzenten können parallel Nachrichten an verschiedene Partitionen senden, und mehrere Konsumenten können parallel Nachrichten von verschiedenen Partitionen empfangen.

  • Lastausgleich: Partitionierte Themen ermöglichen eine gleichmäßige Verteilung von Nachrichten auf verschiedene Partitionen, was zu einem besseren Lastausgleich führt. Dies ist wichtig, wenn Pulsar-Instanzen horizontal in einem Cluster skaliert werden.

1.4 Beispiel

Ein Beispiel soll die Verwendung von Partitionsthemen veranschaulichen:

package main

import (
	"context"
	"fmt"
	"github.com/apache/pulsar/pulsar-client-go/pulsar"
)

func main() {
    
    
	client, err := pulsar.NewClient(pulsar.ClientOptions{
    
    
		URL: "pulsar://localhost:6650",
	})
	if err != nil {
    
    
		fmt.Println("Error creating Pulsar client:", err)
		return
	}
	defer client.Close()

	topic := "my-partitioned-topic"
	producer, err := client.CreateProducer(pulsar.ProducerOptions{
    
    
		Topic:          topic,
		Partitioning:   pulsar.HashingSchemeMurmur3,
		MessageRouting: pulsar.MessageRoutingModeCustomPartition,
	})
	if err != nil {
    
    
		fmt.Println("Error creating producer:", err)
		return
	}
	defer producer.Close()

	// 生产消息
	for i := 0; i < 10; i++ {
    
    
		message := &pulsar.ProducerMessage{
    
    
			Payload: []byte(fmt.Sprintf("Message %d", i)),
		}
		_, err := producer.Send(context.Background(), message)
		if err != nil {
    
    
			fmt.Println("Error sending message:", err)
			return
		}
		fmt.Printf("Produced: %s\n", message.Payload)
	}

	// 消费消息
	consumer, err := client.Subscribe(pulsar.ConsumerOptions{
    
    
		Topic:            topic,
		SubscriptionName: "my-subscription",
		SubscriptionType: pulsar.Shared,
	})
	if err != nil {
    
    
		fmt.Println("Error creating consumer:", err)
		return
	}
	defer consumer.Close()

	for i := 0; i < 10; i++ {
    
    
		msg, err := consumer.Receive(context.Background())
		if err != nil {
    
    
			fmt.Println("Error receiving message:", err)
			return
		}
		fmt.Printf("Consumed: %s\n", msg.Payload())
		consumer.Ack(msg)
	}
}

In diesem Beispiel erstellen wir ein partitioniertes Thema und senden Nachrichten über eine Hash-Partitionierungsstrategie an verschiedene Partitionen. Anschließend erstellen wir einen Verbraucher, der diese Nachrichten empfängt. Dadurch wird sichergestellt, dass Nachrichten in einer verteilten Umgebung gleichmäßig produziert und konsumiert werden.

2. Verspätete Nachricht

Verzögerte Nachrichten ermöglichen eine Verzögerung der Zustellung nach dem Senden einer Nachricht. Dies ist sehr nützlich für die Verarbeitung von Aufgaben, deren Verarbeitung Zeit in Anspruch nimmt, oder von Ereignissen, die zu einem bestimmten Zeitpunkt ausgelöst werden.

2.1 Konfiguration verzögerter Nachrichten

Pulsar bietet eine flexible Konfiguration von Verzögerungsnachrichten und die Verzögerungszeit kann entsprechend den spezifischen Anforderungen eingestellt werden.

Hier sind einige wichtige Konfigurationsparameter:

  • Verzögerungszeit: Definiert in Millisekunden, wie lange eine Nachricht nach dem Senden warten muss. Je nach Geschäftsanforderungen können unterschiedliche Verzögerungszeiten eingestellt werden.

  • Verzögerte Nachrichtenspeicherung: Verzögerte Nachrichten erfordern normalerweise einen separaten Speichermechanismus, um sicherzustellen, dass sie rechtzeitig ausgelöst werden. Pulsar speichert verzögerte Nachrichten in einem dedizierten verzögerten Speicher.

2.2 Nutzungsszenarien verzögerter Nachrichten

Verzögerte Nachrichten können in verschiedenen Szenarien verwendet werden. Zu den typischen Anwendungsfällen gehören:

  • Geplante Aufgaben: Durch Festlegen einer Verzögerungszeit können Sie geplante Triggeraufgaben implementieren, z. B. die Ausführung einer bestimmten Geschäftslogik oder das Senden von Benachrichtigungen in geplanten Intervallen.

  • Wiederholungsmechanismus: Bei der Behandlung fehlgeschlagener Aufgaben kann ein einfacher Wiederholungsmechanismus implementiert werden, indem Nachrichten verzögert werden. Wenn eine Aufgabe fehlschlägt, kann die Nachricht so eingestellt werden, dass das erneute Senden verzögert wird, sodass sie zu einem späteren Zeitpunkt erneut versucht werden kann.

  • Planungssystem: Verzögerungsnachrichten können auch zum Aufbau eines einfachen Planungssystems verwendet werden, um die Ausführung von Aufgaben durch Festlegen einer Verzögerungszeit zu planen.

2.3 Beispiel

Lassen Sie uns die Verwendung verzögerter Nachrichten anhand eines einfachen Go-Sprachcodebeispiels veranschaulichen:

package main

import (
	"context"
	"fmt"
	"github.com/apache/pulsar/pulsar-client-go/pulsar"
	"time"
)

func main() {
    
    
	client, err := pulsar.NewClient(pulsar.ClientOptions{
    
    
		URL: "pulsar://localhost:6650",
	})
	if err != nil {
    
    
		fmt.Println("Error creating Pulsar client:", err)
		return
	}
	defer client.Close()

	topic := "my-delayed-topic"
	producer, err := client.CreateProducer(pulsar.ProducerOptions{
    
    
		Topic: topic,
	})
	if err != nil {
    
    
		fmt.Println("Error creating producer:", err)
		return
	}
	defer producer.Close()

	// 发送延迟消息
	message := &pulsar.ProducerMessage{
    
    
		Payload: []byte("Delayed Message"),
	}
	message.SetDeliverAfter(time.Second * 30) // 设置30秒的延迟时间

	_, err = producer.Send(context.Background(), message)
	if err != nil {
    
    
		fmt.Println("Error sending delayed message:", err)
		return
	}
	fmt.Printf("Sent delayed message with ID: %s\n", message.ID())
}

In diesem Beispiel erstellen wir ein Thema und verwenden einen Produzenten, um eine verzögerte Nachricht zu senden, die 30 Sekunden nach dem Senden zugestellt wird. Dadurch können einige Szenarien simuliert werden, die eine verzögerte Verarbeitung erfordern.

3. Nachrichtenkomprimierung

Durch die Nachrichtenkomprimierung kann die Datengröße während der Nachrichtenübermittlung reduziert werden, wodurch die Netzwerkbandbreitennutzung reduziert und die Übertragungseffizienz verbessert wird.

Pulsar unterstützt mehrere Komprimierungsalgorithmen und kann entsprechend den spezifischen Anforderungen konfiguriert werden.

3.1 Unterstützte Komprimierungsalgorithmen

Pulsar unterstützt eine Vielzahl gängiger Komprimierungsalgorithmen, darunter unter anderem:

  • LZ4: Ein schneller Komprimierungsalgorithmus mit niedrigem Komprimierungsverhältnis, aber sehr hoher Geschwindigkeit, geeignet für den Einsatz in Szenarien, die eine schnelle Übertragung erfordern.

  • Snappy: Ähnlich wie LZ4 ist Snappy ein schneller Komprimierungsalgorithmus, der sich ideal für Umgebungen mit begrenzten CPU-Ressourcen eignet.

  • Zstandard: Ein Algorithmus mit hoher Komprimierungsrate und mittlerer Geschwindigkeit, geeignet für Szenarien mit relativ ausreichenden Bandbreitenressourcen.

3.2 Leistungseinfluss der Komprimierung

Das Komprimieren von Nachrichten kann die Größe der Datenübertragung bis zu einem gewissen Grad reduzieren, führt jedoch auch zu einem gewissen Leistungsaufwand, insbesondere während des Komprimierungs- und Dekomprimierungsprozesses auf der Produzenten- und Verbraucherseite. Daher gibt es bei der Verwendung der Nachrichtenkomprimierung einen Kompromiss zwischen Komprimierungsverhältnis und Leistungsaufwand.

Zu den Hauptaspekten, die sich auf die Komprimierungsleistung auswirken, gehören:

  • CPU-Ressourcenverbrauch: Der Komprimierungs- und Dekomprimierungsprozess erfordert bestimmte CPU-Ressourcen. In Produzenten- und Konsumentenszenarien mit hohem Durchsatz muss die CPU-Auslastung des Systems berücksichtigt werden.

  • Erhöhte Latenz: Komprimierung und Dekomprimierung führen zu einer gewissen Latenz. Bei latenzempfindlichen Anwendungen müssen Sie sorgfältig entscheiden, ob Sie die Nachrichtenkomprimierung aktivieren möchten.

3.3 Beispiel

Beispiel für die Verwendung der Nachrichtenkomprimierung in Pulsar:

package main

import (
	"context"
	"fmt"
	"github.com/apache/pulsar/pulsar-client-go/pulsar"
)

func main() {
    
    
	client, err := pulsar.NewClient(pulsar.ClientOptions{
    
    
		URL: "pulsar://localhost:6650",
	})
	if err != nil {
    
    
		fmt.Println("Error creating Pulsar client:", err)
		return
	}
	defer client.Close()

	topic := "my-compressed-topic"
	producer, err := client.CreateProducer(pulsar.ProducerOptions{
    
    
		Topic:       topic,
		Compression: pulsar.LZ4, // 设置压缩算法
	})
	if err != nil {
    
    
		fmt.Println("Error creating producer:", err)
		return
	}
	defer producer.Close()

	// 发送压缩消息
	message := &pulsar.ProducerMessage{
    
    
		Payload: []byte("Compressed Message"),
	}
	_, err = producer.Send(context.Background(), message)
	if err != nil {
    
    
		fmt.Println("Error sending compressed message:", err)
		return
	}
	fmt.Printf("Sent compressed message with ID: %s\n", message.ID())
}

In diesem Beispiel erstellen wir ein Thema und senden eine komprimierte Nachricht mithilfe des Produzenten, wobei LZ4 als Komprimierungsalgorithmus ausgewählt wird.

4. Nachrichtendeduplizierung

Durch die Nachrichtendeduplizierung kann das System die Verarbeitung doppelter Nachrichten bei der Nachrichtenverarbeitung vermeiden und so sicherstellen, dass das System beim Empfang derselben Nachricht keine doppelten Ergebnisse erzeugt.

Pulsar bietet einen integrierten Nachrichtendeduplizierungsmechanismus und unterstützt auch benutzerdefinierte Deduplizierungsmethoden.

4.1 Deduplizierungsmechanismus

Der Nachrichtendeduplizierungsmechanismus von Pulsar basiert auf der eindeutigen Kennung der Nachricht (Nachrichten-ID). Jede Nachricht verfügt über eine eindeutige ID, die zur Identifizierung der Einzigartigkeit der Nachricht in Pulsar verwendet wird. Der Nachrichtendeduplizierungsmechanismus bestimmt, ob die Nachricht verarbeitet wurde, indem er die Nachrichten-ID überprüft.

4.2 Implementierungsmethode der Deduplizierung

Die Implementierung der Nachrichtendeduplizierung umfasst normalerweise die folgenden Schritte:

  • Generieren Sie eine eindeutige ID: Beim Senden einer Nachricht muss eine eindeutige ID für die Nachricht generiert werden. Diese ID kann basierend auf Nachrichteninhalt, Zeitstempel usw. generiert werden.

  • Speichern Sie die ID der verarbeiteten Nachricht: Die ID der verarbeiteten Nachricht muss im System gespeichert werden. Zum Speichern dieser IDs kann ein dauerhafter Speicher (z. B. eine Datenbank) verwendet werden, der sicherstellt, dass eine Aufzeichnung der verarbeiteten Nachrichten auch dann erhalten bleibt, wenn das System neu gestartet wird.

  • Prüfung der ID bei der Verarbeitung einer Nachricht: Bei der Verarbeitung einer Nachricht prüft das System, ob die ID der Nachricht bereits im Datensatz der verarbeiteten Nachricht vorhanden ist. Wenn sie vorhanden ist, wird sie als doppelte Nachricht betrachtet und kann direkt verworfen oder entsprechend verarbeitet werden.

4.3 Beispiel

Das Beispiel zeigt, wie die Nachrichtendeduplizierung in Pulsar verwendet wird:

package main

import (
	"context"
	"fmt"
	"github.com/apache/pulsar/pulsar-client-go/pulsar"
)

func main() {
    
    
	client, err := pulsar.NewClient(pulsar.ClientOptions{
    
    
		URL: "pulsar://localhost:6650",
	})
	if err != nil {
    
    
		fmt.Println("Error creating Pulsar client:", err)
		return
	}
	defer client.Close()

	topic := "my-dedup-topic"
	producer, err := client.CreateProducer(pulsar.ProducerOptions{
    
    
		Topic: topic,
	})
	if err != nil {
    
    
		fmt.Println("Error creating producer:", err)
		return
	}
	defer producer.Close()

	// 发送消息
	messageContent := "Deduplicated Message"
	message := &pulsar.ProducerMessage{
    
    
		Payload: []byte(messageContent),
	}

	// 在生产者端生成唯一 ID,这里使用消息内容的哈希值作为 ID
	messageID := fmt.Sprintf("%x", message.Payload)
	message.SetOrderingKey(messageID)

	// 发送消息
	_, err = producer.Send(context.Background(), message)
	if err != nil {
    
    
		fmt.Println("Error sending message:", err)
		return
	}
	fmt.Printf("Sent message with ID: %s\n", messageID)

	// 在消费者端处理消息时,检查消息的 ID 是否已经存在于已处理消息的记录中
	consumer, err := client.Subscribe(pulsar.ConsumerOptions{
    
    
		Topic:            topic,
		SubscriptionName: "my-subscription",
		SubscriptionType: pulsar.Shared,
	})
	if err != nil {
    
    
		fmt.Println("Error creating consumer:", err)
		return
	}
	defer consumer.Close()

	msg, err := consumer.Receive(context.Background())
	if err != nil {
    
    
		fmt.Println("Error receiving message:", err)
		return
	}

	// 获取消息的 ID
	receivedMessageID := fmt.Sprintf("%x", msg.Payload())
	fmt.Printf("Received message with ID: %s\n", receivedMessageID)

	// 判断是否已经处理过该消息
	if isMessageProcessed(receivedMessageID) {
    
    
		fmt.Println("Message already processed. Discarding...")
	} else {
    
    
		// 处理消息的业务逻辑
		fmt.Println("Processing message...")
		// TODO: 处理消息

		// 记录已处理消息的 ID
		recordProcessedMessage(receivedMessageID)

		// 确认消息已经被处理
		consumer.Ack(msg)
	}
}

// 模拟已处理消息的记录
var processedMessages = make(map[string]bool)

func isMessageProcessed(messageID string) bool {
    
    
	return processedMessages[messageID]
}

func recordProcessedMessage(messageID string) {
    
    
	processedMessages[messageID] = true
}

In diesem Beispiel haben wir die eindeutige ID der Nachricht auf der Produzentenseite generiert und als OrderingKey der Nachricht festgelegt.

Nachdem der Verbraucher die Nachricht empfangen hat, stellt er fest, ob die Nachricht verarbeitet wurde, indem er prüft, ob die ID der Nachricht bereits im Datensatz der verarbeiteten Nachricht vorhanden ist.

Wenn die Nachricht verarbeitet wurde, kann sie direkt verworfen werden. Andernfalls kann eine spezifische Geschäftsverarbeitung durchgeführt werden. Dadurch wird sichergestellt, dass das System bei der Verarbeitung von Nachrichten eine doppelte Verarbeitung derselben Nachricht vermeidet.

5. Multi-Tenant-Unterstützung

Die Unterstützung mehrerer Mandanten ermöglicht unabhängige Namespaces und Ressourcenisolation für verschiedene Mandanten im selben Pulsar-Cluster und stellt so sicher, dass Daten und Ressourcen zwischen Mandanten sich nicht gegenseitig beeinträchtigen.

5.1 Mieter-Isolationsmechanismus

Pulsar verwendet einen Mandantenisolationsmechanismus, um sicherzustellen, dass Daten und Metadaten zwischen verschiedenen Mandanten voneinander isoliert sind.

Zu den wichtigsten Isolationsmechanismen gehören:

  • Namespace-Isolation: Pulsar verwendet Namespace als Isolationseinheit. Jeder Mandant kann mehrere Namespaces erstellen und jeder Namespace verfügt über unabhängige Themen und Abonnements.

  • Authentifizierung und Autorisierung: Pulsar verwendet Authentifizierungs- und Autorisierungsmechanismen, um Verbindungsanfragen zu authentifizieren und den Zugriff auf Ressourcen zu steuern. Dadurch wird sichergestellt, dass nur authentifizierte Benutzer auf die Ressourcen des Mandanten zugreifen können, zu dem sie gehören.

5.2 Mandantenressourcenmanagement

Bei Pulsar umfasst das Mieterressourcenmanagement hauptsächlich die Zuweisung und Beschränkung von Rechen- und Speicherressourcen. Dadurch wird sichergestellt, dass Mandanten sich nicht gegenseitig bei der Nutzung von Clusterressourcen behindern.

Zu den wichtigsten Aspekten des Mieterressourcenmanagements gehören:

  • Ressourcenkontingente: Mit Pulsar können Administratoren Ressourcenkontingente für jeden Mandanten festlegen, einschließlich Durchsatz, Speicherplatz usw. Dadurch wird die Nutzung der Clusterressourcen durch jeden Mandanten eingeschränkt.

  • Priorität: Für unterschiedliche Mandanten können unterschiedliche Prioritäten festgelegt werden, um sicherzustellen, dass Mandanten mit hoher Priorität mehr Ressourcenzuteilung erhalten, wenn die Ressourcen begrenzt sind.

5.3 Beispiel

Beispiel, das die Verwendung der Mandantenfähigkeitsunterstützung in Pulsar demonstriert:

package main

import (
	"context"
	"fmt"
	"github.com/apache/pulsar/pulsar-client-go/pulsar"
)

func main() {
    
    
	client, err := pulsar.NewClient(pulsar.ClientOptions{
    
    
		URL: "pulsar://localhost:6650",
	})
	if err != nil {
    
    
		fmt.Println("Error creating Pulsar client:", err)
		return
	}
	defer client.Close()

	// 定义租户和命名空间
	tenant := "my-tenant"
	namespace := "my-namespace"

	// 创建租户管理员
	admin, err := pulsar.NewAdminClient(pulsar.ClientOptions{
    
    
		URL: "http://localhost:8080",
	})
	if err != nil {
    
    
		fmt.Println("Error creating Pulsar admin client:", err)
		return
	}
	defer admin.Close()

	// 创建租户
	err = admin.Tenants().CreateTenant(context.Background(), tenant,
		&pulsar.TenantInfo{
    
    AllowedClusters: []string{
    
    "standalone"}})
	if err != nil {
    
    
		fmt.Println("Error creating tenant:", err)
		return
	}

	// 创建命名空间
	err = admin.Namespaces().CreateNamespace(context.Background(),
		pulsar.Namespace{
    
    
			Name: tenant + "/" + namespace,
		})
	if err != nil {
    
    
		fmt.Println("Error creating namespace:", err)
		return
	}

	// 使用租户和命名空间创建生产者和消费者
	topic := "persistent://" + tenant + "/" + namespace + "/my-topic"
	producer, err := client.CreateProducer(pulsar.ProducerOptions{
    
    
		Topic: topic,
	})
	if err != nil {
    
    
		fmt.Println("Error creating producer:", err)
		return
	}
	defer producer.Close()

	consumer, err := client.Subscribe(pulsar.ConsumerOptions{
    
    
		Topic:            topic,
		SubscriptionName: "my-subscription",
		SubscriptionType: pulsar.Shared,
	})
	if err != nil {
    
    
		fmt.Println("Error creating consumer:", err)
		return
	}
	defer consumer.Close()

	// 发送和接收消息
	message := &pulsar.ProducerMessage{
    
    
		Payload: []byte("Hello, Pulsar!"),
	}
	_, err = producer.Send(context.Background(), message)
	if err != nil {
    
    
		fmt.Println("Error sending message:", err)
		return
	}

	receivedMsg, err := consumer.Receive(context.Background())
	if err != nil {
    
    
		fmt.Println("Error receiving message:", err)
		return
	}

	fmt.Printf("Received message: %s\n", string(receivedMsg.Payload()))
}

In diesem Beispiel implementieren wir die mandantenfähige Isolation, indem wir Mandanten und Namespaces erstellen. Erstellen Sie Produzenten und Konsumenten mit unterschiedlichen Mandanten und Namespaces, um sicherzustellen, dass die Ressourcen und Daten zwischen ihnen voneinander isoliert sind.

Supongo que te gusta

Origin blog.csdn.net/weixin_49015143/article/details/135101116
Recomendado
Clasificación