centrifuge

repository·master·Indexed 23 days ago

https://github.com/centrifugal/centrifuge

An experimental implementation of WebSockets over HTTP/2 using the RFC 8441 extended CONNECT protocol. The repository includes various examples for the Centrifuge protocol, including JSON and Protobuf binary protocols, real-time document synchronization, concurrency handling, and HTTP/2 WebSocket emulation with HAProxy.

Tokens
33.5K
Snippets
68
Records
214
Agent score
81%

What's inside centrifuge

  1. Overview of the centrifuge websocket package

    master

    The centrifuge websocket package is a fork of the Gorilla WebSocket library, specifically adapted for Centrifuge's requirements. It maintains the original copyrights of the Gorilla authors while introducing specific optimizations and feature changes.

    Key differences from the original Gorilla WebSocket include:

    • HTTP/2 Support: Supports WebSocket over HTTP/2 (RFC 8441) on the server side.
    • Performance: Optimized upgrade process (reducing allocations from 9 to 3, as seen in BenchmarkUpgrade).
    • Protocol Handling: The selected subprotocol is not stored internally; instead, it is returned during the Upgrade or Dial operations.
    • Removed Features:
      • No custom Proxy in Client.
      • No concurrent use best-effort detection.
      • No possibility to set a custom CloseHandler.
    • Other: Includes various lint fixes.
  2. How Real-Time Document Synchronization Works

    master

    This example implements real-time document synchronization by delegating Centrifuge Subscriptions to a helper class called RealTimeDocument.

    Core Logic and Requirements

    • Conflict Resolution: Conflicts are resolved on the server side during transactions. Only committed updates are broadcast in real-time.
    • Versioning: The document must use an incremental version number (e.g., a database incremental field). The frontend uses these versions to compare state and ensure real-time updates are not applied to non-actual versions.
    • Ordering: Messages must be sent to the Centrifugo channel in the correct version order to maintain consistency with frontend version checks.
    • Reliability: It is assumed the backend uses a transactional outbox or Change Data Capture (CDC) approach to ensure document changes are atomically saved and reliably exported to Centrifugo.

    Synchronization Workflow

    To prevent missing updates that occur between the initial data fetch and the establishment of the real-time stream, follow this specific sequence:

    1. Subscribe to the Centrifuge channel first.
    2. Load the document and its current version from the backend second.

    This sequence ensures that any intermediary updates occurring during the loading process are captured by the active subscription.

  3. How native histograms and OTel integration work

    master

    When centrifuge.MetricsConfig.EnableNativeHistograms is enabled, Centrifuge changes how duration metrics are exposed to provide higher fidelity for OpenTelemetry (OTel) backends.

    Specifically, for the command_duration_seconds and survey_duration_seconds metrics:

    1. Summaries are disabled: The traditional Summary metrics are no longer exposed.
    2. Histograms switch to native schema: The companion _histogram metrics switch to a native, sparse, exponential schema.
    3. OTel Translation: An OTel Prometheus bridge translates these native histograms into OTel ExponentialHistogram format, which is the preferred high-fidelity format for most OTel-native backends.

    The data pipeline follows this flow: Centrifuge metrics (native histograms) $\rightarrow$ prometheus.Registry $\rightarrow$ OTel Prometheus bridge $\rightarrow$ OTel SDK PeriodicReader $\rightarrow$ exporter (e.g., stdoutmetric or otlpmetricgrpc).

  4. Scale Centrifuge nodes using Redis sharding

    master
    To scale Centrifuge server nodes, you can connect multiple server instances to a Redis cluster or multiple Redis servers. This example demonstrates how to use Redis sharding to distribute published messages across multiple Redis instances, where messages are consistently sharded by channel. This allows multiple Centrifuge nodes to communicate and synchronize state (like chat messages) across the cluster.
  5. Use Protobuf protocol in the tags filter example

    master

    The tags filter example supports both JSON and Protobuf protocols. By default, the example uses the JSON protocol with a local centrifuge.js build.

    To switch to Protobuf mode, append the protobuf=true query parameter to the URL:

    http://localhost:8000?protobuf=true

    When using Protobuf mode:

    • The example loads centrifuge.protobuf.js from the unpkg CDN.
    • Data encoding and decoding are handled via TextEncoder and TextDecoder.
    • The server-side implementation is identical for both JSON and Protobuf modes.
    http://localhost:8000?protobuf=true
  6. Run the Chat JSON Example

    master

    The Chat JSON example demonstrates a simple chat application using the JSON protocol. The client connects to a Centrifuge server via WebSockets to showcase various capabilities of the Centrifuge protocol.

    To run the example:

    1. Navigate to the example directory.
    2. Execute the application using go run main.go.
    3. Open your browser and navigate to http://localhost:8000 to interact with the chat. You can open multiple tabs to simulate different users publishing messages.
    go run main.go
  7. Run the stress_runtime tool

    master

    The stress_runtime is a self-contained regression tool for the centrifuge server library. It boots an in-process Node and uses centrifuge-go clients over WebSocket to verify the server's feature matrix through self-checking scenarios. Scenarios run in parallel, and the suite exits with a non-zero code if any scenario fails, printing the expected vs. observed values.

    go run .            # run the whole suite (~18s)
    go run -race .      # run with the race detector on the in-process server
    go run . -v         # log server-side disconnects (debugging)
    go run . -only delta_correctness   # run a single scenario
    go run . -d 60s     # change the overall suite deadline
  8. Emulate network latency for HTTP/2 WebSockets

    master

    You can emulate a 100ms Round Trip Time (RTT) by creating two pipes with a 50ms delay each way on port 8080 using dnctl and pfctl on macOS/BSD:

    sudo dnctl pipe 1 config delay 50
    sudo dnctl pipe 2 config delay 50
    sudo pfctl -e
    echo "
    dummynet in proto tcp from any port 8080 to any pipe 1
    dummynet out proto tcp from any to any port 8080 pipe 2
    " | sudo pfctl -f -

    To clean up the latency emulation rules:

    sudo dnctl -q flush
    sudo pfctl -d
  9. Use Node.Survey to collect channel information from all nodes

    master

    The Node.Survey feature allows you to collect channel information (including the number of subscribers) from all running Centrifuge nodes in a cluster. This is useful for monitoring and observing the state of channels across a distributed system.

    To run this example and observe the survey in action, follow these steps:

    1. Start a Redis server locally to act as the pub/sub and state backend:

      redis-server
    2. Start the first Centrifuge instance on port 8000:

      go run main.go -port 8000
    3. Start a second Centrifuge instance on port 8001 in a separate terminal:

      go run main.go -port 8001
    4. Open the instances in a browser: Navigate to http://localhost:8000 and http://localhost:8001 in separate tabs.

    Behavior:

    • Clients will automatically subscribe to channels based on their window.location.host.
    • Every second, a survey will trigger, requesting all active channels and their subscriber counts from all running nodes. The results will be printed to the console.
    redis-server
    go run main.go -port 8000
    go run main.go -port 8001