#json-rpc #json-rpc-client #websocket #websocket-client #rpc-client #json

reconnecting-jsonrpsee-ws-client

jsonrpc-ws-client that reconnects automatically. Warning: It may lose subscription messages when reconnecting.

7 unstable releases (3 breaking)

0.4.3 Jun 25, 2024
0.4.2 Apr 24, 2024
0.3.0 Feb 8, 2024
0.2.0 Jan 24, 2024
0.1.0 Jan 4, 2024

#286 in Network programming

Download history 1209/week @ 2024-07-23 1176/week @ 2024-07-30 2097/week @ 2024-08-06 2190/week @ 2024-08-13 2713/week @ 2024-08-20 2471/week @ 2024-08-27 2092/week @ 2024-09-03 3215/week @ 2024-09-10 26676/week @ 2024-09-17 27840/week @ 2024-09-24 32344/week @ 2024-10-01 31047/week @ 2024-10-08 34975/week @ 2024-10-15 34671/week @ 2024-10-22 34382/week @ 2024-10-29 44811/week @ 2024-11-05

155,315 downloads per month

MIT license

58KB
1K SLoC

reconnecting-jsonrpsee-ws-client

Wrapper crate over the jsonrpsee ws client, which automatically reconnects under the hood; without that, one has to restart it. It supports a few retry strategies, such as exponential backoff, but it's also possible to use custom strategies as long as it implements Iterator<Item = Duration>.

By default, the library is re-transmitting pending calls and re-establishing subscriptions that were closed when the connection was terminated, but it's also possible to disable that and manage it yourself.

For instance, you may not want to re-subscribe to a subscription that has side effects or retries at all. Then the library exposes request_with_policy and subscribe_with_policy to support that

    let mut sub = client
        .subscribe_with_policy(
            "subscribe_lo".to_string(),
            rpc_params![],
            "unsubscribe_lo".to_string(),
            // Do not re-subscribe if the connection is closed.
            CallRetryPolicy::Retry,
        )
        .await
        .unwrap();

The tricky part is subscriptions, which may lose a few notifications when it's re-connecting, it's not possible to know which ones.

Lost subscription notifications may be very important to know in some cases, and then this library is not recommended to use.

There is one way to determine how long a reconnection takes:

    // Print when the RPC client starts to reconnect.
    loop {
        rpc.reconnect_started().await;
        let now = std::time::Instant::now();
        rpc.reconnected().await;
        println!(
            "RPC client reconnection took `{} seconds`",
            now.elapsed().as_secs()
        );
    }

Example

use reconnecting_jsonrpsee_ws_client::{rpc_params, Client, ExponentialBackoff, PingConfig};
use std::time::Duration;

async fn run() {
    // Create a new client with with a reconnecting RPC client.
    let client = Client::builder()
        // Reconnect with exponential backoff and if fails more then 
        // 10 retries we give up and terminate.
        .retry_policy(ExponentialBackoff::from_millis(100).take(10))
        // Send period WebSocket pings/pongs every 6th second and
        // if ACK:ed in 30 seconds then disconnect.
        //
        // This is just a way to ensure that the connection isn't
        // idle if no message is sent that often
        //
        // This only works for native.
        .enable_ws_ping(
            PingConfig::new()
                .ping_interval(Duration::from_secs(6))
                .inactive_limit(Duration::from_secs(30)),
        )
        .build("ws://localhost:9944".to_string())
        .await
        .unwrap();

    // make a JSON-RPC call
    let json = client
        .request("say_hello".to_string(), rpc_params![])
        .await
        .unwrap();

    // make JSON-RPC subscription.
    let mut sub = client
        .subscribe(
            "subscribe_lo".to_string(),
            rpc_params![],
            "unsubscribe_lo".to_string(),
        )
        .await
        .unwrap();
    let notif = sub.next().await.unwrap();
}

Dependencies

~4–15MB
~180K SLoC