Autor: Wang Yuxuan (Github: DMwangnima), Kitex-Committer
1. Hintergrund
Kitex ist ein Go-Microservice-RPC-Framework, das vom ByteDance-Infrastruktur-Service-Framework-Team gestartet wurde. Es unterstützt Nachrichtenprotokolle wie Thrift, Kitex Protobuf und gRPC und zeichnet sich durch hohe Leistung und starke Skalierbarkeit aus. Nach der offiziellen Open-Source-Veröffentlichung im September 2021 wurde es in vielen externen Unternehmen erfolgreich implementiert und bringt ihnen echte Kosten-, Leistungs- und Stabilitätsvorteile.
Bei der Nutzung von Kitex-Transformationsdiensten benötigen viele Unternehmensbenutzer Kitex, um mit den vom bestehenden Dubbo-Framework implementierten Diensten zu kommunizieren. Dies deckt sich mit dem Ziel der CloudWeGo-Community, das Ökosystem aktiv zu erweitern. Daher ist das Dubbo-Interoperabilitätsprojekt codec-dubbo entstand.
Mit der enthusiastischen Hilfe von Community-Studenten ist codec-dubbo derzeit in der Lage, zwischen Kitex und Dubbo-Java, Kitex und Dubbo-Go zu interagieren und unterstützt Dubbo-Benutzer bei der Migration zu Kitex.
In diesem Artikel wird die erfolgreiche Servicetransformation von Founder Securities mit Kitex und Codec-Dubbo als Beispiel genommen, die Hauptfunktionen des im Transformationsprozess verwendeten Codec-Dubbo erläutert und die Implementierungsdetails kurz analysiert.
2. Fälle der Unternehmensimplementierung
Die ursprünglichen Dienste von Founder Securities sind in Java- und Dubbo-Frameworks geschrieben. Beide sind stabil und wurden in einer Vielzahl von Szenarien verifiziert und erfüllen ihre Produktions- und Entwicklungsanforderungen. Nehmen wir als Beispiel die individuelle Bestandsdetailseite von Xiaofang, die eine große Anzahl von Anfragen enthält. In Spitzenzeiten beträgt die Schnittstellen-QPS 3-4.000, und für das Hosting werden 16 virtuelle 16-Core-64G-Maschinen verwendet.
Mit dem Aufstieg der Cloud-nativen Architektur hat sich Go aufgrund seiner Vorteile bei Speichernutzung und Ausführungseffizienz sowie seiner natürlichen Anpassungsfähigkeit an Cloud-Native nach und nach zu einer wichtigen Technologieoption für den Aufbau von Unternehmensdiensten entwickelt. Um die Kosten besser zu senken und die Effizienz zu steigern, beschlossen sie nach umfassender Berücksichtigung von Faktoren wie Kosten, Leistung und Stabilität, für neue Anwendungen von Java auf Go umzusteigen, führten Kitex, Hertz und andere CloudWeGo-Projekte für die Serviceentwicklung und -rekonstruktion ein und migrierten die gesamte Kubernetes-Umgebung.
Während des Rekonstruktionsprozesses stützte sich Codec-Dubbo auf eine Benutzererfahrung, die der von nativem Kitex + Thrift ähnelt, und auf eine gute Unterstützung für Dubbo-Konzepte, um die Nutzungskosten und das Verständnis zu senken. Dies half ihnen erfolgreich, das Interoperabilitätsproblem von Kitex <-> Dubbo zu lösen Kitex-Service Rufen Sie problemlos den ursprünglichen Dubbo-Service an.
Derzeit wurde der Kitex-Dienst mit Codec-Dubbo erfolgreich gestartet und läuft seit zwei Monaten stabil. Am Beispiel der Seite mit den einzelnen Bestandsdetails von Xiaofang hosten Kitex und Hertz etwa die Hälfte der Schnittstellen auf dieser Seite. Bei unverändertem QPS müssen nur 12 4-Core-4G-Pods bereitgestellt werden, was den Ressourcenverbrauch erheblich reduziert.
3. Codec-Dubbo-Funktionsmerkmale
Dubbo-Protokoll-Codec
Der Dubbo-Dienst verwendet hauptsächlich das Dubbo-Protokoll für die Kommunikation. Um die Interoperabilität von Kitex <-> Dubbo zu unterstützen, müssen wir das Dubbo-Protokoll in Kitex implementieren. Dank der hervorragenden Skalierbarkeit von Kitex implementiert codec-dubbo DubboCodec, den Kerncodec, basierend auf der von Kitex bereitgestellten Codec-Schnittstelle. Sie müssen DubboCodec nur während der Initialisierung injizieren, um das Dubbo-Protokoll zu verwenden.
Typzuordnung und -erweiterung
Typzuordnung
Dubbo verwendet hauptsächlich das Hessian2-Serialisierungsprotokoll zum Kodieren und Dekodieren der Nutzdaten. Sein größtes Merkmal ist der selbstbeschreibende Serialisierungstyp, das heißt, es ist nicht auf externe Schema- oder Schnittstellendefinitionen angewiesen. Der Serialisierungsprozess basiert auf der Zuordnung zwischen Programmiersprachentypen und Hessian2-Typen. Am Beispiel der Konvertierung von Go-Typen in Java-Typen:
Nach der Analyse haben wir festgestellt, dass sich das Grundtypsystem von Hessian2 grundsätzlich mit Thrift überschneidet. Um sicherzustellen, dass die Nutzungserfahrung von Kitex + Codec-Dubbo grundsätzlich mit der von Kitex + Thrift übereinstimmt, generieren wir Kitex Dubbo-Hessian2-Gerüstcode basierend auf Thrift IDL. Zu diesem Zeitpunkt ist der Typkonvertierungsprozess wie folgt:
Weitere Informationen finden Sie in der offiziellen Dubbo-go-hessian2 -Typzuordnung. Codec-Dubbo bietet die folgende Typzuordnung (hier ist nur ein Teil der Zuordnung enthalten. Weitere Vorsichtsmaßnahmen finden Sie in der Codec-Dubbo-Readme-Datei):
Gemäß der von codec-dubbo bereitgestellten Typzuordnung können wir die Dubbo-Schnittstellendefinition problemlos in Thrift IDL konvertieren, das Kitex-Befehlszeilentool verwenden, um Projektgerüstcode zu generieren, und schließlich DubboCodec injizieren, um die Kitex -> Dubbo-Kommunikation abzuschließen. Nehmen Sie als Beispiel die folgende Dubbo-Schnittstellendefinition:
package org.cloudwego.kitex.samples.api;
public interface GreetProvider {
GreetResponse Greet(GreetRequest req) throws Exception;
}
public class GreetRequest implements Serializable {
String req;
public GreetRequest(String req) {
this.req = req;
}
}
public class GreetResponse implements Serializable {
String resp;
public GreetResponse(String resp) {
this.resp = resp;
}
}
Die entsprechende api.thrift-Datei lautet wie folgt. Es ist zu beachten, dass die darin enthaltenen Strukturdefinitionen mit JavaClassName versehen werden müssen, was dem Paket- und Klassennamen in der Dubbo-Schnittstellendefinition entspricht.
struct GreetRequest {
1: required string req,
} (JavaClassName="org.cloudwego.kitex.samples.api.GreetRequest")
struct GreetResponse {
1: required string resp,
} (JavaClassName="org.cloudwego.kitex.samples.api.GreetResponse")
service GreetService {
GreetResponse Greet(1: GreetRequest req)
}
Verwenden Sie das Kitex-Befehlszeilentool und geben Sie das Protokoll als Hessian2 an:
kitex -module demo-client -protocol Hessian2 ./api.thrift
Initialisieren Sie dann DubboCodec und fügen Sie ihn in Kitex ein. Verwenden Sie den generierten Code, um den folgenden Client-Code zu schreiben, um den Kitex -> Dubbo-Aufruf zu implementieren:
javaClass := "org.cloudwego.kitex.samples.api.GreetProvider"
cli, err := greetservice.NewClient("helloworld",
// 指定想要访问的服务端地址,也支持 ZooKeeper 服务发现
client.WithHostPorts("127.0.0.1:21000"),
// 配置 DubboCodec
client.WithCodec(
// 指定想要调用的 Dubbo Interface
dubbo.NewDubboCodec(dubbo.WithJavaClassName(javaClass))
),
)
if err != nil {
panic(err)
}
resp, err := cli.Greet(context.Background(),
&hello.GreetRequest{Req: "world"})
if err != nil {
klog.Error(err)
return
}
klog.Infof("resp: %s", resp.Resp)
Der serverseitige Kitex + Codec-Dubbo-Prozess ähnelt im Wesentlichen dem clientseitigen Prozess. Konkrete Beispiele finden Sie auf der Projekthomepage.
Typerweiterung
Die schemafreie Funktion von Hessian2 macht die Implementierung von Dubbo „zu flexibel“ und kann jeden Typ verwenden. Um sich an die Typnutzungsflexibilität von Dubbo Hessian2 anzupassen, unterstützt Codec-Dubbo die Typerweiterung, die hauptsächlich benutzerdefinierte Zuordnung und Java-Common-Type-Erweiterung umfasst.
Benutzerdefinierte Zuordnung
Die Grundtypen von Java verfügen über entsprechende Wrapper-Typen, z. B. boolean
und java.lang.Boolean
. Die Standardzuordnung bool
von Go-Typen zu Java- java.lang.Boolean
Typen in der Typzuordnung deckt nicht die Verwendung boolean
von ab. Um die Benutzererfahrung zu vereinheitlichen und ihnen die Verwendung bool
des Typs nur auf der Kitex-Seite zu ermöglichen, können wir nach der Thrift-Methodendefinition Anmerkungen hinzufügen hessian.argsType="boolean"
, die IDL-Reflexionsfunktion von thriftgo verwenden, um IDL-Metainformationen im Voraus zu generieren und sie in den Codec einzufügen. dubbo, und dann kann es ausgeführt werden. java.lang.Boolean
Schreiben Sie den Standardzuordnungstyp dynamisch in um boolean
. Die spezifische Thrift-Definition lautet wie folgt:
service EchoService {
bool EchoBoolean(1: bool req) (hessian.argsType="boolean")
}
Ähnlich wie boolean
und java.lang.Boolean
können auch andere Java-Basistypen und Wrapper-Typen auf diese Weise angepasst werden. Derzeit sieht die vollständige Typzuordnung von codec-dubbo wie folgt aus:
Java-Common-Type-Erweiterung
Aufgrund der Einschränkungen von Thrift-Typen können wir allgemeine Typen, die in Java-Klassenbibliotheken bereitgestellt werden, nicht direkt verwenden. Zu diesem Zweck verwaltet codec-dubbo Java-Typen, die Thrift nicht unterstützt (z. B. ) , und die entsprechenden java.thrift im codec-dubbo/java- Paket . Gleichzeitig wird die Funktion idl-ref verwendet Durch Thriftgo können wir Thrift direkt verwenden. Diese Typen werden in IDL referenziert und entsprechender Code generiert. Die aktuelle java.thrift sieht so aus:java.lang.Object
java.util.Date
struct Object {} (JavaClassName="java.lang.Object")
struct Date {} (JavaClassName="java.util.Date")
struct Exception {} (JavaClassName="java.lang.Exception")
Um diese Typen zu aktivieren, müssen wir sie in Thrift IDL importieren include "java.thrift"
und Parameter hinzufügen, wenn wir Code mit dem Kitex-Befehlszeilentool generieren, -hessian2 java_extension
um das Erweiterungspaket abzurufen.
Das Kitex-Befehlszeilentool lädt java.thrift automatisch herunter. Sie können es auch manuell herunterladen und im Stammverzeichnis des Projekts ablegen. Thrift IDL-Beispiel für die Referenzierung von Typen in java.thrift:
include "java.thrift"
service EchoService {
// java.lang.Object
i64 EchoString2ObjectMap(1: map<string, java.Object> req)
// java.util.Date
i64 EchoDate(1: java.Date req)
}
Methodenüberladung
Go unterstützt das Überladen von Methoden nicht nativ. Sie können überladungsähnliche Effekte nur durch die Definition mehrerer Methoden erzielen. Um mehrere Methoden in Go überladenen Methoden in Java zuzuordnen, fügen wir ähnlich wie im benutzerdefinierten Zuordnungsabschnitt das JavaMethodName-Tag nach der Methodendefinition in Thrift hinzu und verwenden die IDL-Reflexionsfunktion von thriftgo, um Go zur Laufzeit dynamisch zuzuordnen Der Methodenname wird in die überladene Methode in Java umgeschrieben, die durch JavaMethodName angegeben wird.
Nehmen Sie als Beispiel EchoMethod auf der Java-Seite:
String EchoMethod(Boolean req);
String EchoMethod(Integer req);
String EchoMethod(int req);
String EchoMethod(Boolean req1, Integer req2);
Wir schreiben die folgende Thrift-Definition, um die überladene Methodenzuordnung zwischen Go und Java zu vervollständigen. Beachten Sie, JavaMethodName
dass und hessian.argsType
gleichzeitig verwendet werden kann:
service EchoService {
string EchoMethodA(1: bool req) (JavaMethodName="EchoMethod")
string EchoMethodB(1: i32 req) (JavaMethodName="EchoMethod")
string EchoMethodC(1: i32 req) (JavaMethodName="EchoMethod", hessian.argsType="int")
string EchoMethodD(1: bool req1, 2: i32 req2) (JavaMethodName="EchoMethod")
}
Ausnahmebehandlung
codec-dubbo ordnet Ausnahmen in Java Fehlern in Go zu. Diese Fehler implementieren einheitlich die folgende Schnittstelle:
type Throwabler interface {
Error() string
JavaClassName() string
GetStackTrace() []StackTraceElement
}
Basierend auf den von Dubbo offiziell empfohlenen Ausnahmebehandlungspraktiken und den aktuellen Anforderungen von Unternehmensbenutzern unterteilen wir Ausnahmen in allgemeine Ausnahmen und benutzerdefinierte Ausnahmen und berücksichtigen dabei die Grundbedürfnisse und Skalierbarkeitsanforderungen der Benutzer.
Häufige Ausnahmen
codec-dubbo stellt allgemeine Java-Ausnahmen im Paket pkg/hessian2/Exception bereit und unterstützt derzeit java.lang.Exception.
Häufige Ausnahmen erfordern keine Unterstützung durch das Kitex-Befehlszeilentool und können direkt zitiert werden. Im Folgenden finden Sie Beispiele für vom Client extrahierte und vom Server zurückgegebene Ausnahmen.
Ausnahme bei der Client-Extraktion
resp, err := cli.Greet(context.Background(),
&hello.GreetRequest{Req: "world"})
if err != nil {
// FromError 返回 Throwabler
exceptionRaw, ok := hessian2_exception.FromError(err)
if !ok {
// 视作常规错误处理
} else {
// 若不关心 exceptionRaw 的具体类型,直接调用 Throwabler 提供的方法即可
klog.Errorf("get %s type Exception", exceptionRaw.JavaClassName())
// 若想获得 exceptionRaw 的具体类型,需要进行类型转换,但前提是已知该具体类型
exception := exceptionRaw.(*hessian2_exception.Exception)
}
}
Die Serverseite gibt eine Ausnahme zurück
func (s *GreetServiceImpl) Greet(ctx context.Context, req *hello.GreetRequest) (resp *hello.GreetResponse, err error) {
return nil, hessian2_exception.NewException("Your detailed message")
}
Benutzerdefinierte Ausnahme
Benutzerdefinierte Ausnahmen in Java erben häufig eine grundlegende Ausnahme . Hier als CustomizedException
Beispiel :CustomizedException
java.lang.Exception
public class CustomizedException extends Exception {
private final String customizedMessage;
public CustomizedException(String customizedMessage) {
super();
this.customizedMessage = customizedMessage;
}
}
Dank der Unterstützung von thriftgo für die Generierung verschachtelter Strukturen schreiben wir zur Definition entsprechender Ausnahmen auf der Kitex-Seite die folgende Definition in Thrift:
exception CustomizedException {
// thrift.nested=“true” 注解让 thriftgo 生成嵌套结构体
1: required java.Exception exception (thrift.nested="true")
2: required string customizedMessage
}(JavaClassName="org.cloudwego.kitex.samples.api.CustomizedException")
Achten Sie exception
auf die Feldanmerkungen thrift.nested="true"
, die es thriftgo ermöglichen, verschachtelte Strukturen zu generieren, um einen ähnlichen Effekt wie bei der Vererbung zu erzielen.
Wie bei allgemeinen Java-Typerweiterungen müssen Sie Parameter hinzufügen, wenn Sie das Kitex-Scaffolding-Tool verwenden, um Code -hessian2 java_extension
zum Abrufen des Erweiterungspakets zu generieren. Der generierte Code lautet wie folgt:
type EchoCustomizedException struct {
java.Exception `thrift:"exception,1,required" frugal:"1,required,java.Exception" json:"exception"`
CustomizedMessage string `thrift:"customizedMessage,2,required" frugal:"2,required,string" json:"customizedMessage"`
}
Die Verwendungsmethode entspricht den üblichen Ausnahmen und wird hier nicht wiederholt.
Dienstregistrierung und -erkennung
Dubbo bietet sowohl Dienstregistrierungs- und Erkennungsmodelle auf Schnittstellenebene als auch auf Anwendungsebene. Basierend auf den aktuellen Anforderungen von Unternehmensbenutzern an die Produktionsumgebung haben wir uns dafür entschieden, das auf Zookeeper basierende Modell auf Schnittstellenebene zu priorisieren: Dubbo Registry-Zookeeper.
Anders als das uns bekannte Modell auf Anwendungsebene muss das Modell auf Schnittstellenebene die Zuordnungsbeziehung von Schnittstellenname => Dienst beibehalten (anders als bei Microservices, näher am Handler). Ein Schnittstellenname wird mehreren Diensten zugeordnet Diese Dienste können im selben Prozess vorhanden sein.
Da sich das Servicemodell auf Schnittstellenebene von Dubbo stark vom Servicemodell von Kitex unterscheidet und Dubbo Registry-Zookeeper zur Verwendung an Codec-Dubbo gebunden sein sollte, wird nicht in Betracht gezogen, den ursprünglichen Registry-Zookeeper in Kitex-Contrib zu ändern, um eine Dubbo-Registrierung zu erstellen -zookeeper Ein Sub-Go-Modul von Codec-Dubbo wird einheitlich entwickelt und gepflegt.
Unter Berücksichtigung des Dubbo-Servicemodells auf Schnittstellenebene, der Kitex-API und der Benutzererfahrung bieten wir die folgenden Konfigurationsebenen an:
- Die Funktionen „WithServers“ und „WithRegistryGroup“ in „ registry/options.go“ und „resolver/options.go“ stellen eine Konfiguration auf Registrierungsebene bereit und geben jeweils die Adresse des Zookeepers und die Gruppe an, zu der diese Zookeeper gehören. Benutzer verwenden diese Funktionen, um Kitex
registry.Registry
unddiscovery.Resolver
Instanzen zu generieren. - Die Konfiguration auf Service-Ebene wird
client.WithTag
mit und übergeben, registries/common.go stellt Tag-Schlüssel bereit, die den Service-Metadaten in Dubbo eins zu eins entsprechen.server.WithRegistryInfo
Resolver-Beispiel
intfName := "org.cloudwego.kitex.samples.api.GreetProvider"
res, err := resolver.NewZookeeperResolver(
// 指定 zookeeper 服务器的地址,可指定多个,请至少指定一个
resolver.WithServers("127.0.0.1:2181"),
)
if err != nil {
panic(err)
}
cli, err := greetservice.NewClient("helloworld",
// 配置 ZookeeperResolver
client.WithResolver(res),
// 指定想要调用的 dubbo Interface
client.WithTag(registries.DubboServiceInterfaceKey, intfName),
)
if err != nil {
panic(err)
}
// 使用 cli 进行 RPC 调用
Registrierungsbeispiel
intfName := "org.cloudwego.kitex.samples.api.GreetProvider"
reg, err := registry.NewZookeeperRegistry(
// 指定 zookeeper 服务器的地址,可指定多个,请至少指定一个
registry.WithServers("127.0.0.1:2181"),
)
if err != nil {
panic(err)
}
svr := greetservice.NewServer(new(GreetServiceImpl),
server.WithRegistry(reg),
// 配置dubbo URL元数据
server.WithRegistryInfo(&kitex_registry.Info{
Tags: map[string]string{
registries.DubboServiceInterfaceKey: intfName,
// application请与dubbo所设置的ApplicationConfig保持一致,此处仅为示例
registries.DubboServiceApplicationKey: "application-name",
}
}),
)
// 启动 svr
Zusammenfassen
Kitex unterstützt das Dubbo-Protokoll, was für CloudWeGo einen großen Schritt bei der Integration mehrsprachiger Cloud-nativer Ökologie darstellt. Es löst die Probleme vieler Unternehmensbenutzer bei der Konvertierung von Java in Go und die Koexistenz zwischen Java und Go Versuchen Sie, darauf zuzugreifen. Wenn Sie während der Nutzung auf Probleme stoßen, können Sie unserer Feishu-Benutzergruppe beitreten oder uns Feedback auf Github geben.
Die Raubkopien von „Celebrating More Than Years 2“ wurden auf npm hochgeladen, was dazu führte, dass npmmirror den Unpkg-Dienst einstellen musste und sich gemeinsam mit Hunderten von Menschen in die USA begab Front-End-Visualisierungsbibliothek und Baidus bekanntes Open-Source-Projekt ECharts – „Going to the Sea“ zur Unterstützung Fischbetrüger nutzten TeamViewer, um 3,98 Millionen zu überweisen! Was sollten Remote-Desktop-Anbieter tun? Zhou Hongyi: Für Google bleibt nicht mehr viel Zeit. Es wird empfohlen, dass alle Produkte Open Source sind. Ein ehemaliger Mitarbeiter eines bekannten Open-Source-Unternehmens brachte die Nachricht: Nachdem er von seinen Untergebenen herausgefordert wurde, wurde der technische Leiter wütend hat die schwangere Mitarbeiterin entlassen. Google hat gezeigt, wie man ChromeOS in einer virtuellen Android-Maschine ausführt. Bitte geben Sie mir einen Rat, welche Rolle time.sleep(6) hier spielt. Microsoft reagiert auf Gerüchte, dass Chinas KI-Team „für die USA packt“. People's Daily Online kommentiert die matroschkaartige Aufladung von Bürosoftware: Nur durch das aktive Lösen von „Sets“ können wir eine Zukunft haben