Esta traducción fue generada mediante aprendizaje automático y puede no ser 100% precisa. Ver versión en inglés

Protocolo de la Granja de Ajo

Proposal 150
Abierto
Author zzz
Created 2019-05-02
Last Updated 2019-05-20

Descripción general

Esta es la especificación del protocolo de red Garlic Farm, basado en JRaft, su código “exts” para implementación sobre TCP, y su aplicación de ejemplo “dmprinter” JRAFT .

No pudimos encontrar ninguna implementación con un protocolo de red documentado. Sin embargo, la implementación de JRaft es lo suficientemente simple como para que pudiéramos inspeccionar el código y luego documentar su protocolo. Esta propuesta es el resultado de ese esfuerzo.

Esto será el backend para la coordinación de routers que publican entradas en un Meta LeaseSet. Ver propuesta 123.

Objetivos

  • Pequeño tamaño de código
  • Basado en una implementación existente
  • Sin objetos Java serializados ni características o codificaciones específicas de Java
  • Cualquier proceso de arranque queda fuera del alcance. Se asume que al menos otro servidor está codificado directamente o configurado fuera de banda respecto a este protocolo.
  • Soportar casos de uso tanto fuera de banda como dentro de I2P.

Diseño

El protocolo Raft no es un protocolo concreto; solo define una máquina de estados. Por lo tanto, documentamos el protocolo concreto de JRaft y basamos nuestro protocolo en él. No hay cambios en el protocolo JRaft más allá de la adición de un handshake de autenticación.

Raft elige un Líder cuyo trabajo consiste en publicar un registro (log). El registro contiene datos de configuración de Raft y datos de aplicación. Los datos de aplicación contienen el estado de cada Router del Servidor y el Destino para el clúster Meta LS2. Los servidores usan un algoritmo común para determinar el publicador y el contenido del Meta LS2. El publicador del Meta LS2 NO es necesariamente el Líder de Raft.

Especificación

El protocolo de red utiliza sockets SSL o sockets I2P sin SSL. Los sockets I2P se transmiten a través del Proxy HTTP. No hay soporte para sockets sin SSL en clearnet.

Handshake y autenticación

No definido por JRaft.

Objetivos:

  • Método de autenticación usuario/contraseña
  • Identificador de versión
  • Identificador de clúster
  • Extensible
  • Facilidad de proxy cuando se usa para sockets I2P
  • No exponer innecesariamente al servidor como servidor Garlic Farm
  • Protocolo simple para que no se requiera una implementación completa de servidor web
  • Compatible con estándares comunes, para que las implementaciones puedan usar bibliotecas estándar si se desea

Usaremos un handshake similar al de websocket y autenticación HTTP Digest RFC 2617 . No se admite la autenticación básica de RFC 2617. Al transmitir a través del proxy HTTP, comuníquese con el proxy según lo especificado en RFC 2616 .

Credenciales

Si los nombres de usuario y contraseñas son por clúster o por servidor, depende de la implementación.

Solicitud HTTP 1

El originador enviará lo siguiente.

Todas las líneas terminan con CRLF según requiere HTTP.


GET /GarlicFarm/CLUSTER/VERSION/websocket HTTP/1.1
  Host: (ip):(port)
  Cache-Control: no-cache
  Connection: close
  (cualquier otra cabecera ignorada)
  (línea en blanco)

  CLUSTER es el nombre del clúster (por defecto "farm")
  VERSION es la versión de Garlic Farm (actualmente "1")

Respuesta HTTP 1

Si la ruta no es correcta, el destinatario enviará una respuesta estándar “HTTP/1.1 404 Not Found”, como en RFC 2616 .

Si la ruta es correcta, el destinatario enviará una respuesta estándar “HTTP/1.1 401 Unauthorized”, incluyendo la cabecera WWW-Authenticate de autenticación digest HTTP, como en RFC 2617 .

Ambas partes cerrarán entonces el socket.

Solicitud HTTP 2

El originador enviará lo siguiente, como en RFC 2617 .

Todas las líneas terminan con CRLF según requiere HTTP.


GET /GarlicFarm/CLUSTER/VERSION/websocket HTTP/1.1
  Host: (ip):(port)
  Cache-Control: no-cache
  Connection: keep-alive, Upgrade
  Upgrade: websocket
  (cabeceras Sec-Websocket-* si está transmitido)
  Authorization: (cabecera de autorización digest HTTP como en RFC 2617)
  (cualquier otra cabecera ignorada)
  (línea en blanco)

  CLUSTER es el nombre del clúster (por defecto "farm")
  VERSION es la versión de Garlic Farm (actualmente "1")

Respuesta HTTP 2

Si la autenticación no es correcta, el destinatario enviará otra respuesta estándar “HTTP/1.1 401 Unauthorized”, como en RFC 2617 .

Si la autenticación es correcta, el destinatario enviará la siguiente respuesta, como en el protocolo WebSocket.

Todas las líneas terminan con CRLF según requiere HTTP.


HTTP/1.1 101 Switching Protocols
  Connection: Upgrade
  Upgrade: websocket
  (cabeceras Sec-Websocket-*)
  (cualquier otra cabecera ignorada)
  (línea en blanco)

Después de recibir esto, el socket permanece abierto. El protocolo Raft, definido a continuación, comienza en el mismo socket.

Caché

Las credenciales deben almacenarse en caché al menos una hora, para que las conexiones posteriores puedan saltar directamente a “Solicitud HTTP 2” anterior.

Tipos de mensajes

Hay dos tipos de mensajes: solicitudes y respuestas. Las solicitudes pueden contener entradas de registro (log entries) y son de tamaño variable; las respuestas no contienen entradas de registro y son de tamaño fijo.

Los tipos de mensaje 1-4 son los mensajes RPC estándar definidos por Raft. Este es el protocolo Raft principal.

Los tipos de mensaje 5-15 son los mensajes RPC extendidos definidos por JRaft, para soportar clientes, cambios dinámicos de servidores y sincronización eficiente del registro.

Los tipos de mensaje 16-17 son los mensajes RPC de compactación de registro definidos en la sección 7 de Raft.

MensajeNúmeroEnviado porEnviado aNotas
RequestVoteRequest1CandidatoSeguidorRPC estándar de Raft; no debe contener entradas de registro
RequestVoteResponse2SeguidorCandidatoRPC estándar de Raft
AppendEntriesRequest3LíderSeguidorRPC estándar de Raft
AppendEntriesResponse4SeguidorLíder / ClienteRPC estándar de Raft
ClientRequest5ClienteLíder / SeguidorLa respuesta es AppendEntriesResponse; debe contener solo entradas de registro de aplicación
AddServerRequest6ClienteLíderDebe contener solo una entrada de registro ClusterServer
AddServerResponse7LíderClienteEl Líder también enviará un JoinClusterRequest
RemoveServerRequest8SeguidorLíderDebe contener solo una entrada de registro ClusterServer
RemoveServerResponse9LíderSeguidor
SyncLogRequest10LíderSeguidorDebe contener solo una entrada de registro LogPack
SyncLogResponse11SeguidorLíder
JoinClusterRequest12LíderNuevo ServidorInvitación para unirse; debe contener solo una entrada de registro de Configuración
JoinClusterResponse13Nuevo ServidorLíder
LeaveClusterRequest14LíderSeguidorComando para salir
LeaveClusterResponse15SeguidorLíder
InstallSnapshotRequest16LíderSeguidorSección 7 de Raft; debe contener solo una entrada de registro SnapshotSyncRequest
InstallSnapshotResponse17SeguidorLíderSección 7 de Raft

Establecimiento

Después del handshake HTTP, la secuencia de establecimiento es la siguiente:


Nuevo Servidor Alice              Seguidor aleatorio Bob

  ClientRequest   ------->
          <---------   AppendEntriesResponse

  Si Bob dice que es el líder, continúe como abajo.
  De lo contrario, Alice debe desconectarse de Bob y conectarse al líder.


  Nuevo Servidor Alice              Líder Charlie

  ClientRequest   ------->
          <---------   AppendEntriesResponse
  AddServerRequest   ------->
          <---------   AddServerResponse
          <---------   JoinClusterRequest
  JoinClusterResponse  ------->
          <---------   SyncLogRequest
                       OR InstallSnapshotRequest
  SyncLogResponse  ------->
  OR InstallSnapshotResponse

Secuencia de desconexión:


Seguidor Alice              Líder Charlie

  RemoveServerRequest   ------->
          <---------   RemoveServerResponse
          <---------   LeaveClusterRequest
  LeaveClusterResponse  ------->

Secuencia de elección:


Candidato Alice               Seguidor Bob

  RequestVoteRequest   ------->
          <---------   RequestVoteResponse

  si Alice gana la elección:

  Líder Alice                Seguidor Bob

  AppendEntriesRequest   ------->
  (heartbeat)
          <---------   AppendEntriesResponse

Definiciones

  • Origen: Identifica al originador del mensaje
  • Destino: Identifica al destinatario del mensaje
  • Términos: Ver Raft. Inicializados a 0, aumentan monótonamente
  • Índices: Ver Raft. Inicializados a 0, aumentan monótonamente

Solicitudes

Las solicitudes contienen un encabezado y cero o más entradas de registro. Las solicitudes contienen un encabezado de tamaño fijo y entradas de registro opcionales de tamaño variable.

Encabezado de solicitud

El encabezado de solicitud tiene 45 bytes, como sigue. Todos los valores son enteros sin signo en orden big-endian.


Tipo de mensaje:      1 byte
  Origen:               ID, entero de 4 bytes
  Destino:              ID, entero de 4 bytes
  Término:              Término actual (ver notas), entero de 8 bytes
  Último término de registro:     entero de 8 bytes
  Último índice de registro:    entero de 8 bytes
  Índice de confirmación:      entero de 8 bytes
  Tamaño de entradas de registro:  Tamaño total en bytes, entero de 4 bytes
  Entradas de registro:       ver abajo, longitud total según especificado

Notas

En RequestVoteRequest, Término es el término del candidato. En otro caso, es el término actual del líder.

En AppendEntriesRequest, cuando el tamaño de las entradas de registro es cero, este mensaje es un mensaje de latido (heartbeat o keepalive).

Entradas de registro

El registro contiene cero o más entradas de registro. Cada entrada de registro es como sigue. Todos los valores son enteros sin signo en orden big-endian.


Término:           entero de 8 bytes
  Tipo de valor:     1 byte
  Tamaño de entrada: En bytes, entero de 4 bytes
  Entrada:          longitud según especificado

Contenido del registro

Todos los valores son enteros sin signo en orden big-endian.

Tipo de valor de registroNúmero
Aplicación1
Configuración2
ClusterServer3
LogPack4
SnapshotSyncRequest5

Aplicación

El contenido de la aplicación está codificado en UTF-8 JSON . Ver la sección Capa de Aplicación más abajo.

Configuración

Se utiliza para que el líder serialice una nueva configuración de clúster y la replique a los pares. Contiene cero o más configuraciones ClusterServer.


Índice de registro:  entero de 8 bytes
  Último índice de registro:  entero de 8 bytes
  Datos ClusterServer para cada servidor:
    ID:                entero de 4 bytes
    Longitud de datos de punto final: En bytes, entero de 4 bytes
    Datos de punto final:     cadena ASCII de la forma "tcp://localhost:9001", longitud según especificado

ClusterServer

La información de configuración para un servidor en un clúster. Esto se incluye solo en un mensaje AddServerRequest o RemoveServerRequest.

Cuando se usa en un mensaje AddServerRequest:


ID:                entero de 4 bytes
  Longitud de datos de punto final: En bytes, entero de 4 bytes
  Datos de punto final:     cadena ASCII de la forma "tcp://localhost:9001", longitud según especificado

Cuando se usa en un mensaje RemoveServerRequest:


ID:                entero de 4 bytes

LogPack

Esto se incluye solo en un mensaje SyncLogRequest.

Lo siguiente se comprime con gzip antes de la transmisión:


Longitud de datos de índice: En bytes, entero de 4 bytes
  Longitud de datos de registro:   En bytes, entero de 4 bytes
  Datos de índice:     8 bytes para cada índice, longitud según especificado
  Datos de registro:       longitud según especificado

SnapshotSyncRequest

Esto se incluye solo en un mensaje InstallSnapshotRequest.


Último índice de registro:  entero de 8 bytes
  Último término de registro:   entero de 8 bytes
  Longitud de datos de configuración: En bytes, entero de 4 bytes
  Datos de configuración:     longitud según especificado
  Desplazamiento:          El desplazamiento de los datos en la base de datos, en bytes, entero de 8 bytes
  Longitud de datos:        En bytes, entero de 4 bytes
  Datos:            longitud según especificado
  Está terminado:         1 si está terminado, 0 si no (1 byte)

Respuestas

Todas las respuestas tienen 26 bytes, como sigue. Todos los valores son enteros sin signo en orden big-endian.


Tipo de mensaje:   1 byte
  Origen:         ID, entero de 4 bytes
  Destino:    Normalmente el ID del destinatario real (ver notas), entero de 4 bytes
  Término:           Término actual, entero de 8 bytes
  Siguiente índice:     Inicializado al último índice de registro del líder + 1, entero de 8 bytes
  Está aceptado:    1 si aceptado, 0 si no aceptado (ver notas), 1 byte

Notas

El ID de destino es normalmente el destinatario real de este mensaje. Sin embargo, para AppendEntriesResponse, AddServerResponse y RemoveServerResponse, es el ID del líder actual.

En RequestVoteResponse, Está aceptado es 1 si se vota por el candidato (solicitante), y 0 si no se vota.

Capa de Aplicación

Cada servidor publica periódicamente datos de Aplicación en el registro mediante una ClientRequest. Los datos de Aplicación contienen el estado de cada Router del Servidor y el Destino para el clúster Meta LS2. Los servidores usan un algoritmo común para determinar el publicador y el contenido del Meta LS2. El servidor con el estado más “óptimo” reciente en el registro es el publicador del Meta LS2. El publicador del Meta LS2 NO es necesariamente el Líder de Raft.

Contenido de los datos de Aplicación

El contenido de la aplicación está codificado en UTF-8 JSON , por simplicidad y extensibilidad. La especificación completa está por determinar. El objetivo es proporcionar suficientes datos para escribir un algoritmo que determine el router “mejor” para publicar el Meta LS2, y para que el publicador tenga suficiente información para ponderar los Destinos en el Meta LS2. Los datos contendrán estadísticas tanto del router como de los Destinos.

Los datos pueden contener opcionalmente datos de detección remota sobre la salud de otros servidores y la capacidad de obtener el Meta LS. Estos datos no estarían soportados en la primera versión.

Los datos pueden contener opcionalmente información de configuración publicada por un cliente administrador. Estos datos no estarían soportados en la primera versión.

Si se lista “nombre: valor”, eso especifica la clave y el valor del mapa JSON. De lo contrario, la especificación está por determinar.

Datos del clúster (nivel superior):

  • cluster: Nombre del clúster
  • date: Fecha de estos datos (long, ms desde la época)
  • id: ID de Raft (entero)

Datos de configuración (config):

  • Cualquier parámetro de configuración

Estado de publicación de MetaLS (meta):

  • destination: destino de metals, en base64
  • lastPublishedLS: si está presente, codificación en base64 del último metals publicado
  • lastPublishedTime: en ms, o 0 si nunca
  • publishConfig: estado de configuración del publicador: off/on/auto
  • publishing: estado booleano del publicador de metals: verdadero/falso

Datos del router (router):

  • lastPublishedRI: si está presente, codificación en base64 de la última información del router publicada
  • uptime: Tiempo de actividad en ms
  • Retraso de tareas (Job lag)
  • Túneles exploratorios
  • Túneles participantes
  • Ancho de banda configurado
  • Ancho de banda actual

Destinos (destinations): Lista

Datos del destino:

  • destination: el destino, en base64
  • uptime: Tiempo de actividad en ms
  • Túneles configurados
  • Túneles actuales
  • Ancho de banda configurado
  • Ancho de banda actual
  • Conexiones configuradas
  • Conexiones actuales
  • Datos de lista negra

Datos de detección remota del router:

  • Última versión de RI vista
  • Tiempo de obtención de LS
  • Datos de prueba de conexión
  • Datos de perfil de floodfills más cercanos para los períodos de ayer, hoy y mañana

Datos de detección remota del destino:

  • Última versión de LS vista
  • Tiempo de obtención de LS
  • Datos de prueba de conexión
  • Datos de perfil de floodfills más cercanos para los períodos de ayer, hoy y mañana

Datos de detección de Meta LS:

  • Última versión vista
  • Tiempo de obtención
  • Datos de perfil de floodfills más cercanos para los períodos de ayer, hoy y mañana

Interfaz de administración

Por determinar, posiblemente una propuesta separada. No requerida para la primera versión.

Requisitos de una interfaz de administración:

  • Soporte para múltiples destinos maestros, es decir, múltiples clústeres virtuales (farms)
  • Proporcionar una vista completa del estado compartido del clúster: todas las estadísticas publicadas por los miembros, quién es el líder actual, etc.
  • Capacidad para forzar la eliminación de un participante o líder del clúster
  • Capacidad para forzar la publicación de metaLS (si el nodo actual es el publicador)
  • Capacidad para excluir hashes de metaLS (si el nodo actual es el publicador)
  • Funcionalidad de importación/exportación de configuración para despliegues masivos

Interfaz del router

Por determinar, posiblemente una propuesta separada. i2pcontrol no es requerido para la primera versión y los cambios detallados se incluirán en una propuesta separada.

Requisitos para la API de Garlic Farm al router (java en-JVM o i2pcontrol)

  • getLocalRouterStatus()
  • getLocalLeafHash(Hash masterHash)
  • getLocalLeafStatus(Hash leaf)
  • getRemoteMeasuredStatus(Hash masterOrLeaf) // probablemente no en MVP
  • publishMetaLS(Hash masterHash, List contents) // o MetaLeaseSet firmado? ¿Quién firma?
  • stopPublishingMetaLS(Hash masterHash)
  • autenticación por determinar?

Justificación

Atomix es demasiado grande y no permitirá la personalización necesaria para enrutar el protocolo sobre I2P. Además, su formato de red no está documentado y depende de la serialización de Java.

Notas

Problemas

  • No hay forma de que un cliente descubra y se conecte a un líder desconocido. Sería un cambio menor que un Seguidor envíe la Configuración como una Entrada de Registro en AppendEntriesResponse.

Migración

No hay problemas de compatibilidad hacia atrás.

Referencias