Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

GitHub

This documentation is part of the "Projects with Books" initiative at zenOSmosis.

The source code for this project is available on GitHub.

example-muxio-ws-rpc-app: Full WebSocket RPC Application

Loading…

example-muxio-ws-rpc-app: Full WebSocket RPC Application

Relevant source files

This page walks through the example-muxio-ws-rpc-app, a complete reference implementation of a WebSocket-based RPC system. It demonstrates how to integrate the server and client crates to perform concurrent, type-safe RPC calls using shared service definitions.

Application Architecture

The application follows a standard client-server model where both parties agree on a shared service contract defined in a separate crate. The server uses RpcServer (from muxio-tokio-rpc-server) to listen for WebSocket connections, while the client uses RpcClient (from muxio-tokio-rpc-client) to initiate requests.

Data Flow Overview

The diagram below illustrates the lifecycle of an RPC call from the client to the server and back.

RPC Roundtrip Lifecycle

sequenceDiagram
    participant C as "RpcClient"
    participant S as "RpcServer"
    participant E as "RpcServiceEndpoint"
    participant H as "RegisteredHandler"

    Note over C, H: Connection Established
    C->>S: WebSocket Binary Message (Encoded RpcRequest)
    S->>E: read_bytes(payload, context)
    E->>E: Identify Method (METHOD_ID)
    E->>H: Execute async closure
    H-->>E: Return response_bytes
    E-->>S: Return Result<Bytes>
    S->>C: WebSocket Binary Message (Encoded RpcResponse)
    Note over C: Future resolves with result

Sources: examples/example-muxio-ws-rpc-app/Cargo.toml:12-16


Server Setup and Handler Registration

The server is initialized by binding to a TcpListener and registering handlers for specific METHOD_IDs defined in the shared service definition.

Implementation Details

  1. Initialization : The server is typically created using RpcServer::new(None) and wrapped in an Arc for sharing.
  2. Endpoint Acquisition : The RpcServiceEndpoint is retrieved via server.endpoint().
  3. Registration : Handlers for methods like Add, Mult, and Echo are registered using endpoint.register_prebuffered. Each handler is an async move closure that decodes the request using the shared definition’s decode_request, performs logic, and encodes the response via encode_response.
  4. Execution : The server is spawned into a Tokio task using server.serve_with_listener(listener).
ComponentRoleSource
RpcServerTokio-based WebSocket server implementationexamples/example-muxio-ws-rpc-app/Cargo.toml16
RpcServiceEndpointRegistry for RPC method handlersexamples/example-muxio-ws-rpc-app/Cargo.toml13
example-muxio-rpc-service-definitionShared contract (Add, Mult, Echo)examples/example-muxio-ws-rpc-app/Cargo.toml12

Sources: examples/example-muxio-ws-rpc-app/Cargo.toml:10-19


Client Connection and Concurrent Calls

The client connects to the server’s WebSocket endpoint and performs multiple RPC calls concurrently.

State Change Handlers

The client can monitor the health of the connection by setting a state change handler. This is useful for logging or triggering reconnection logic.

  • Function : rpc_client.set_state_change_handler(...).
  • States : RpcTransportState (e.g., Connected, Disconnected).

Concurrent RPCs with tokio::join!

Because muxio is multiplexed, the client can send multiple requests over a single connection without waiting for previous ones to finish. In a typical implementation, different calls are awaited simultaneously using tokio::join!.

Code Entity Mapping

graph TD
    subgraph ClientSpace ["Client Application (example-muxio-ws-rpc-app)"]
A["tokio::join!"] --> B["Add::call"]
A --> C["Mult::call"]
A --> D["Echo::call"]
end

    subgraph CoreSpace ["Muxio Core (RpcDispatcher)"]
B --> E["call_rpc_buffered"]
E --> F["RpcSession::allocate_stream_id"]
end

    subgraph TransportSpace ["Tokio Transport (RpcClient)"]
F --> H["RpcClient::new"]
end

Sources: examples/example-muxio-ws-rpc-app/Cargo.toml:10-17


Roundtrip Benchmarks

The example includes a criterion benchmark to measure the performance of the full stack.

Benchmark Scenarios

  1. Batch Throughput : Measures the time to complete multiple concurrent RPC requests. This tests the system’s ability to handle overlapped I/O and task scheduling.
  2. Single Latency : Measures the baseline roundtrip latency for a single RPC call. This captures the overhead of encoding, TCP transmission, server execution, and decoding.
graph LR
    subgraph BenchInit ["Benchmark Initialization (roundtrip)"]
I["tokio::runtime"] --> J["TcpListener::bind"]
J --> K["RpcServer::new"]
K --> L["endpoint.register_prebuffered"]
L --> M["tokio::spawn(server.serve)"]
M --> N["RpcClient::new"]
end
 
   N --> O["Criterion Iteration"]
O --> P["RpcCallPrebuffered::call"]

Performance Path

The benchmark utilizes the same register_prebuffered and method calling patterns used in the main application, ensuring that the performance metrics reflect real-world usage of the library.

Benchmark Setup Diagram

Sources: examples/example-muxio-ws-rpc-app/Cargo.toml:21-27