Pensamiento profundo sobre el marco rpc cara a cara cuatro

7 Cierta comprensión del mecanismo netty

Lectura recomendada: Pensamiento profundo sobre el marco de programación de la red netty

7.1 Números de puerto admitidos por Netty :

Netty puede vincularse a cualquier número de puerto legal, similar a la mayoría de las bibliotecas de redes. Los puertos válidos van del 0 al 65535, pero por lo general se recomienda utilizar puertos superiores al 1024 porque los puertos del 0 al 1023 están reservados para servicios conocidos.

7.2 Netty utiliza el mecanismo NIO :

  • NIO y canal de multiplexación :
    Netty se basa en Java NIO (IO sin bloqueo), y su núcleo es un mecanismo de devolución de llamadas y basado en eventos. Cuando hablamos de "canal de multiplexación", generalmente nos referimos a la tecnología de multiplexación de IO, como los mecanismos de selección, sondeo y epoll en los sistemas Unix. Estos mecanismos permiten que un único subproceso supervise los eventos de E/S de varias conexiones (como lectura, escritura, etc.), mejorando así las capacidades de procesamiento concurrente.

  • Configuración manual del programador :
    Netty proporciona una abstracción de alto nivel, por lo que los desarrolladores no necesitan interactuar directamente con la API de multiplexación de bajo nivel. La multiplexación de IO es automática cuando los desarrolladores crean y configuran Channely otros componentes de Netty. EventLoopPero, por supuesto, Netty también brinda suficiente flexibilidad para que los usuarios avanzados puedan ajustar y optimizar el comportamiento.

7.3 Protocolo de capa de transporte predeterminado de Netty :

Cuando crea un servidor o cliente de Netty sin especificar explícitamente el tipo de transporte, Netty usa TCP como su transporte de manera predeterminada. Sin embargo, siempre es una buena práctica especificar explícitamente el tipo de transporte que desea utilizar. Por ejemplo, en Netty, puede elegir NioServerSocketChannelel NioSocketChanneltransporte TCP basado en NIO u otros tipos de canales, como NioDatagramChannelel transporte UDP.

7.4 El canal aquí es equivalente a establecer una conexión con un determinado cliente. ¿EventLoop es equivalente a llamar al método de selección para monitorear estas conexiones y luego entregar estas conexiones al identificador correspondiente de acuerdo con el modelo de hilo?

Sí, su comprensión es mayormente precisa. El concepto de Netty tiene muchas similitudes con el modelo de programación de red tradicional, pero brinda a los desarrolladores una abstracción de mayor nivel y opciones de configuración más flexibles. La siguiente es una descripción general y un complemento de varios puntos clave que mencionó:

  1. Canal :

    En Netty, Channeles la abstracción central de la comunicación en red, que representa una conexión o un canal de transmisión de datos. Para TCP, uno Channelcorresponde a una conexión entre un cliente y un servidor.

  2. Bucle de eventos :

    EventLoopResponsable de manejar eventos de red y realizar operaciones de E/S. De hecho, uno EventLoopsuele corresponder a un subproceso, y este subproceso utiliza internamente técnicas de multiplexación como select, pollo epollpara escuchar Channeleventos en varios subprocesos. De esta manera, un subproceso (o EventLoop) puede administrar y manejar múltiples conexiones de manera eficiente.

  3. Modelo de hilo y controlador :

    Netty tiene un EventLoopGroupconcepto llamado , que es EventLoopuna colección de , que generalmente se usa para representar un grupo de subprocesos de trabajo. Cuando se acepta una nueva conexión, se le asignará a EventLoopGroupuno de ellos EventLoop, y durante toda la vida de la conexión, todas las operaciones de E/S son EventLoopmanejadas por este (es decir, un hilo fijo) , lo que garantiza Eventos relacionados con un determinado siempre se procesan secuencialmente por el mismo subproceso.

    Cuando EventLoopdetecta Channelun evento en un objeto (por ejemplo, los datos son legibles), pasa el evento al Handlercontrolador correspondiente. HandlerEs un componente utilizado en Netty para manejar varios eventos de red (como lectura, escritura, conexión, desconexión, etc.).

  4. Tubería :

    Netty también tiene un ChannelPipelineconcepto, que es ChannelHandleruna lista enlazada de Channeleventos entrantes y salientes para su procesamiento o intercepción. Esto proporciona a los desarrolladores una poderosa herramienta para combinar y reutilizar varias lógicas de procesamiento.

En resumen, Netty proporciona un conjunto potente y flexible de herramientas y abstracciones que hacen que la programación de redes sea más fácil y eficiente.

8 Servicio de registro/descubrimiento/fuera de línea y equilibrio de carga (lado de Alibaba)

8.1 ¿Cuándo interactúa un marco rpc con el centro de registro, puede presentarlo?

El marco RPC y el centro de registro interactúan principalmente en los siguientes momentos:

  • Registro del servicio: cuando el proveedor de servicios se inicia, registrará la información del servicio que proporciona al centro de registro. Por lo general, este proceso ocurre durante la fase de inicialización del inicio del proveedor de servicios.

  • Detección de servicios: una vez que el consumidor del servicio se inicia, obtendrá la información de servicio requerida del centro de registro.Este proceso se denomina detección de servicios. El descubrimiento de servicios puede ocurrir cuando se inicia el consumidor de servicios, o puede realizarse periódicamente durante la operación para obtener la información de servicio más reciente.

  • Servicio fuera de línea: cuando un proveedor de servicios cierra o no brinda servicios por algún motivo, se eliminará del centro de registro. Este proceso generalmente ocurre cuando el proveedor de servicios cierra.

  • Cambio de servicio: cuando cambia el estado operativo de sí mismo, puede informar al centro de registro

8.2 ¿Qué información se registrará cuando se registre el servicio?

Respuesta correcta:
el registro del servicio es una parte muy importante del uso del marco RPC (llamada a procedimiento remoto). Durante el proceso de registro del servicio, la siguiente es información que normalmente se registra:

  • Identificador de servicio (diferente del ID de instancia): Este es un identificador global único que se utiliza para distinguir servicios individuales. Este identificador suele incluir el nombre del servicio y el número de versión , para que los consumidores puedan seleccionar la versión correspondiente del servicio según sus necesidades.
  • Dirección del servicio: los servicios registrados deben proporcionar la dirección de red donde se pueden encontrar y acceder, que generalmente incluye la dirección IP y el número de puerto.
  • Metadatos de la interfaz del servicio: esto incluye información adicional del servicio, como la descripción del servicio, el estado del servicio (como el estado de salud), el método o los detalles , etc.
  • Información del proveedor de servicios: puede contener información de la máquina del proveedor, como CPU, memoria, etc., para facilitar el descubrimiento de servicios y los sistemas de equilibrio de carga para tomar decisiones más razonables.

Esta información quedará registrada en el centro de registro de servicios para que los consumidores puedan consultarla y utilizarla. Tenga en cuenta que la información que debe registrar puede variar con diferentes marcos RPC y escenarios de uso.

8.3 ¿Cómo hacer el enrutamiento del cliente rpc (se trata de cómo equilibrar la carga)? ¿Admite operaciones de enrutamiento personalizadas (estrategia de equilibrio de carga personalizada)?

La forma real de lograr la expansión:

El marco RPC generalmente proporciona algunas estrategias integradas de balanceo de carga, como round robin, aleatorio, número mínimo de conexiones, etc. Sin embargo, en algunos casos, es posible que estas políticas integradas no satisfagan las necesidades comerciales específicas. Muchos marcos RPC también tienen en cuenta esta situación, por lo que admiten estrategias de equilibrio de carga definidas por el usuario.
Tome algunos marcos RPC comunes como ejemplos:

  • En Dubbo, los usuarios pueden definir su propia estrategia de equilibrio de carga implementando la interfaz LoadBalance. En el lado del consumidor del servicio, puede usar una estrategia de equilibrio de carga personalizada a través de @Reference(loadbalance = "myLoadBalance").

Al implementar una estrategia de equilibrio de carga personalizada, se deben considerar factores como el estado del servicio, el retraso de la red y la carga del servidor. Además, también debe prestar atención a los problemas de rendimiento y seguridad de subprocesos.
Tenga en cuenta que el método específico de personalizar la estrategia de balanceo de carga varía con los diferentes marcos RPC. Para una implementación específica, debe consultar los documentos oficiales o materiales relacionados del marco RPC correspondiente.

9 llamadas generalizadas

9.1 ¿Qué es una convocatoria genérica?

Respuesta: En el proceso de llamada general, la firma del método utilizado es clara y clara, pero hay otra situación en la que el servidor en sí mismo no puede proporcionar una firma de API tan clara. En este momento, el servicio y el método que pasa pueden ser una cadena, el El parámetro que pasó en el pasado es un mapa grande, que solo especifica qué solicitud debe llegar al servidor correspondiente, y la capa del servidor hará la reflexión y la búsqueda correspondientes; cuando el cliente obtenga los datos de respuesta, no use objetos SDK conocidos para recibir , pero use genéricos fijos u objetos Object para recibir.

9.1.1 gpt4: ¿Cuál es la llamada generalizada del marco RPC?

Las llamadas genéricas permiten a los clientes llamar a métodos en el servidor sin conocer la definición de la interfaz del servicio en el momento de la compilación. En resumen, permite llamar a servicios remotos sin interfaz de servicio y clase de entidad . Esto es especialmente útil para ciertos escenarios de invocación dinámica, como puertas de enlace API o herramientas de prueba de servicios específicos.

Al utilizar llamadas generalizadas, los consumidores de servicios suelen pasar información detallada, como nombres de servicios, nombres de métodos y parámetros de métodos, y el marco RPC es responsable de convertir esta información en llamadas de servicio reales.

9.1.2 ¿ Qué marcos RPC admiten este tipo de llamada?

La mayoría de los marcos RPC maduros admiten llamadas genéricas. comunes como:

  • Dubbo : el marco RPC de código abierto de Alibaba, Dubbo, admite llamadas generalizadas, lo que permite a los usuarios realizar llamadas a métodos remotos sin conocer la definición del servicio.
  • gRPC : el marco gRPC de Google también admite una funcionalidad similar, aunque puede implementarse de una manera ligeramente diferente que los marcos como Dubbo.
  • Apache Thrift : este es otro marco RPC que admite llamadas generalizadas.

9.1.3 Bajo la llamada de generalización, ¿el proveedor de servicios todavía necesita registrar todos los servicios de interfaz con el centro de registro?

Sí, los proveedores de servicios aún deben registrar sus servicios de interfaz en el registro. Las llamadas genéricas afectan principalmente a los clientes, ya que les permite realizar llamadas sin conocer la definición de la interfaz. El proveedor de servicios aún necesita registrar su servicio e información de interfaz con el centro de registro para que los consumidores puedan descubrirlo y llamarlo. El objetivo principal del registro es el descubrimiento de servicios.Ya sea una llamada tradicional o una llamada generalizada, los consumidores necesitan conocer la dirección del proveedor y otros metadatos.

9.1.4 Por ejemplo, el siguiente método de llamada de mi cliente es una llamada no generalizada:

    public static void main2(String[] args) {
    
    
        RpcClient client = new NettyClient(CommonSerializer.PROTOBUF_SERIALIZER);
        RpcClientProxy rpcClientProxy = new RpcClientProxy(client);
        HelloService helloService = rpcClientProxy.getProxy(HelloService.class);
        HelloObject object = new HelloObject(12, "This is a message");
        String res = helloService.hello(object);
        System.out.println(res);
        ByeService byeService = rpcClientProxy.getProxy(ByeService.class);
        System.out.println(byeService.bye("Netty"));
    }

Respuesta:
Según el código que proporcionó, está utilizando llamadas no genéricas . Este es el por qué:

  1. Interfaces bien definidas : tiene HelloServicedos ByeServiceinterfaces bien definidas y los métodos correspondientes (p. ej. hello()y bye()). En una llamada no genérica, tanto el consumidor del servicio como el proveedor del servicio suelen conocer la definición de esta interfaz.

  2. Tipos explícitos : se utilizan tipos explícitos, p HelloObject. Este es un parámetro de un tipo específico, cuya estructura es conocida tanto por el consumidor como por el proveedor.

  3. Uso de proxy : RpcClientProxycrea un proxy para la interfaz, haciendo que las llamadas remotas parezcan llamadas locales. Sin embargo, aún llama explícitamente helloService.hello(object)a and byeService.bye("Netty"), lo que significa que conoce el método para llamar y los parámetros que deben pasarse.

En las llamadas generalizadas , las llamadas suelen ser más dinámicas y flexibles. Por ejemplo, es posible que solo necesite conocer el nombre del método, los tipos de parámetros y los valores de los parámetros, pero no necesita conocer la interfaz o la definición explícita del método de antemano. Mientras que en una llamada no genérica, como en su ejemplo, todos estos son explícitos y predefinidos.

9.1.5 ¿Me puede dar una demostración de una llamada de generalización simple?

seguro. La llamada generalizada es un método de llamada más flexible y dinámico, que se utiliza principalmente en los siguientes escenarios:

  1. Cuando no conoce el servicio o método específico para llamar.
  2. Cuando el consumidor del servicio y el proveedor del servicio no tienen una definición de interfaz compartida.
  3. Cuando es necesario construir un sistema más flexible que pueda decidir a qué servicio llamar en tiempo de ejecución.

La clave para las llamadas generalizadas es que usted no inicia una llamada basada en una interfaz o clase fija y conocida, sino que construye dinámicamente la información del servicio que se llamará en tiempo de ejecución.

Aquí hay un ejemplo simple de una llamada genérica:

public class GenericInvokeDemo {
    
    

    public static void main(String[] args) throws Exception {
    
    
        RpcClient client = new NettyClient(CommonSerializer.PROTOBUF_SERIALIZER);
        RpcClientProxy rpcClientProxy = new RpcClientProxy(client);

        // 使用泛化调用,不需要预先定义接口
        String serviceName = "HelloService";
        String methodName = "hello";
        
        // 这里仅仅是为了示意,实际的参数名和参数类型可能需要通过其他方式获得
        String[] parameterTypes = new String[] {
    
    "HelloObject"};
        Object[] arguments = new Object[] {
    
    new HelloObject(12, "This is a message")};

        Object result = invoke(rpcClientProxy, serviceName, methodName, parameterTypes, arguments);
        System.out.println(result);
    }

    public static Object invoke(RpcClientProxy proxy, String serviceName, String methodName, String[] parameterTypes, Object[] arguments) throws Exception {
    
    
        // 构建RpcRequest,通常需要更加复杂的逻辑来处理参数类型和参数值
        RpcRequest request = new RpcRequest();
        request.setInterfaceName(serviceName);
        request.setMethodName(methodName);
        request.setParameters(arguments);
        // ... 其他属性设置

        // 使用RpcClientProxy发送请求
        RpcResponse response = proxy.sendRequest(request);
        
        // 检查响应并返回结果
        if (response.getStatus() == RpcResponse.SUCCESS) {
    
    
            return response.getData();
        } else {
    
    
            throw new Exception("RPC调用失败: " + response.getMessage());
        }
    }
}

En este ejemplo, en lugar de usar HelloServicela interfaz o sus hellométodos directamente, construimos serviceNamey methodNamedeterminamos dinámicamente a qué servicio llamar. Esto nos permite realizar llamadas RPC sin conocer de antemano el servicio específico.

Tenga en cuenta que este es solo un ejemplo simplificado, y la llamada de generalización real puede implicar una lógica más compleja, como el manejo de diferentes tipos de parámetros, el manejo de diferentes tipos de devolución, etc.

9.2 ¿Por qué necesitamos llamadas generalizadas?

Respuesta: La principal ventaja de las llamadas generalizadas es que proporciona un método de llamadas muy flexible, de modo que la persona que llama no tiene que depender de la definición de interfaz específica del servidor. Las llamadas generalizadas tienen los siguientes escenarios de aplicación y beneficios principales:

  1. Llamada dinámica : cuando la cantidad de interfaces es enorme o las interfaces cambian con frecuencia, el uso de llamadas generalizadas puede evitar actualizaciones frecuentes del código o SDK del cliente.

  2. Puentear diferentes sistemas : para algunas aplicaciones que necesitan funcionar en plataformas o idiomas, las llamadas de generalización se pueden usar como una capa intermedia para unir diferentes sistemas, sin tener que personalizar las API para cada sistema.

  3. Pruebas y desarrollo de herramientas : las llamadas generalizadas pueden crear fácilmente herramientas de prueba, lo que permite a los desarrolladores o probadores llamar dinámicamente a los servicios sin conocer de antemano la implementación específica de los servicios.

  4. Puerta de enlace y gestión de API : para las puertas de enlace de API o las plataformas de gestión de servicios, las llamadas generalizadas les permiten reenviar solicitudes a servicios específicos, al tiempo que proporcionan políticas unificadas de registro, supervisión y seguridad.

  5. Reducir la redundancia de código : Las llamadas generalizadas evitan generar y mantener código de cliente para cada servicio o interfaz, reduciendo los costos de mantenimiento.

9.3 ¿Cuáles son las desventajas de las llamadas genéricas?

Respuesta: Aunque la generalización proporciona mucha flexibilidad, también tiene algunas desventajas:

  1. Sobrecarga de rendimiento : debido al análisis dinámico y la reflexión involucrados, las llamadas generalizadas suelen tener una sobrecarga de rendimiento más alta que las llamadas de métodos específicos.

  2. Mayor complejidad : las llamadas de generalización introducen una lógica adicional de análisis y asignación, lo que puede aumentar la complejidad del sistema.

  3. Type-unsafe : debido a que las firmas y los parámetros de los métodos se especifican dinámicamente, pueden generar errores de tiempo de ejecución que son difíciles de detectar en el momento de la compilación.

  4. Experiencia de desarrollo : para los desarrolladores, ninguna definición de interfaz específica puede dificultar su comprensión y uso, y también es difícil aprovechar algunas funciones del IDE, como la finalización de código y las sugerencias.

En general, las llamadas generalizadas son una herramienta potente y flexible, pero es necesario sopesar sus ventajas y desventajas según los escenarios de aplicación específicos.

Protocolo de búfer de 10 protocolos

Lectura recomendada: ¿Cómo transmite protobuf estructuras de datos complejas?

10.1 ¿Qué es un archivo .proto, qué contiene y dónde se almacena generalmente?

  1. Archivo .proto :
    .protoel archivo es un archivo de descripción independiente del idioma de Protocol Buffers (protobuf). Define el formato del mensaje que desea serializar o deserializar a través de protobuf. Las clases de acceso a datos se pueden generar a partir del archivo mediante compiladores específicos del idioma, como protocJava, C++ y Python ..proto

  2. Contenido del archivo .proto :
    .protoel archivo incluye lo siguiente:

    • Definición de mensaje : define los tipos de mensajes y sus campos.
    message Person {
      string name = 1;
      int32 age = 2;
      string email = 3;
    }
    
    • Tipos de datos de campo : tipos de datos básicos como int32, string, bool, bytes, etc., así como tipos personalizados (otros tipos de mensajes o enumeraciones).

    • Definiciones de servicio (opcional): si usa protobuf para la comunicación RPC con gRPC, puede .protodefinir los servicios y sus métodos en el archivo.

    service MyService {
      rpc MyMethod(RequestType) returns (ResponseType);
    }
    
    • Definición de enumeración :
    enum MyEnum {
      UNKNOWN = 0;
      TYPE_A = 1;
      TYPE_B = 2;
    }
    
    • Importar otros .protoarchivos : las definiciones en un archivo se importpueden reutilizar en otro archivo usando la declaración..proto.proto
    import "other_file.proto";
    
    • Paquetes y espacios de nombres : para evitar conflictos de nombres, puede .protodefinir un paquete para un archivo.
    package mypackage;
    
    • Otras opciones : como definir paquetes Java personalizados, modos de optimización, etc.
    option java_package = "com.mycompany.mycode";
    
  3. Dónde se almacenan los archivos .proto :
    .protoEl lugar donde se almacenan los archivos depende de la estructura del proyecto y de la elección del equipo de desarrollo. Cuando los archivos se comparten entre varios proyectos o idiomas .proto, se pueden almacenar en una ubicación central. Para organizaciones más grandes, a veces hay un repositorio Git dedicado para todos .protolos archivos para garantizar el uso compartido y el control de versiones entre los equipos.

    Dentro de un solo proyecto, .protolos archivos generalmente se almacenan junto con el código fuente en un sistema de control de versiones, por ejemplo, en un directorio específico, por ejemplo src/main/proto/(para proyectos Java Maven).

En general, .protolos archivos son una parte central del sistema protobuf y definen la estructura de los mensajes que se serializarán y deserializarán.

10.2 El archivo .proto define el formato del mensaje, por lo que tanto el cliente como el servidor generarán las clases correspondientes en función de este reflejo del archivo, ¿verdad?

Sí, su comprensión es correcta.

Después de definir un .protoarchivo, puede usar el compilador Protocol Buffers protocpara generar el código correspondiente. El código generado contendrá .protolas definiciones de clase para los tipos de mensaje que defina en el archivo.

Por ejemplo, si define un .protoarchivo con los siguientes contenidos:

syntax = "proto3";

message Person {
  string name = 1;
  int32 age = 2;
  string email = 3;
}

Usando protocun compilador, puede generar código para múltiples lenguajes de programación como Java, C++, Python y más. Por ejemplo, si desea generar código para Java, puede usar el siguiente comando:

protoc --java_out=. your_file_name.proto

Después de ejecutar este comando, el compilador generará un archivo Java para usted , que contiene Personla definición de la clase. Esta clase contendrá getters, setters y otros métodos de campo, como serialización y deserialización.

Por lo tanto, tanto el servidor como el cliente pueden usar este .protoarchivo para generar el código correspondiente y asegurarse de que el formato del mensaje entre ellos sea consistente. Este es el poder de los búferes de protocolo: solo necesita definir el formato del mensaje una vez y luego puede generar código para múltiples lenguajes de programación.

10.3 Si se utilizan Protocol Buffers, ¿es necesario implementar la interfaz serializable de objetos serializados en el cliente y el servidor? (Según 10.1 y 2, se sabe que no es necesario)

Cuando usa Protocol Buffers (protobuf) como un marco de serialización, no necesita hacer que sus objetos implementen la interfaz Serializable de Java. De hecho, protobuf tiene su propia forma de definir y serializar datos.

En protobuf, define archivos .proto, que describen sus estructuras de datos. Luego, el compilador protobuf (protoc) generará clases de datos y código de serialización/deserialización para el lenguaje correspondiente. Estos códigos generados no dependen de la interfaz Serializable en Java.

En resumen, si ha elegido usar protobuf, no necesita (y no debería) implementar la interfaz Serializable. Los métodos de serialización y deserialización proporcionados por protobuf son más eficientes y flexibles que el Serializable nativo de Java.

10.4 Cómo entender la compatibilidad de los búferes de protocolo

Uno de los enfoques de diseño de Protocol Buffers (Protobuf para abreviar) es la compatibilidad con versiones anteriores y posteriores. Esto permite que los servicios se iteren e implementen de forma independiente sin interrumpir la comunicación con otros servicios o clientes. Esta compatibilidad es especialmente importante para las arquitecturas de microservicios y los sistemas distribuidos, donde los servicios individuales pueden actualizarse e implementarse de forma independiente.

Estos son algunos puntos clave sobre la compatibilidad de Protobuf:

  1. Compatibilidad con versiones anteriores :

    • Si agrega nuevos campos, cuando la versión anterior del código lee la nueva versión de los datos, ignora los nuevos campos.
    • Si elimina un campo, ya no debe usar el número de ese campo. Las versiones más nuevas del código tratarán el campo como el valor predeterminado (si se proporciona) al leer los datos de la versión anterior.
  2. Compatibilidad con reenvíos :

    • Las versiones anteriores del código pueden analizar los datos producidos por las versiones más nuevas del código (excluyendo los nuevos campos introducidos por el código más nuevo).
    • Los campos nuevos se ignoran en versiones anteriores del código.
  3. Cambios incompatibles :

    • Modificar el número de un campo existente es destructivo.
    • Cambiar el tipo de un campo (por ejemplo, de int32a string) también es destructivo.
  4. Campos reservados :

    • Si elimina un campo, debe .protodeclarar el campo como reservado en el archivo para asegurarse de que el número o nombre del campo no se vuelva a utilizar en el futuro.

Para maximizar la compatibilidad, el consejo general es:

  • No cambie el número y tipo de campos que ya existen.
  • Los campos agregados deben ser opcionales o tener valores predeterminados explícitos.
  • Al eliminar campos, utilice palabras clave reservadas para evitar futuros conflictos.

Este mecanismo de compatibilidad asegura que incluso si las versiones del servidor y del cliente no coinciden exactamente, la comunicación entre ellos funcionará, al menos en la mayoría de los casos.

10.5 ¿Puede dar un ejemplo?

seguro. A través de ejemplos concretos, comprendamos la compatibilidad hacia adelante y hacia atrás de los búferes de protocolo.

Supongamos que tenemos un servicio que gestiona la información de los usuarios. Una versión inicial del .protoarchivo podría verse así:

Archivos para la versión 1 .proto:

message User {
    int32 id = 1;
    string name = 2;
}
  1. Cuando tanto el servidor como el cliente tienen esta versión, pueden comunicarse entre sí sin problemas.

Ahora, decidimos extender este mensaje y agregar un nuevo campo.

Documentación para la versión 2 .proto:

message User {
    int32 id = 1;
    string name = 2;
    string email = 3; // 新增的字段
}
  1. Compatibilidad con versiones anteriores: cuando una nueva versión de un servicio envía un mensaje que contiene emailun campo Usera una versión anterior de un cliente, el cliente anterior ignorará este nuevo campo y procesará otros campos normalmente. Esto es compatibilidad con versiones anteriores.

Más tarde, decidimos que un campo ya no es necesario, por lo que lo eliminamos.

.protoDocumentación para la versión 3 :

message User {
    int32 id = 1;
    string name = 2;
    reserved 3; // 字段编号 3 现在已经被保留
}
  1. Compatibilidad hacia adelante: cuando una versión anterior del servicio (y emailuna versión del campo) envía Userun mensaje a una versión más nueva del cliente, el nuevo cliente verá que el campo está marcado como reservado (aunque el nombre real emailno aparecerá) , por lo tanto, se ignora y los demás campos se procesan normalmente. Esta es la compatibilidad hacia adelante.

Estos ejemplos muestran cómo mantener la compatibilidad de comunicación cuando cambia la estructura del mensaje de Protobuf. Esta compatibilidad facilita enormemente la iteración y el despliegue de sistemas distribuidos.

10.6 ¿Qué es la compatibilidad con versiones anteriores y la compatibilidad con versiones anteriores?

La respuesta a esta pregunta pasa por cómo definimos los conceptos de "antes" y "detrás", y desde qué dirección pensamos en esta compatibilidad.

  1. Compatibilidad con versiones anteriores :

    • Cuando decimos que algo es "compatible con versiones anteriores", generalmente queremos decir que las versiones más nuevas del sistema pueden manejar datos o solicitudes de versiones anteriores. En otras palabras, estamos mirando "hacia atrás", considerando la compatibilidad entre la nueva versión y la versión anterior.
    • Por ejemplo, una nueva versión de software puede leer archivos creados por una versión anterior. O, en este caso, una versión más nueva del servicio puede manejar solicitudes de clientes de versiones anteriores.
    • Escenario real: el servidor usa una nueva versión (como agregar un nuevo campo), pero el cliente aún usa la versión anterior. Cuando el cliente envía datos que no contienen campos nuevos al servidor, el servidor puede procesar los datos normalmente.
  2. Compatibilidad con reenvíos :

    • Cuando decimos que algo es "compatible con versiones anteriores", queremos decir que las versiones anteriores del sistema pueden manejar versiones más nuevas de datos o solicitudes. Estamos mirando "hacia adelante" y pensando en cómo mantener la compatibilidad entre las versiones anteriores y las versiones futuras.
    • Por ejemplo, una versión anterior del software puede leer archivos creados por una versión más nueva. O en este caso, una versión anterior del cliente puede procesar la respuesta de la versión más nueva del servidor.
    • Escenario real: el cliente usa una nueva versión (como agregar un nuevo campo), pero el servidor aún usa la versión anterior. Cuando el servidor envía datos que no contienen nuevos campos al cliente, el cliente puede procesar los datos normalmente e ignorar los nuevos campos.

Esta nomenclatura puede ser confusa, pero es principalmente desde la perspectiva del sistema: cuando pensamos en versiones "posteriores", pensamos en compatibilidad con versiones anteriores, cuando pensamos en versiones "anteriores", pensamos en compatibilidad con versiones anteriores.

La versión antigua y nueva de 10.7 se basa en la comparación entre el servidor y el cliente, y está centrada en el servidor.

Sí, su comprensión es correcta. En muchos sistemas distribuidos y modelos cliente-servidor, especialmente cuando se considera la compatibilidad, el servidor suele considerarse el centro. Hay varias razones para esto:

  1. Control centralizado : El servidor suele tener más control. Por ejemplo, puede decidir cuándo actualizar el software del servidor, pero no puede decidir cuándo actualizar todos los clientes.

  2. Diversos clientes : los clientes pueden venir en una amplia variedad de versiones, plataformas y configuraciones. Teniendo en cuenta esta diversidad, es particularmente importante mantener la estabilidad y compatibilidad del servidor.

  3. Estabilidad y continuidad : el servidor generalmente necesita estar en línea las 24 horas del día, los 7 días de la semana para brindar servicios para varias versiones de clientes. Por lo tanto, es particularmente crítico considerar la compatibilidad del servidor.

  4. Proceso de lanzamiento : en algunos casos, las actualizaciones del lado del servidor pueden estar relativamente centralizadas y controladas, mientras que las actualizaciones del lado del cliente (especialmente en entornos móviles) pueden verse afectadas por factores como la elección del usuario y la compatibilidad del dispositivo.

Entonces, cuando hablamos de compatibilidad, generalmente lo vemos desde la perspectiva del lado del servidor. Por ejemplo, la compatibilidad con versiones anteriores garantiza que las versiones más nuevas de los servidores puedan interactuar con las versiones anteriores de los clientes, mientras que la compatibilidad con versiones anteriores garantiza que las versiones más antiguas de los servidores puedan interactuar con las versiones más nuevas de los clientes. Esta perspectiva centrada en el servidor hace que la administración de versiones y las actualizaciones de protocolos sean relativamente más simples y controlables.

Supongo que te gusta

Origin blog.csdn.net/yxg520s/article/details/132286806
Recomendado
Clasificación