[.NET SignalR] Real-time synchronizatie tussen clients

Pagina: 1
Acties:

Acties:
  • 0 Henk 'm!

  • NickThissen
  • Registratie: November 2007
  • Laatst online: 09-09 10:50
Ik ben bezig met een applicatie die door een aantal clients tegelijk gebruikt zal worden, en ik wil graag alle clients in (min of meer) real-time met elkaar synchronizeren. Denk bijvoorbeeld aan Google Docs waar je met meerdere mensen tegelijk kunt werken aan een excel sheet oid (maar dan iets eenvoudiger). Ik heb echter twee ideeën om de synchronizatie te doen maar kan niet kiezen welke nou beter is.


Laat ik beginnen met een heel erg uitgeklede versie van wat ik wil bereiken, hopelijk is dat genoeg als uitleg, zo niet dan kan ik meer details geven. Ik werk in .NET (C#) en wil graag SignalR gebruiken voor de communicatie, maar voor deze vraag is dat eigenlijk niet zo van belang.

De applicatie heeft een lijst met data. Deze lijst met data moet gesynchronizeerd worden naar alle andere clients. Elke client kan items toevoegen aan de lijst, of bestaande items bewerken, of zelfs verwijderen. Zodra een item toegevoegd, bewerkt of verwijderd wordt moet dat ook in real time (nou ja, toch wel binnen een seconde) bij alle andere clients automatisch updaten.

Naast alle clients moet er natuurlijk ook een server zijn die de communicatie tussen de clients regelt. Tenminste, dat lijkt me de meest logische oplossing. Clients praten niet met elkaar, maar praten altijd met de server, en de server kan dan weer met alle andere clients praten.

Momenteel is de opzet van de client/server communicatie als volgt:
  • De server heeft als enige de definitieve lijst met data welke altijd correct geacht wordt. Dit noem ik de 'State' van de applicatie. In het echt zit er veel meer in dan alleen deze lijst met data maar voor nu is dit een ok voorbeeld.
  • Clients sturen incremental updates van de data naar de server. Bijvoorbeeld: een nieuw item in de lijst, of een bewerkt item, of een id om te verwijderen.
  • De server update dan zijn eigen State (voegt het item toe, bewerkt het item, etc).
  • De server stuurt dan naar alle clients de nieuwe State.
  • Clients ontvangen de state en updaten hun applicatie (de lijst met data) om de nieuwe data te tonen.
Clients hebben dus altijd een kopie van de State, en kunnen alleen updates sturen en nooit een geheel nieuwe State. De server heeft altijd de echte State en stuurt deze in zijn geheel naar alle clients.

De voordelen van deze opzet leken mij:
  • Clients kunnen binnenvallen wanneer ze willen en krijgen meteen de gehele State, zijn dus meteen up-to-date.
  • Berichten van Client naar Server mogen gemist worden (connectie problemen ofzo). De server State wordt dan niet geupdate (update gaat verloren) en de andere clients krijgen de update niet door, maar zodra er een nieuwe update komt krijgen alle clients weer dezelfde State en lopen ze niet 'van elkaar weg'.
  • In andere woorden, de clients zijn altijd (lees: na elke state update) compleet in sync en lopen nooit uit sync omdat ze altijd de gehele state krijgen. Hoogstens gaat er een update verloren maar dat is niet zo erg.
Het grootste nadeel loop ik echter al snel tegenaan, en dat is dat de State vrij groot kan worden. Omdat de gehele lijst met data elke keer opgestuurd moet worden naar alle clients gaat dit nooit schalen en met een lange lijst met data zal het vast in de soep lopen (updates duren te lang etc).


Het alternatief dat ik kan bedenken is om de updates vanuit de server naar de client ook als 'incremental' te doen. Oftewel: een client stuurt een nieuw item, en de server stuurt alleen dat nieuwe item door aan alle andere clients. Dit houdt de communicatie altijd kort, zelfs als de lijst enorm lang is (omdat alleen de updates aan de lijst doorgestuurd worden).

Hier zie ik echter een groot nadeel: clients kunnen 'out of sync' gaan lopen als ze een update missen bijvoorbeeld. Die update komt dan nooit meer aan en omdat ze nooit de hele state ontvangen komt dat ook nooit meer goed. Er zal dus op een of andere manier af en toe gechecked moeten worden of de clients nog in sync zijn (bijvoorbeeld een hash van de state rondsturen en kijken of die nog hetzelfde is). Maar als dan blijkt dat een client niet meer in sync loopt zal toch echt de hele state opgestuurd moeten worden, en ben ik weer terug bij het probleem in mijn eerste idee. Ook als een client later binnenkomt zal hij de state moeten ontvangen.

Dit lijkt me echter minder erg dan elke keer de gehele state opsturen. Een client die laat binnenkomt kan best even een wachtscherm krijgen terwijl de gehele state opgehaald wordt, en in het extreme geval dat iemand niet meer in sync loopt is dat ook niet zo'n punt. Het tweede idee lijkt me dus uiteindelijk beter. Echter volgens mij is dit ook veel ingewikkelder te programmeren...

Echter vraag ik me af hoe dit "normaal" in zijn werk gaat. Dit probleem lijkt me al lang en breed opgelost, want volgens mij moet elke online game (om maar iets te noemen) hier ook mee om gaan. Ik wil voorkomen dat ik het wiel opnieuw ga uitvinden, echter kan ik maar heel weinig informatie vinden over dit soort onderwerpen. Alles wat ik kan vinden gaat meteen veel te diep door in dingen als lag compensatie etc. Daar ben ik niet zo in geinteresseerd. Ik vind het prima als een client een delay van 1 seconde heeft oid.


Long story short: ik kan niet kiezen uit twee methodes om clients te synchronizeren:

1. Clients sturen incremental updates, server stuurt de gehele nieuwe state door aan alle clients.
2. Clients sturen incremental updates, server stuurt incremental updates naar alle clients. Verder geen synchronizatie tussen clients (en dus moet ik gaan checken of de state nog wel in sync is).

Heeft iemand input?

Mijn iRacing profiel


Acties:
  • 0 Henk 'm!

  • xFeverr
  • Registratie: Juni 2011
  • Laatst online: 20:32
Ik vind je manier van incremental updates heel mooi, maar zo moeilijk met hashes hoeft het niet te gaan. Kan je niet gewoon bij elke incremental update naar de client toe een volgnummer meegeven? Als de cliënt update nr. 10982, 10983 en 10984 heeft gehad, weet hij dat alles nog goed loopt. Zodra er 1 of meerdere nummers worden overgeslagen, weet je dar je out of sync loopt, en kan je 'resetten'

Acties:
  • 0 Henk 'm!

  • Sebazzz
  • Registratie: September 2006
  • Laatst online: 19:03

Sebazzz

3dp

Bij SignalR kan je een callback laten uitvoeren bij een 'reconnect'. Hiermee kan je dus je notificatieprobleem oplossen. Wist je dat of werkte dat niet in jouw geval?

[Te koop: 3D printers] [Website] Agile tools: [Return: retrospectives] [Pokertime: planning poker]


Acties:
  • 0 Henk 'm!

  • Pmleader
  • Registratie: Februari 2012
  • Laatst online: 05-08 15:40
Je kunt eventueel ook de complete lijst op de server bij houden, en de clients na elke 5(?) incremental updates een volledige synchronisatie laten doen.

[ Voor 4% gewijzigd door Pmleader op 07-05-2015 13:41 ]


Acties:
  • 0 Henk 'm!

  • EfBe
  • Registratie: Januari 2000
  • Niet online
Incremental updates werken niet omdat dat updates zijn op de state zoals de client die de updates stuurt die ziet, en niet de state van de applicatie op de server. Je moet rekening houden met het feit dat het netwerk traag/uit kan vallen (CAP). Je hebt hier in feite hetzelfde probleem als een distributed database heeft met een aantal replicas.

Ik denk dat je beter commands kunt sturen naar de server die de state op de server bewerken, en dan de state view terugsturen naar de clients. Maar je moet rekening houden met fouttolerantie: commands / state changes op de client zijn gedaan op stale data en die kunnen dus niet matchen met wanneer ze op de real state worden uitgevoerd.

Creator of: LLBLGen Pro | Camera mods for games
Photography portfolio: https://fransbouma.com


Acties:
  • 0 Henk 'm!

  • Xiphalon
  • Registratie: Juni 2001
  • Laatst online: 15:51
Je zou ook de updates via een algoritme als Paxos kunnen distribueren. Of alleen maar om het laatste versienummer van de state door te geven.
Clients die dan zelf zien dat er gaten in de updates zitten kunnen dan de gemiste updates zelf van de server halen.

Acties:
  • 0 Henk 'm!

  • NickThissen
  • Registratie: November 2007
  • Laatst online: 09-09 10:50
Bedankt voor de info. Helaas lees ik conflicterende meningen, ofwel de hele state terugsturen, ofwel de server ook incremental updates laten sturen. Ik denk dat ik gewoon ga proberen om de hele state terug te sturen en bij de ontwikkeling in het achterhoofd hou dat dat wellicht nog moet veranderen later. Misschien dat ik het flexibel genoeg kan maken om beide opties te kunnen gebruiken.

Mijn iRacing profiel


Acties:
  • 0 Henk 'm!

  • creator1988
  • Registratie: Januari 2007
  • Laatst online: 14:44
Lees je hier eens in: Operation transformation. De basis van vrijwel alle collab tools these days.

Acties:
  • 0 Henk 'm!

  • bas09213
  • Registratie: Februari 2015
  • Laatst online: 27-08-2024
Ik denk dat je te moeilijk denkt. De client en de server weten beide wanneer de verbinding uitvalt. Zolang je WebSocket (of equivalent onderliggende technologie gebruikt door SignalR bij gebrek aan support voor WebSockets) open blijft heb je garantie dat alle paketten aankomen (TCP). Ik zou de volgende aanpak hanteren:
  • Nieuwe client maakt verbinding en krijgt de hele lijst
  • Server broadcast wijzigingen naar alle clients (e.g. Item 3 is nu state X, of nieuw item 5)
  • Bij uitval verbinding, laat de oude lijst zien totdat de nieuwe beschikbaar wordt bij reconnect. Dan zou de state read-only moeten zijn. Zo voorkom je complexe (niet automatisch oplosbare) synchronizatieproblemen. E.g. Pietje en Henkie veranderen beide Item 1, wie krijgt voorrang etc.

Acties:
  • 0 Henk 'm!

Verwijderd

Mee eens dat je te moeilijk denkt. Zoals eerder aangegeven heeft SignalR een reconnect event die je zowel op de server als op de client kunt afvangen. De server stuurt dan gewoon de nieuwe state.

Ben het ook eens met EfBe; je moet commando's sturen op basis waarvan de server de state aanpast en incrementele updates broadcast naar alle clients, inclusief de zender.

Acties:
  • 0 Henk 'm!

  • Guillome
  • Registratie: Januari 2001
  • Niet online

Guillome

test

Wat Bas zegt lijkt mij ook de beste oplossing. Niet bij elke wijziging de complete State oversturen, dat is veel data bij veel clients. Je kan prima bijhouden of er nog connectie is en of je nog in synch bent (versienummer of hash meesturen en controleren)

If then else matters! - I5 12600KF, Asus Tuf GT501, Asus Tuf OC 3080, Asus Tuf Gaming H670 Pro, 48GB, Corsair RM850X PSU, SN850 1TB, Arctic Liquid Freezer 280, ASUS RT-AX1800U router

Pagina: 1