Skip to content

Commit

Permalink
json-rpc: add a common json-rpc server builder
Browse files Browse the repository at this point in the history
  • Loading branch information
bmwill committed May 23, 2022
1 parent b20d060 commit d2e0d91
Show file tree
Hide file tree
Showing 6 changed files with 144 additions and 131 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions crates/sui-gateway/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ tracing = "0.1.34"
tokio = { version = "1.18.2", features = ["full"] }
futures = "0.3.21"
ed25519-dalek = { version = "1.0.1", features = ["batch", "serde"] }
prometheus_exporter = "0.8.4"

sui_core = { path = "../../sui_core" }
sui-config = { path = "../sui-config" }
Expand Down
126 changes: 126 additions & 0 deletions crates/sui-gateway/src/json_rpc.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
// Copyright (c) 2022, Mysten Labs, Inc.
// SPDX-License-Identifier: Apache-2.0

use anyhow::Result;
use jsonrpsee::{
http_server::{AccessControlBuilder, HttpServerBuilder, HttpServerHandle},
RpcModule,
};
use jsonrpsee_core::{middleware::Middleware, server::rpc_module::Methods};
use prometheus_exporter::prometheus::{
register_histogram_vec, register_int_counter_vec, HistogramVec, IntCounterVec,
};
use serde::Serialize;
use std::{env, net::SocketAddr, time::Instant};
use tracing::info;

pub struct JsonRpcServerBuilder {
module: RpcModule<()>,
server_builder: HttpServerBuilder<JsonRpcMetrics>,
}

impl JsonRpcServerBuilder {
pub fn new() -> Result<Self> {
let mut ac_builder = AccessControlBuilder::default();

if let Ok(value) = env::var("ACCESS_CONTROL_ALLOW_ORIGIN") {
let list = value.split(',').collect::<Vec<_>>();
info!("Setting ACCESS_CONTROL_ALLOW_ORIGIN to : {:?}", list);
ac_builder = ac_builder.set_allowed_origins(list)?;
}

let acl = ac_builder.build();
info!(?acl);

let server_builder = HttpServerBuilder::default()
.set_access_control(acl)
.set_middleware(JsonRpcMetrics::new());

let module = RpcModule::new(());

Ok(Self {
module,
server_builder,
})
}

pub fn register_methods(&mut self, methods: impl Into<Methods>) -> Result<()> {
self.module.merge(methods).map_err(Into::into)
}

pub fn register_open_rpc<T>(&mut self, spec: T) -> Result<()>
where
T: Clone + Serialize + Send + Sync + 'static,
{
self.module
.register_method("rpc.discover", move |_, _| Ok(spec.clone()))?;
Ok(())
}

pub async fn start(self, listen_address: SocketAddr) -> Result<HttpServerHandle> {
let server = self.server_builder.build(listen_address).await?;

let addr = server.local_addr()?;
info!(local_addr =? addr, "Sui JSON-RPC server listening on {addr}");
info!(
"Available JSON-RPC methods : {:?}",
self.module.method_names().collect::<Vec<_>>()
);

server.start(self.module).map_err(Into::into)
}
}

#[derive(Clone)]
struct JsonRpcMetrics {
/// Counter of requests, route is a label (ie separate timeseries per route)
requests_by_route: IntCounterVec,
/// Request latency, route is a label
req_latency_by_route: HistogramVec,
/// Failed requests by route
errors_by_route: IntCounterVec,
}

impl JsonRpcMetrics {
pub fn new() -> Self {
Self {
requests_by_route: register_int_counter_vec!(
"rpc_requests_by_route",
"Number of requests by route",
&["route"]
)
.unwrap(),
req_latency_by_route: register_histogram_vec!(
"req_latency_by_route",
"Latency of a request by route",
&["route"]
)
.unwrap(),
errors_by_route: register_int_counter_vec!(
"errors_by_route",
"Number of errors by route",
&["route"]
)
.unwrap(),
}
}
}

impl Middleware for JsonRpcMetrics {
type Instant = Instant;

fn on_request(&self) -> Instant {
Instant::now()
}

fn on_result(&self, name: &str, success: bool, started_at: Instant) {
self.requests_by_route.with_label_values(&[name]).inc();
let req_latency_secs = (Instant::now() - started_at).as_secs_f64();
self.req_latency_by_route
.with_label_values(&[name])
.observe(req_latency_secs);
if !success {
self.errors_by_route.with_label_values(&[name]).inc();
}
}
}
3 changes: 2 additions & 1 deletion crates/sui-gateway/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// SPDX-License-Identifier: Apache-2.0

pub mod api;
pub mod config;
pub mod json_rpc;
pub mod rpc_gateway;
pub mod rpc_gateway_client;
pub mod config;
40 changes: 6 additions & 34 deletions sui/src/bin/full_node.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,7 @@
// SPDX-License-Identifier: Apache-2.0

use clap::Parser;
use jsonrpsee::{
http_server::{AccessControlBuilder, HttpServerBuilder},
RpcModule,
};
use std::{
env,
net::{IpAddr, Ipv4Addr, SocketAddr},
path::PathBuf,
};
Expand All @@ -16,6 +11,7 @@ use sui::{
sui_full_node::SuiFullNode,
};
use sui_gateway::api::{RpcGatewayOpenRpc, RpcGatewayServer};
use sui_gateway::json_rpc::JsonRpcServerBuilder;
use tracing::info;

const DEFAULT_NODE_SERVER_PORT: &str = "5002";
Expand Down Expand Up @@ -59,40 +55,16 @@ async fn main() -> anyhow::Result<()> {
.unwrap_or(sui_config_dir()?.join("network.conf"));
info!("Node config file path: {:?}", config_path);

let server_builder = HttpServerBuilder::default();
let mut ac_builder = AccessControlBuilder::default();

if let Ok(value) = env::var("ACCESS_CONTROL_ALLOW_ORIGIN") {
let list = value.split(',').collect::<Vec<_>>();
info!("Setting ACCESS_CONTROL_ALLOW_ORIGIN to : {:?}", list);
ac_builder = ac_builder.set_allowed_origins(list)?;
}

let acl = ac_builder.build();
info!("{:?}", acl);

let server = server_builder
.set_access_control(acl)
.build(SocketAddr::new(IpAddr::V4(options.host), options.port))
.await?;

let mut module = RpcModule::new(());
let open_rpc = RpcGatewayOpenRpc::open_rpc();
module.register_method("rpc.discover", move |_, _| Ok(open_rpc.clone()))?;
module.merge(
let address = SocketAddr::new(IpAddr::V4(options.host), options.port);
let mut server = JsonRpcServerBuilder::new()?;
server.register_open_rpc(RpcGatewayOpenRpc::open_rpc())?;
server.register_methods(
SuiFullNode::start_with_genesis(&config_path, &db_path)
.await?
.into_rpc(),
)?;

info!(
"Available JSON-RPC methods : {:?}",
module.method_names().collect::<Vec<_>>()
);

let addr = server.local_addr()?;
let server_handle = server.start(module)?;
info!("Sui RPC Gateway listening on local_addr:{}", addr);
let server_handle = server.start(address).await?;

server_handle.await;
Ok(())
Expand Down
104 changes: 8 additions & 96 deletions sui/src/bin/rpc-server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,23 +2,14 @@
// SPDX-License-Identifier: Apache-2.0

use clap::Parser;
use jsonrpsee::{
http_server::{AccessControlBuilder, HttpServerBuilder},
RpcModule,
};
use jsonrpsee_core::middleware::Middleware;
use prometheus_exporter::prometheus::{
register_histogram_vec, register_int_counter_vec, HistogramVec, IntCounterVec,
};
use std::{
env,
net::{IpAddr, Ipv4Addr, SocketAddr},
path::PathBuf,
time::Instant,
};
use sui_config::sui_config_dir;
use sui_gateway::{
api::{RpcGatewayOpenRpc, RpcGatewayServer},
json_rpc::JsonRpcServerBuilder,
rpc_gateway::RpcGatewayImpl,
};
use tracing::info;
Expand Down Expand Up @@ -64,96 +55,17 @@ async fn main() -> anyhow::Result<()> {
.unwrap_or(sui_config_dir()?.join("gateway.conf"));
info!(?config_path, "Gateway config file path");

let server_builder = HttpServerBuilder::default();
let mut ac_builder = AccessControlBuilder::default();

if let Ok(value) = env::var("ACCESS_CONTROL_ALLOW_ORIGIN") {
let list = value.split(',').collect::<Vec<_>>();
info!("Setting ACCESS_CONTROL_ALLOW_ORIGIN to : {:?}", list);
ac_builder = ac_builder.set_allowed_origins(list)?;
}

let acl = ac_builder.build();
info!(?acl);

let server = server_builder
.set_access_control(acl)
.set_middleware(JsonRpcMetrics::new())
.build(SocketAddr::new(IpAddr::V4(options.host), options.port))
.await?;

let mut module = RpcModule::new(());
let open_rpc = RpcGatewayOpenRpc::open_rpc();
module.register_method("rpc.discover", move |_, _| Ok(open_rpc.clone()))?;
module.merge(RpcGatewayImpl::new(&config_path)?.into_rpc())?;

info!(
"Available JSON-RPC methods : {:?}",
module.method_names().collect::<Vec<_>>()
);

let addr = server.local_addr()?;
let server_handle = server.start(module)?;
info!(local_addr =? addr, "Sui RPC Gateway listening on local_addr");

let prom_binding = PROM_PORT_ADDR.parse().unwrap();
info!("Starting Prometheus HTTP endpoint at {}", PROM_PORT_ADDR);
prometheus_exporter::start(prom_binding).expect("Failed to start Prometheus exporter");

server_handle.await;
Ok(())
}
let address = SocketAddr::new(IpAddr::V4(options.host), options.port);
let mut server = JsonRpcServerBuilder::new()?;
server.register_open_rpc(RpcGatewayOpenRpc::open_rpc())?;
server.register_methods(RpcGatewayImpl::new(&config_path)?.into_rpc())?;

#[derive(Clone)]
struct JsonRpcMetrics {
/// Counter of requests, route is a label (ie separate timeseries per route)
requests_by_route: IntCounterVec,
/// Request latency, route is a label
req_latency_by_route: HistogramVec,
/// Failed requests by route
errors_by_route: IntCounterVec,
}

impl JsonRpcMetrics {
pub fn new() -> Self {
Self {
requests_by_route: register_int_counter_vec!(
"rpc_requests_by_route",
"Number of requests by route",
&["route"]
)
.unwrap(),
req_latency_by_route: register_histogram_vec!(
"req_latency_by_route",
"Latency of a request by route",
&["route"]
)
.unwrap(),
errors_by_route: register_int_counter_vec!(
"errors_by_route",
"Number of errors by route",
&["route"]
)
.unwrap(),
}
}
}
let server_handle = server.start(address).await?;

impl Middleware for JsonRpcMetrics {
type Instant = Instant;

fn on_request(&self) -> Instant {
Instant::now()
}

fn on_result(&self, name: &str, success: bool, started_at: Instant) {
self.requests_by_route.with_label_values(&[name]).inc();
let req_latency_secs = (Instant::now() - started_at).as_secs_f64();
self.req_latency_by_route
.with_label_values(&[name])
.observe(req_latency_secs);
if !success {
self.errors_by_route.with_label_values(&[name]).inc();
}
}
server_handle.await;
Ok(())
}

0 comments on commit d2e0d91

Please sign in to comment.