-
Notifications
You must be signed in to change notification settings - Fork 248
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: add
reconnecting-rpc-client
(#1396)
* initial commit * update to reconnecting-ws-client v0.2 * re-export: reconnecting_rpc_client behind feature * add helper function for reconnect * fix nit in example * simplify code without weird error fmt * address grumbles * address grumbles * update reconnecting-ws-client 0.3 * cleanup error message
- Loading branch information
Showing
7 changed files
with
287 additions
and
16 deletions.
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,73 @@ | ||
//! Example to utilize the `reconnecting rpc client` in subxt | ||
//! which hidden behind behind `--feature unstable-reconnecting-rpc-client` | ||
//! | ||
//! To utilize full logs from the RPC client use: | ||
//! `RUST_LOG="jsonrpsee=trace,reconnecting_jsonrpsee_ws_client=trace"` | ||
|
||
#![allow(missing_docs)] | ||
|
||
use std::time::Duration; | ||
|
||
use subxt::backend::rpc::reconnecting_rpc_client::{Client, ExponentialBackoff, PingConfig}; | ||
use subxt::backend::rpc::RpcClient; | ||
use subxt::error::{Error, RpcError}; | ||
use subxt::{OnlineClient, PolkadotConfig}; | ||
|
||
// Generate an interface that we can use from the node's metadata. | ||
#[subxt::subxt(runtime_metadata_path = "../artifacts/polkadot_metadata_small.scale")] | ||
pub mod polkadot {} | ||
|
||
#[tokio::main] | ||
async fn main() -> Result<(), Box<dyn std::error::Error>> { | ||
tracing_subscriber::fmt::init(); | ||
|
||
// Create a new client with with a reconnecting RPC client. | ||
let rpc = Client::builder() | ||
// Reconnect with exponential backoff | ||
// | ||
// This API is "iterator-like" so one could limit it to only | ||
// reconnect x times and then quit. | ||
.retry_policy(ExponentialBackoff::from_millis(100).max_delay(Duration::from_secs(10))) | ||
// Send period WebSocket pings/pongs every 6th second and if it's not 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 | ||
.enable_ws_ping( | ||
PingConfig::new() | ||
.ping_interval(Duration::from_secs(6)) | ||
.inactive_limit(Duration::from_secs(30)), | ||
) | ||
// There are other configurations as well that can be found here: | ||
// <https://docs.rs/reconnecting-jsonrpsee-ws-client/latest/reconnecting_jsonrpsee_ws_client/struct.ClientBuilder.html> | ||
.build("ws://localhost:9944".to_string()) | ||
.await?; | ||
|
||
let api: OnlineClient<PolkadotConfig> = | ||
OnlineClient::from_rpc_client(RpcClient::new(rpc.clone())).await?; | ||
|
||
// Subscribe to all finalized blocks: | ||
let mut blocks_sub = api.blocks().subscribe_finalized().await?; | ||
|
||
// For each block, print a bunch of information about it: | ||
while let Some(block) = blocks_sub.next().await { | ||
let block = match block { | ||
Ok(b) => b, | ||
Err(Error::Rpc(RpcError::DisconnectedWillReconnect(err))) => { | ||
println!("{err}"); | ||
continue; | ||
} | ||
Err(e) => { | ||
return Err(e.into()); | ||
} | ||
}; | ||
|
||
let block_number = block.header().number; | ||
let block_hash = block.hash(); | ||
|
||
println!("Block #{block_number} ({block_hash})"); | ||
} | ||
|
||
println!("RPC client reconnected `{}` times", rpc.reconnect_count()); | ||
|
||
Ok(()) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
// Copyright 2019-2023 Parity Technologies (UK) Ltd. | ||
// This file is dual-licensed as Apache-2.0 or GPL-3.0. | ||
// see LICENSE for license details. | ||
|
||
use super::{RawRpcFuture, RawRpcSubscription, RpcClientT}; | ||
use crate::error::RpcError; | ||
use futures::{FutureExt, StreamExt, TryStreamExt}; | ||
use reconnecting_jsonrpsee_ws_client::SubscriptionId; | ||
use serde_json::value::RawValue; | ||
|
||
impl RpcClientT for reconnecting_jsonrpsee_ws_client::Client { | ||
fn request_raw<'a>( | ||
&'a self, | ||
method: &'a str, | ||
params: Option<Box<RawValue>>, | ||
) -> RawRpcFuture<'a, Box<RawValue>> { | ||
async { | ||
self.request_raw(method.to_string(), params) | ||
.await | ||
.map_err(|e| RpcError::ClientError(Box::new(e))) | ||
} | ||
.boxed() | ||
} | ||
|
||
fn subscribe_raw<'a>( | ||
&'a self, | ||
sub: &'a str, | ||
params: Option<Box<RawValue>>, | ||
unsub: &'a str, | ||
) -> RawRpcFuture<'a, RawRpcSubscription> { | ||
async { | ||
let sub = self | ||
.subscribe_raw(sub.to_string(), params, unsub.to_string()) | ||
.await | ||
.map_err(|e| RpcError::ClientError(Box::new(e)))?; | ||
|
||
let id = match sub.id() { | ||
SubscriptionId::Num(n) => n.to_string(), | ||
SubscriptionId::Str(s) => s.to_string(), | ||
}; | ||
let stream = sub | ||
.map_err(|e| RpcError::DisconnectedWillReconnect(e.to_string())) | ||
.boxed(); | ||
|
||
Ok(RawRpcSubscription { | ||
stream, | ||
id: Some(id), | ||
}) | ||
} | ||
.boxed() | ||
} | ||
} |
Oops, something went wrong.