cloud-game icon indicating copy to clipboard operation
cloud-game copied to clipboard

New net code

Open sergystepanov opened this issue 3 years ago • 0 comments

This PR contains updated network code. The basic communication architecture was left intact.

Changelog

  • All network client IDs now have custom type network.Uid backed by gofrs' UUID(v4) lib and a byte slice.
    The string representation of a UUID takes 32 bytes, and the new type will take just 16.
    Because of Golang JSON serialization problems with omitting zero-length empty slices (it can't) 
    and the need to use UID values as map keys (maps don't support slices as keys), 
    IDs are stored as strings (for now).
    
  • A whole new WebSocket client/server implementation was added, as well as a new communication layer with synchronous and async call handlers.
    • WebSocket connections now support dedicated Ping/Pong frames as opposed to original ping text messages.
    • Used Gorilla WebSocket library doesn't allow concurrent (simultaneous) reads and writes, so this part was handled via send channel synchronization.
  • New API structures can be found in the pkg/api folder.
  • Updated communication protocol is based on JSON-encoded messaging through WebSocket and has the following structure:
    Packet
      [id] string — a globally unique identification tag for the packet to track it trough a chain of requests.
      t uint8 — contains packet type information (i.e. INIT_PACKET, SDP_OFFER_PACKET, ...).
      [p] interface{} — contains packet data (any type).
    
    Each packet is a text message in JSON-serialized form (WebSocket control frames obviously not).
    
    The main principle of this protocol and the duplex data exchange is:
    the one who initializes connection is called a client, and 
    the one who is being connected to is called a server. 
    With the current architecture, the coordinator is the server, the user browsers and workers are the clients.
    
              ____           ____
             ↓    ↑         ↑    ↓
       browser ⟶ coordinator ⟵ worker
         (c)          (s)         (c)
    
    One of the most crucial performance vise parts of these interactions is that 
    all the server-initiated calls to clients should be asynchronous only!
    
    • In order to track synchronous calls (packets) with an asynchronous protocol, such as WebSocket, each packet may have an id that should be copied in all subsequent requests/responses.
    • The old sessionID param was replaced by id that should be stored inside the p (payload) part of the packet.
  • It is possible to skip the default ping check for all connected workers on every user connection and just pick the first available with the new roundRobin param in the coordinator config file.
    coordinator:
      # get any free workers unconditionally on user connection
      roundRobin: true
    ...
    
  • Added structured logging system (zerolog) for better logging and cloud services integration.

TODO

  • [x] Fix slow worker retention on user disconnect.
  • [ ] Migrate CI
  • [ ] Use state machine for async initialization procedure.
  • [ ] Update STREAMING.md.
  • [ ] Check socket read crash when a debugger enabled

sergystepanov avatar Sep 22 '21 20:09 sergystepanov