Kitex soporta el protocolo Dubbo: facilitando la integración de ecosistemas nativos de la nube multilingües

Autor: Wang Yuxuan (github: DMwangnima), Committer de Kitex

1. Antecedentes

Kitex es un marco RPC de microservicio Go lanzado por el equipo de marco de servicios de infraestructura de ByteDance. Admite protocolos de mensajes como Thrift, Kitex Protobuf y gRPC, y se caracteriza por un alto rendimiento y una gran escalabilidad. Después de ser oficialmente de código abierto en septiembre de 2021, se implementó con éxito en muchas empresas externas, lo que les brindó beneficios reales de costo, rendimiento y estabilidad.

En el proceso de utilizar los servicios de transformación de Kitex, muchos usuarios empresariales necesitan que Kitex se comunique con los servicios implementados por el marco Dubbo existente. Esto coincide con el objetivo de la comunidad CloudWeGo de expandir activamente el ecosistema. Por lo tanto, el proyecto de interoperabilidad Dubbo codec-dubbo. nació.

Con la ayuda entusiasta de los estudiantes de la comunidad, codec-dubbo actualmente puede interoperar entre Kitex y Dubbo-Java, Kitex y Dubbo-Go, y ayuda a los usuarios de Dubbo a migrar a Kitex.

Este artículo tomará como ejemplo la exitosa transformación del servicio de Founder Securities utilizando Kitex y codec-dubbo, explicará las funciones principales de codec-dubbo utilizadas en el proceso de transformación y analizará brevemente los detalles de la implementación.

2. Casos de implementación empresarial

Los servicios originales de Founder Securities están escritos en los marcos Java y Dubbo. Ambos son estables y han sido verificados en una gran cantidad de escenarios, satisfaciendo sus necesidades de producción y desarrollo. Tomando como ejemplo la página de detalles de acciones individuales de Xiaofang, que tiene una gran cantidad de solicitudes, el QPS de la interfaz durante los períodos pico es de 3-4k y se utilizan 16 máquinas virtuales de 16 Core 64G para el alojamiento.

Con el auge de la arquitectura nativa de la nube, Go se ha convertido gradualmente en una opción tecnológica importante para crear servicios empresariales debido a sus ventajas en el uso de la memoria y la eficiencia de ejecución, así como a su adaptabilidad natural a la arquitectura nativa de la nube. Para reducir mejor los costos y aumentar la eficiencia, después de considerar exhaustivamente factores como el costo, el rendimiento y la estabilidad, decidieron cambiar de Java a Go para nuevas aplicaciones, introdujeron Kitex, Hertz y otros proyectos de CloudWeGo para el desarrollo y reconstrucción de servicios, y migraron. todo el entorno de Kubernetes.

Durante el proceso de reconstrucción, codec-dubbo se basó en una experiencia de usuario cercana a la nativa Kitex + Thrift y un buen soporte para los conceptos de Dubbo para reducir el costo de uso y comprensión. Esto les ayudó a resolver con éxito el problema de interoperabilidad de Kitex <-> Dubbo y permitirles. Servicio Kitex Llame sin problemas al servicio Dubbo original.

Actualmente, el servicio Kitex que utiliza codec-dubbo se lanzó con éxito y ha estado funcionando de manera estable durante dos meses. Tomando como ejemplo la página de detalles de acciones individuales de Xiaofang, Kitex y Hertz alojan aproximadamente la mitad de las interfaces en esta página. Cuando el QPS permanece sin cambios, solo es necesario proporcionar 12 Pods 4G de 4 núcleos, lo que reduce significativamente el uso de recursos.

Tres, características funcionales del códec-dubbo

Códec del protocolo Dubbo

El servicio Dubbo utiliza principalmente el protocolo Dubbo para la comunicación. Para admitir la interoperabilidad de Kitex <-> Dubbo, necesitamos implementar el protocolo Dubbo en Kitex. Gracias a la excelente escalabilidad de Kitex, codec-dubbo implementa DubboCodec, el códec principal, basado en la interfaz Codec proporcionada por Kitex. Solo necesita inyectar DubboCodec durante la inicialización para usar el protocolo Dubbo.

Mapeo de tipos y expansión

tipo de mapeo

Dubbo utiliza principalmente el protocolo de serialización Hessian2 para codificar y decodificar la carga útil. Su característica más importante es el tipo de serialización autodescriptivo, es decir, no depende de definiciones de interfaz o esquemas externos. El proceso de serialización se basa en el mapeo entre los tipos de lenguaje de programación y los tipos Hessian2. Tomando como ejemplo la conversión de tipos Go en tipos Java:

Después del análisis, encontramos que el sistema de tipos básico de Hessian2 básicamente se superpone con Thrift. Para garantizar que la experiencia de uso de Kitex + codec-dubbo sea básicamente la misma que la de Kitex + Thrift, generamos el código de andamio Kitex Dubbo-Hessian2 basado en Thrift IDL. En este momento, el proceso de conversión de tipos es el siguiente:

Consulte la asignación de tipos oficial dubbo-go-hessian2 de Dubbo . codec-dubbo proporciona la siguiente asignación de tipos (aquí solo se incluye una parte de la asignación. Para obtener más precauciones, consulte el archivo Léame de codec-dubbo):

De acuerdo con el mapeo de tipos proporcionado por codec-dubbo, podemos convertir fácilmente la definición de la interfaz Dubbo en Thrift IDL, usar la herramienta de línea de comando Kitex para generar el código de andamiaje del proyecto y finalmente inyectar DubboCodec para completar la comunicación Kitex -> Dubbo. Tome la siguiente definición de interfaz Dubbo como ejemplo:

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;
    }
}

El archivo api.thrift correspondiente es el siguiente. Cabe señalar que las definiciones de estructura que contiene deben anotarse con JavaClassName, que corresponde al paquete + nombre de clase en la definición de la interfaz Dubbo.

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)
}

Utilice la herramienta de línea de comando Kitex y especifique el protocolo como Hessian2:

kitex -module demo-client -protocol Hessian2 ./api.thrift

Luego inicialice DubboCodec e inyéctelo en Kitex. Utilice el código generado para escribir el siguiente código de cliente para implementar la llamada de Kitex -> Dubbo:

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)

El proceso del lado del servidor Kitex + codec-dubbo es básicamente similar al proceso del lado del cliente. Para ejemplos específicos, consulte la página de inicio del proyecto.

Expansión de tipo

La característica sin esquema de Hessian2 hace que la implementación de Dubbo sea "demasiado flexible" y pueda usar cualquier tipo. Para adaptarse a la flexibilidad de uso de tipos de Dubbo Hessian2, codec-dubbo admite la expansión de tipos, que incluye principalmente mapeo personalizado y expansión de tipos comunes de Java.

Mapeo personalizado

Los tipos básicos de Java tienen tipos contenedores correspondientes, como booleany java.lang.Boolean. La asignación predeterminada boolde tipos Go a java.lang.Booleantipos Java en la asignación de tipos no cubre el uso booleande . Para unificar la experiencia del usuario y permitirles usar solo boolel tipo en el lado de Kitex, podemos agregar anotaciones después de la definición del método Thrift hessian.argsType="boolean", usar la función de reflexión IDL de thriftgo para generar metainformación IDL por adelantado e inyectarla en el códec. dubbo, y luego se puede ejecutar. java.lang.BooleanReescribe dinámicamente el tipo de mapeo predeterminado en boolean. La definición específica de ahorro es la siguiente:

service EchoService {
   bool EchoBoolean(1: bool req) (hessian.argsType="boolean")
}

De manera similar a booleany java.lang.Boolean, otros tipos básicos de Java y tipos de contenedor también se pueden personalizar de esta manera. En este momento, la asignación de tipos completa proporcionada por codec-dubbo es la siguiente:

expansión de tipo común de java

Debido a las limitaciones de los tipos Thrift, no podemos utilizar directamente los tipos comunes proporcionados en las bibliotecas de clases Java. Con este fin, codec-dubbo mantiene los tipos de Java que Thrift no admite (por ejemplo , ) y el java.thrift correspondiente en el paquete codec-dubbo/java , al mismo tiempo, con la ayuda de la función idl-ref proporcionada. A través de thriftgo, podemos usar Thrift directamente. Se hace referencia a estos tipos en IDL y se genera el código correspondiente. El java.thrift actual tiene este aspecto:java.lang.Objectjava.util.Date

struct Object {} (JavaClassName="java.lang.Object")

struct Date {} (JavaClassName="java.util.Date")

struct Exception {} (JavaClassName="java.lang.Exception")

Para habilitar estos tipos, debemos importarlos en Thrift IDL include "java.thrift"y agregar parámetros al generar código usando la herramienta de línea de comandos Kitex -hessian2 java_extensionpara extraer el paquete de expansión.

La herramienta de línea de comandos Kitex descargará automáticamente java.thrift. También puede descargarlo manualmente y colocarlo en el directorio raíz del proyecto. Ejemplo de Thrift IDL que hace referencia a tipos en 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)
}

Sobrecarga de métodos

Go no admite de forma nativa la sobrecarga de métodos. Solo puede lograr efectos similares a los de una sobrecarga definiendo múltiples métodos. Para mapear múltiples métodos en Go a métodos sobrecargados en Java, similar a la sección de mapeo personalizado, agregamos la etiqueta JavaMethodName después de la definición del método en Thrift y usamos la función de reflexión IDL de thriftgo para mapear dinámicamente Go en tiempo de ejecución. El nombre del método se reescribe en el método sobrecargado en Java especificado por JavaMethodName.

Tome EchoMethod en el lado de Java como ejemplo:

String EchoMethod(Boolean req);
String EchoMethod(Integer req);
String EchoMethod(int req);
String EchoMethod(Boolean req1, Integer req2);

Escribimos la siguiente definición de Thrift para completar el mapeo del método sobrecargado entre Go y Java. Tenga en cuenta JavaMethodNameque y hessian.argsTypese puede usar al mismo tiempo:

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")
 }

Manejo de excepciones

codec-dubbo asigna excepciones en Java a errores en Go. Estos errores implementan de manera uniforme la siguiente interfaz:

type Throwabler interface {
    Error() string
    JavaClassName() string
    GetStackTrace() []StackTraceElement
}

Según las prácticas de manejo de excepciones recomendadas oficialmente por Dubbo y las necesidades actuales de los usuarios empresariales, dividimos las excepciones en excepciones comunes y excepciones personalizadas, teniendo en cuenta las necesidades básicas y de escalabilidad de los usuarios.

Excepciones comunes

codec-dubbo proporciona excepciones comunes de Java en el paquete pkg/hessian2/exception , que actualmente admite java.lang.Exception.

Las excepciones comunes no requieren soporte de la herramienta de línea de comandos de Kitex y se pueden citar directamente. Los siguientes son ejemplos de excepciones extraídas por el cliente y excepciones devueltas por el servidor.

Excepción de extracción de cliente

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)
    }
}

El lado del servidor devuelve una excepción

func (s *GreetServiceImpl) Greet(ctx context.Context, req *hello.GreetRequest) (resp *hello.GreetResponse, err error) {
    return nil, hessian2_exception.NewException("Your detailed message")
}

Excepción personalizada

Las excepciones personalizadas en Java a menudo heredan una excepción básica. Aquí, tomando como CustomizedExceptionejemplo , CustomizedExceptionhereda java.lang.Exception:

public class CustomizedException extends Exception {
    private final String customizedMessage;
    
    public CustomizedException(String customizedMessage) {
        super();
        this.customizedMessage = customizedMessage;
    }
}

Gracias al soporte de thriftgo para generar estructuras anidadas, para definir las excepciones correspondientes en el lado de Kitex, escribimos la siguiente definición en 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")

Preste atención exceptiona las anotaciones de campo thrift.nested="true", que permiten a thriftgo generar estructuras anidadas para lograr un efecto similar a la herencia.

Al igual que las extensiones de tipo común de Java, debe agregar parámetros cuando utilice la herramienta de andamio kitex para generar código -hessian2 java_extensionpara extraer el paquete de expansión. El código generado es el siguiente:

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"`
}

El método de uso es consistente con excepciones comunes y no se repetirá aquí.

Registro y descubrimiento de servicios.

Dubbo proporciona modelos de registro y descubrimiento de servicios a nivel de interfaz y a nivel de aplicación. Según las necesidades actuales del entorno de producción de los usuarios empresariales, elegimos priorizar el modelo a nivel de interfaz basado en zookeeper: Dubbo registra-zookeeper.

A diferencia del modelo a nivel de aplicación que conocemos, el modelo a nivel de interfaz necesita mantener la relación de mapeo de nombre de interfaz => servicio (a diferencia de los microservicios, más cercano a Handler, se asignará un nombre de interfaz a múltiples servicios). estos servicios pueden existir en el mismo en un proceso.

Teniendo en cuenta que el modelo de servicio a nivel de interfaz de Dubbo es bastante diferente del modelo de servicio de Kitex, y que el registro-zookeeper de Dubbo debe vincularse al códec-dubbo para su uso, no se considera modificar el registro-zookeeper original en kitex-contrib para crear el registro de dubbo. -zookeeper Se desarrolla y mantiene de manera uniforme un módulo sub-go de codec-dubbo.

Teniendo en cuenta el modelo de servicio a nivel de interfaz de Dubbo, la API de Kitex y la experiencia del usuario, proporcionamos los siguientes niveles de configuración:

  1. Las funciones WithServers y WithRegistryGroup en registro/options.go y resolver/options.go proporcionan configuración a nivel de registro, especificando respectivamente la dirección del cuidador del zoológico y el grupo al que pertenecen estos cuidadores del zoológico. Los usuarios utilizan estas funciones para generar Kitex registry.Registrye discovery.Resolverinstancias.
  2. La configuración de nivel de servicio se pasa client.WithTagcon y , registries/common.go proporciona claves de etiqueta, que corresponden a los metadatos del servicio en Dubbo uno a uno.server.WithRegistryInfo

ejemplo de resolución

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 调用

ejemplo de registro

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

Resumir

Kitex admite el protocolo Dubbo, que es un gran paso para que CloudWeGo ayude a integrar la ecología nativa de la nube en varios idiomas. Resuelve los puntos débiles de muchos usuarios empresariales al convertir Java a Go y coexistir entre Java y Go. intente acceder a él si encuentra algún problema durante el uso. Si tiene alguna pregunta, puede unirse a nuestro grupo de usuarios de Feishu o enviarnos sus comentarios en Github.

Los recursos pirateados de "Celebrating More Than Years 2" se cargaron en npm, lo que provocó que npmmirror tuviera que suspender el servicio unpkg. El equipo de inteligencia artificial de Microsoft en China empacó colectivamente y se fue a los Estados Unidos, involucrando a cientos de personas. Biblioteca de visualización frontal y el conocido proyecto de código abierto ECharts de Baidu: "ir al mar" para respaldar a Fish. ¡ Los estafadores utilizaron TeamViewer para transferir 3,98 millones! ¿Qué deberían hacer los proveedores de escritorio remoto? Zhou Hongyi: No queda mucho tiempo para que Google recomiende que todos los productos sean de código abierto. Un ex empleado de una conocida empresa de código abierto dio la noticia: después de ser desafiado por sus subordinados, el líder técnico se enfureció. Despidió a la empleada embarazada. Google mostró cómo ejecutar ChromeOS en una máquina virtual de Android. Por favor, dame un consejo, ¿qué papel juega aquí time.sleep(6)? Microsoft responde a los rumores de que el equipo de IA de China está "haciendo las maletas para Estados Unidos" El People's Daily Online comenta sobre la carga tipo matrioska del software de oficina: Sólo resolviendo activamente los "conjuntos" podremos tener un futuro
{{o.nombre}}
{{m.nombre}}

Supongo que te gusta

Origin my.oschina.net/u/4843764/blog/11044578
Recomendado
Clasificación