Messaggio protobuf arbitrario come array di byte su Websocket: come determinare l'effettivo tipo di messaggio in anticipo

1

Protobuf è carino e dandy, ma non è stato creato con l'auto-descrizione in mente. Ora questo è assolutamente ok se stai usando un protocollo ben definito e vuoi sostituirlo per es. Messaggi SOAP o semplicemente vuoi sostituire un'API JSON riposante o qualcosa di simile.

Comunque mi piacerebbe usarlo su un Websocket e invece di chiuderlo e riaprirlo ancora e ancora con URL diversi - che sconfigge il punto del websocket - vorrei tenerlo aperto e inviare diversi messaggi sul collegare come Matrici di byte !

Ora il mio problema è semplice ma complesso da risolvere. Voglio inviare un messaggio arbitrario dal client al server (o viceversa) e il ricevitore dovrebbe determinare in modo semplice quale tipo di messaggio ha ricevuto e come interpretarlo.

In pseudocodice questo dovrebbe essere così:

client :

Message m = new Auth().withUserName("Sorona").withPassword("TotallyNotMyActualPassword")

ws.send(m)

server :

Map[Type, Handler]

receiveMessage(Message m) {
   handlers.get(m.determineType()).handle(m)
}

handler :

trait Handler[T] {
  def handle()
}

Quindi quello che voglio veramente sono due cose:

1) Essere in grado di determinare il tipo effettivo di qualsiasi messaggio senza aumentare (drasticamente) la dimensione del messaggio.

2) Essere robusti ed estendibili

Per un nuovo tipo di messaggio, voglio semplicemente aggiungere un gestore e farlo.

Nessuna enumerazione hardcoded che devo mantenere, nessuna struttura di switch case che devo adottare ecc.

In OOP semplice questo sarebbe abbastanza facile, ma con i Protobufs sono un po 'bloccato.

Qualche suggerimento?

    
posta Sorona 02.02.2017 - 19:57
fonte

2 risposte

0

Ciò che ho inventato finora:

message Root
{
    string type = 1;
    google.protobuf.Any content = 2; 
}

message Dog
{
   string name = 1;
   string fav_food = 2;
}

message Cat
{
   string name = 1;
   uint32 colors = 2; 
}

Quindi da qualche parte (file condiviso, database ecc.) creo una mappa come questa:

val mapper: Map[String, Class] = Map(
  "Dog" -> Dog,
  "Cat" -> Cat
)

Sul mittente, vorrei fare:

val nero = Dog().withName("Nero").withFavoriteFood("Raw meat")
val msg = Root().withType("Dog").withMessage(Any.pack(nero))

e sul lato ricevitore:

val r = Root.parseFrom(msg)
val a = r.message.get.unpack(mapper(r.type))

Questo è fondamentalmente il piano. Vorrei quindi introdurre i miei builder che hanno il type s (ad esempio "Dog" o "Cat") con hardcoded e aggiungerei anche un altro mapper tra una classe / messagetype e un gestore.

Ma immagino che se arriverò così lontano e funzioni (al momento non funziona perché sto usando Scala.js e sembra che Any.pack non sia supportato lì in questo momento) sarà una storia completamente diversa; )

    
risposta data 02.02.2017 - 21:16
fonte
0

Posso pensare a diversi approcci qui:

  • auto che descrive i messaggi (come nella tua proposta)
  • descrizione del messaggio al di fuori dei messaggi (meta-dati)
  • framework rpc (consigliato per progetti complessi)

descrizione nei metadati

Se non si desidera inserire una proprietà che si descrive da sé in ciascun messaggio, è possibile definirli all'esterno e, ad esempio, inviarli tra i messaggi. Ad esempio

stream: ... "now comes a dog", { dog }, "now comes a cat", { cat }, ...

come codifichi "ora arriva un cane" e come lo mapperai al tuo messaggio dipende da te. A seconda del modo in cui strutturi i tuoi identificatori, questo potrebbe essere più complicato di avere un messaggio auto-descrittivo (se usi stringhe di lunghezza arbritaria, ad esempio, dovresti anche inviare quel numero).

messaggi che descrivono autonomamente

il tuo protocollo personalizzato è simile a questo

stream: ..., { self-describing dog }, { self-describing cat }, ...  

utilizzando un framework rpc

potresti anche provare a utilizzare un framework rpc come gRPC che ti darà un design più sofisticato. Qui puoi definire le interfacce di servizio con diversi metodi e parametri. La comunicazione verrà generata e sarà necessario fornire solo le implementazioni del metodo. Tuttavia non sono sicuro che i websocket siano pienamente supportati

    
risposta data 09.02.2017 - 08:53
fonte

Leggi altre domande sui tag