Ausführliche Erläuterung der Anwendung von ZooKeeper im Microservice-Registrierungscenter

Dieser Artikel wurde von der Huawei Cloud Community geteilt. „ Detaillierte Erläuterung von SpringCloud ZooKeeper und Integration mit Nicht-Java-Diensten wie Go und Rust “, Autor: Zhang Jian.

ZooKeeper ist ein verteilter Open-Source-Koordinierungsdienst, der nicht nur verteilte Wahlen und Aufgabenzuweisungen unterstützt, sondern auch als Registrierungszentrum und Konfigurationszentrum für Mikrodienste verwendet werden kann. In diesem Artikel werden wir uns mit dem Szenario befassen, in dem ZooKeeper als Microservice-Registrierungszentrum verwendet wird.

Dienstregistrierungspfad in ZooKeeper

SpringCloud ZooKeeper folgt einer bestimmten Pfadstruktur für die Dienstregistrierung

/services/${spring.application.name}/${serviceId}

Beispiel:

/services/provider-service/d87a3891-1173-45a0-bdfa-a1b60c71ef4e

/services und /${spring.application.name} sind permanente Knoten in ZooKeeper und /${serviceId} ist ein temporärer Knoten. Wenn der Dienst offline geht, löscht ZooKeeper den Knoten automatisch.

Hinweis: Wenn die letzte Instanz des Mikrodienstes offline geht, löscht das SpringCloud ZooKeeper-Framework den Knoten /${spring.application.name}.

Dienstregistrierungsdaten in ZooKeeper

Das Folgende ist ein Beispiel für einen typischen Inhalt der Dienstregistrierung:

{

„name“: „Anbieter-Dienst“,

„id“: „d87a3891-1173-45a0-bdfa-a1b60c71ef4e“,

„Adresse“: „192.168.0.105“,

"Port":8080,

„sslPort“:null,

"Nutzlast":{

„@class“: „org.springframework.cloud.zookeeper.discovery.ZookeeperInstance“,

„id“: „Anbieter-Dienst“,

„name“: „Anbieter-Dienst“,

„Metadaten“:{

„instance_status“: „UP“

}

},

„registrationTimeUTC“:1695401004882,

„serviceType“: „DYNAMISCH“,

„uriSpec“:{

"Teile":[

{

„value“: „Schema“,

„Variable“:true

},

{

"Wert":"://",

„Variable“:false

},

{

„Wert“: „Adresse“,

„Variable“:true

},

{

"Wert":":",

„Variable“:false

},

{

„Wert“: „Port“,

„Variable“:true

}

]

}

}

Darunter sind Adresse, Port und uriSpec die Kerndaten. Die Teile in uriSpec unterscheiden, welcher Inhalt variabel und welcher fest ist.

SpringCloud-Dienste verwenden OpenFeign, um sich gegenseitig aufzurufen

Sobald beide Microservices bei ZooKeeper registriert sind, können sie sich gegenseitig über OpenFeign aufrufen. Ein einfaches Beispiel ist wie folgt

Dienstleister

Erstellen Sie ein SpringBoot-Projekt

Erstellen Sie ein SpringBoot-Projekt und fügen Sie die Abhängigkeiten spring-cloud-starter-zookeeper-discovery und spring-boot-starter-web hinzu .

Konfigurieren Sie application.yaml

Frühling:

Anwendung:

Name: Anbieter-Dienst

Wolke:

Tierpfleger:

Verbindungszeichenfolge: localhost:2181

Server:

Port: 8082

Registrieren Sie sich bei ZooKeeper

Fügen Sie der Startklasse die Annotation @EnableDiscoveryClient hinzu.

Erstellen Sie eine einfache REST-Schnittstelle

@RestController

öffentliche Klasse ProviderController {

@GetMapping("/hello")

public String hello() {

return „Hallo vom Anbieterdienst!“;

}

}

Verbraucher bedienen

Erstellen Sie ein SpringBoot-Projekt

Erstellen Sie ein SpringBoot-Projekt und fügen Sie die Abhängigkeiten spring-cloud-starter-zookeeper-discovery , spring-cloud-starter-openfeign und spring-boot-starter-web hinzu .

Konfigurieren Sie application.yaml

Frühling:

Anwendung:

Name: Verbraucherservice

Wolke:

Tierpfleger:

Verbindungszeichenfolge: localhost:2181

Server:

Port: 8081

Registrieren Sie sich bei ZooKeeper

Fügen Sie der Startklasse die Annotation @EnableDiscoveryClient hinzu.

Erstellen Sie eine REST-Schnittstelle, um den Dienstanbieter über OpenFeign aufzurufen

@RestController

öffentliche Klasse ConsumerController {



@Autowired

privater ProviderClient ProviderClient;

@GetMapping("/getHello")

öffentlicher String getHello() {

return anbieterClient.hello();

}

}

Laufergebnis

curl localhost:8081/getHello -i

HTTP/1.1 200

Inhaltstyp: text/plain;charset=UTF-8

Inhaltslänge: 28

Datum: Mi, 18. Okt. 2023 02:40:57 GMT

Hallo vom Provider Service!

Nicht-Java-Dienste sind bei SpringCloud ZooKeeper registriert

Einige Leser finden es auf den ersten Blick vielleicht etwas seltsam: Warum sollten wir Nicht-Java-Dienste in SpringCloud ZooKeeper registrieren? Ein solches Anwendungsszenario gibt es nicht.

Natürlich sind solche Szenarien relativ selten. Es ist üblich, dass die meisten Projekte mit Spring Cloud entwickelt werden, aber eine kleine Anzahl von Projekten muss aus verschiedenen Gründen andere Sprachen wie Go, Rust usw. verwenden. Zu diesem Zeitpunkt müssen wir Nicht-Java-Dienste in SpringCloud ZooKeeper registrieren.

Stellen Sie bei Diensten, die in Nicht-JVM-Sprachen entwickelt wurden, lediglich sicher, dass sie eine Rest/HTTP-Schnittstelle bereitstellen und korrekt bei ZooKeeper registriert sind, damit sie vom Feign-Client von SpringCloud aufgerufen werden können.

Go-Dienste in SpringCloud ZooKeeper

Beispielcode-Organisation:

├── Verbraucher

│ └── Consumer.go

├── go.mod

├── go.sum

└── Anbieter

└── anbieter.go

Go-Dienstanbieter in SpringCloud ZooKeeper

Hinweis: Die Qualität dieses Codes entspricht dem Demo-Niveau. Die tatsächliche Produktionsumgebung erfordert strengeren Code, z. B. einen Wiederverbindungsmechanismus, einen Timeout-Mechanismus, einen besseren Algorithmus zur Service-ID-Generierung usw.

Paket main

importieren (

„fmt“

"Protokoll"

„net/http“

"Zeit"

„Kodierung/json“

"github.com/gin-gonic/gin"

"github.com/samuel/go-zookeeper/zk"

)

const (

zkServers = "localhost:2181" // Zookeeper-Serveradresse

)

func main() {

// Initialisiere das Gin-Framework

r := gin.Default()

//Eine einfache Hallo-Schnittstelle hinzufügen

r.GET("/hello", func(c *gin.Context) {

c.String(http.StatusOK, „Hello from Go-Dienst!“)

})

//Dienst für Zookeeper registrieren

registerToZookeeper()

//Starte den Gin-Server

r.Run(":8080")

}

func registerToZookeeper() {

conn, _, err := zk.Connect([]string{zkServers}, time.Second*5)

if err != nil {

Panik(irr)

}

// Überprüfe und erstelle den übergeordneten Pfad

securePathExists(conn, "/services")

securePathExists(conn, "/services/provider-service")

// Registrierte Daten erstellen

Daten, _ := json.Marshal(map[string]interface{}{}{

„name“: „provider-service“,

"Adresse": "127.0.0.1",

„Port“: 8080,

„sslPort“: null,

„payload“: map[string]interface{}{“@class“: „org.springframework.cloud.zookeeper.discovery.ZookeeperInstance“, „id“: „provider-service“, „name“: „provider-service“ , „metadata“: map[string]string{“instance_status“: „UP“}},

„serviceType“: „DYNAMISCH“,

„uriSpec“: map[string]interface{}{

"parts": []map[string]interface{}{}{

{"value": "scheme", "variable": true},

{"value": "://", "variable": false},

{"value": "address", "variable": true},

{"Wert": ::, "Variable": falsch},

{"value": "port", "variable": true},

},

},

})

// Registrieren Sie den Dienst in zookeeper

Pfad := "/services/provider-service/" + genericServiceId()

_, err = conn.Create(path, data, zk.FlagEphemeral, zk.WorldACL(zk.PermAll))

if err != nil {

log.Fatalf("Registrierungsdienstfehler: %s", err)

} anders {

log.Println(Pfad)

}

}

func securePathExists(conn *zk.Conn, path string) {

existiert, _, err := conn.Exists(path)

if err != nil {

log.Fatalf("Pfadfehler prüfen: %s", err)

}

wenn !exists {

_, err := conn.Create(path, []byte{}, 0, zk.WorldACL(zk.PermAll))

if err != nil {

log.Fatalf("Pfadfehler erstellen: %s", err)

}

}

}

func genericServiceId() string {

// Dies wird vereinfacht, um die aktuelle Zeit zum Generieren der ID zu verwenden. Die tatsächliche Produktionsumgebung erfordert möglicherweise einen komplexeren Algorithmus.

return fmt.Sprintf("%d", time.Now().UnixNano())

}

Anrufeffekt

curl localhost:8081/getHello -i

HTTP/1.1 200

Inhaltstyp: text/plain;charset=UTF-8

Inhaltslänge: 28

Datum: Mi, 18. Okt. 2023 02:43:52 GMT

Hallo vom Go-Service!

Gehen Sie zum Servicekonsumenten in SpringCloud ZooKeeper

Paket main

importieren (

„Kodierung/json“

„fmt“

„io“

"Protokoll"

„net/http“

"Zeit"

"github.com/samuel/go-zookeeper/zk"

)

const (

zkServers = "localhost:2181" // Zookeeper-Serveradresse

)

var conn *zk.Conn

func main() {

// ZooKeeper-Verbindung initialisieren

initializeZookeeper()

// Serviceinformationen abrufen

serviceInfo := getServiceInfo("/services/provider-service")

fmt.Println("Abgerufene Dienstinformationen:", serviceInfo)

port := int(serviceInfo["port"].(float64))

resp, err := http.Get(fmt.Sprintf("http://%s:%d/hello", serviceInfo["address"], port))

if err != nil {

Panik(irr)

}

body, err := io.ReadAll(resp.Body)

if err != nil {

Panik(irr)

}

fmt.Println(string(body))

}

func initializeZookeeper() {

var err Fehler

conn, _, err = zk.Connect([]string{zkServers}, time.Second*5)

if err != nil {

log.Fatalf("Verbindung zu ZooKeeper fehlgeschlagen: %s", err)

}

}

func getServiceInfo(path string) map[string]interface{} {

children, _, err := conn.Children(path)

if err != nil {

log.Fatalf("Untergeordnete Elemente von %s konnten nicht abgerufen werden: %s", Pfad, Fehler)

}

if len(children) == 0 {

log.Fatalf("Keine Dienste unter %s gefunden", Pfad)

}

// Hier erhalten wir nur als Beispiel die Informationen des ersten Dienstknotens. Tatsächlich können Sie einen Dienstknoten gemäß der Lastausgleichsrichtlinie auswählen.

data, _, err := conn.Get(fmt.Sprintf("%s/%s", path,children[0]))

if err != nil {

log.Fatalf("Fehler beim Abrufen der Daten von %s: %s", Kinder[0], err)

}

var serviceInfo map[string]interface{}

if err := json.Unmarshal(data, &serviceInfo); ähm != null {

log.Fatalf("Daten konnten nicht entmarshaliert werden: %s", Fehler)

}

RückgabeserviceInfo

}

Rust-Dienst in SpringCloud ZooKeeper

Beispielcode-Organisation:

├── Cargo.lock

├── Cargo.toml

└── src

└── bin

├── Verbraucher.rs

└── anbieter.rs

Rust-Dienstanbieter in SpringCloud ZooKeeper

verwenden Sie std::collections::HashMap;

verwenden Sie std::time::Duration;

use serde_json::Value;

verwenden warp::Filter;

use zookeeper::{Acl, CreateMode, WatchedEvent, Watcher, ZooKeeper};

static ZK_SERVERS: &str = "localhost:2181";

static mut ZK_CONN: Option<ZooKeeper> = None;

struct LoggingWatcher;

impl Watcher für LoggingWatcher {

fn handle(&self, e: WatchedEvent) {

println!("WatchedEvent: {:?}", e);

}

}

#[tokio::main]

async fn main() {

let hello = warp::path!("hello").map(|| warp::reply::html("Hallo vom Rust-Dienst!"));

register_to_zookeeper().await;

warp::serve(hello).run(([127, 0, 0, 1], 8083)).await;

}

async fn register_to_zookeeper() {

unsicher {

ZK_CONN = Some(ZooKeeper::connect(ZK_SERVERS, Duration::from_secs(5), LoggingWatcher).unwrap());

let zk = ZK_CONN.as_ref().unwrap();

let path = "/services/provider-service";

if zk.exists(path, false).unwrap().is_none() {

zk.create(path, vec![], Acl::open_unsafe().clone(), CreateMode::Persistent).unwrap();

}

let service_data = get_service_data();

let service_path = format!("{}/{}", path, generic_service_id());

zk.create(&service_path, service_data, Acl::open_unsafe().clone(), CreateMode::Ephemeral).unwrap();

}

}

fn get_service_data() -> Vec<u8> {

let mut data: HashMap<&str, Value> = HashMap::new();

data.insert("name", serde_json::Value::String("provider-service".to_string()));

data.insert("address", serde_json::Value::String("127.0.0.1".to_string()));

data.insert("port", serde_json::Value::Number(8083.into()));

serde_json::to_vec(&data).unwrap()

}

fn generic_service_id() -> String {

format!("{}", chrono::Utc::now().timestamp_nanos())

}

Rust-Dienstkonsument in SpringCloud ZooKeeper

verwenden Sie std::collections::HashMap;

verwenden Sie std::time::Duration;

use zookeeper::{WatchedEvent, Watcher, ZooKeeper};

benutze reqwest;

use serde_json::Value;

static ZK_SERVERS: &str = "localhost:2181";

struct LoggingWatcher;

impl Watcher für LoggingWatcher {

fn handle(&self, e: WatchedEvent) {

println!("WatchedEvent: {:?}", e);

}

}

#[tokio::main]

async fn main() {

letProvider_data = fetch_provider_data_from_zookeeper().await;

let Response = request_provider(&provider_data).await;

println!("Antwort vom Anbieter: {}", Antwort);

}

async fn fetch_provider_data_from_zookeeper() -> HashMap<String, Value> {

let zk = ZooKeeper::connect(ZK_SERVERS, Duration::from_secs(5), LoggingWatcher).unwrap();

let children = zk.get_children("/services/provider-service", false).unwrap();

if children.is_empty() {

panic!("Keine Anbieterdienste gefunden!");

}

// Der Einfachheit halber nehmen wir einfach das erste untergeordnete Element (dh die Dienstinstanz).

// In einem realen Szenario würden Lastausgleichsstrategien bestimmen, welche Dienstinstanz verwendet werden soll.

let data = zk.get_data(&format!("/services/provider-service/{}",children[0]), false).unwrap();

serde_json::from_slice(&data.0).unwrap()

}

async fn request_provider(provider_data: &HashMap<String, Value>) -> String {

let address =Provider_data.get("address").unwrap().as_str().unwrap();

let port =Provider_data.get("port").unwrap().as_i64().unwrap();

let url = format!("http://{}:{}/hello", Adresse, Port);

let Response = reqwest::get(&url).await.unwrap();

Antwort.text().await.unwrap()

}

Klicken Sie hier, um zu folgen und so schnell wie möglich mehr über die neuen Technologien von Huawei Cloud zu erfahren~

Tang Xiaoou, Gründer von SenseTime, verstarb im Alter von 55 Jahren. Im Jahr 2023 stagnierte PHP . Das Hongmeng-System steht kurz vor der Unabhängigkeit und viele Universitäten haben „Hongmeng-Kurse“ eingerichtet. Die PC-Version von Quark Browser hat mit internen Tests begonnen . ByteDance wurde von OpenAI „verboten“. Das Startup-Unternehmen von Zhihuijun wurde mit einem Betrag von über 600 Millionen Yuan und einer Pre-Money-Bewertung von 3,5 Milliarden Yuan refinanziert. KI-Code-Assistenten sind so beliebt, dass sie nicht einmal in der Programmierung mithalten können Sprachrankings . Das 5G-Modem und die Hochfrequenztechnologie des Mate 60 Pro liegen weit vorne. No Star, No Fix MariaDB spaltet SkySQL ab und gründet sich als unabhängiges Unternehmen
{{o.name}}
{{m.name}}

Supongo que te gusta

Origin my.oschina.net/u/4526289/blog/10322513
Recomendado
Clasificación