在之前文章有提到,会采用 web server 的方式提供相关的 restful api,可以在外部观测网络收发包的情况。目前已设计完成,在这里简单分享一下设计过程。
设计 Metric 时,为了减少与 swarm 通信的次数,我们在 control 中放了一份 metric 的 clone。对于 api server 来说,我们完全可以借助 control 提供的 metric 相关操作方法,获得我们想要得到的网络流量数据,以及当前连接的一些相关情况。
Tide 作为 rust 的一个 web 应用框架,实现了一系列相关的路由功能,可以很方便地构建 API;同时,serde 的序列化/反序列化功能,能帮助我们将数据格式化成 json 类型,更容易阅读和解析。
server.at(path).get(method)
at 方法和 get 方法如下所示:
// self为server对象
pub fn at<'a>(&'a mut self, path: &str) -> Route<'a, State> {
let router = Arc::get_mut(&mut self.router)
.expect("Registering routes is not possible after the Server has started");
Route::new(router, path.to_owned())
}
// self为Route对象
pub fn get(&mut self, ep: impl Endpoint<State>) -> &mut Self {
self.method(http_types::Method::Get, ep);
self
}
可以看到,method 参数实际上是一个 impl Trait。
在这个实现了这个 trait 的类型中,有这样一种形式:
#[async_trait]
impl<State, F, Fut, Res> Endpoint<State> for F
where
State: Clone + Send + Sync + 'static,
F: Send + Sync + 'static + Fn(Request<State>) -> Fut,
Fut: Future<Output = Result<Res>> + Send + 'static,
Res: Into<Response> + 'static,
{
async fn call(&self, req: Request<State>) -> crate::Result {
let fut = (self)(req);
let res = fut.await?;
Ok(res.into())
}
}
对应到我们的代码中,泛型 State 为 Control,Fn 我们可以实现为一个 async 的方法,传入参数是 Request,返回值类型为 tide::Result。
1. 从 request 中取出 Control,由于下一步需要可变引用,所以这里要进行clone。
2. 调用 control 的 retrieve_info() 获取 NetworkInfo 数据。
3. 由于 ConnectionInfo 包含了 PeerId,而 PeerId 底层的 Multihash 尚未支持 serde,因此在这里新建了 NetworkConnectionInfo 这个 struct,PeerId 设置为 String 类型,即可实现 serde 的格式化操作。
4. 迭代 network_info 的 connect_info,得到的 vector 与其他数据组合生成 NetworkConnectionStatus。
5. 调用 Body::from_json() 将数据格式化成 json,作为 body 返回。
/// Get connection info
async fn get_connection_info(req: Request<Control>) -> tide::Result {
let mut control = req.state().clone();
let network_info = control.retrieve_networkinfo().await.map_err(|e| {
log::error!("{:?}", e);
tide::Error::new(500, e)
})?;
let mut connection_info = Vec::new();
for item in network_info.connection_info.iter() {
let info = NetworkConnectionInfo {
la: item.la.to_vec(),
ra: item.ra.to_vec(),
local_peer_id: item.local_peer_id.to_string(),
remote_peer_id: item.remote_peer_id.to_string(),
num_inbound_streams: item.num_inbound_streams,
num_outbound_streams: item.num_outbound_streams,
};
connection_info.push(info);
}
let network_connection_status = NetworkConnectionStatus {
num_connections: network_info.num_connections,
num_connections_pending: network_info.num_connections_pending,
num_connections_established: network_info.num_connections_established,
num_active_streams: network_info.num_active_streams,
connection_info,
};
let result_body = Body::from_json(&ResponseBody {
status: 0,
message: "".to_string(),
result: vec![serde_json::to_string(&network_connection_status).unwrap()],
})?;
let response = Response::builder(200).body(result_body).build();
Ok(response)
}
无参数接口
127.0.0.1:8999
127.0.0.1:8999/recv
127.0.0.1:8999/send
127.0.0.1:8999/peer
127.0.0.1:8999/connection
带参数接口
127.0.0.1:8999/peer/_
127.0.0.1:8999/protocol?protocol_id=_
其中,带参数的 peer 接口意为需要传递一个具体的 PeerID。
而 ProtocolID 则使用 param 的方式进行传递。
这个方法的难点在于,我们的 method 实际上是一个返回值类型为 future 的闭包。假设以闭包的形式作为 value,编译器会提示以下错误:
`impl Trait` not allowed outside of function and inherent method return types
意思是 impl Trait 无法作为函数以外的返回值类型。
如果 value 以动态分派作为类型,意味着我们需要以 Box<dyn Endpoint> 作为 value 类型。对于 HashMap 而言,除非直接消耗掉,不然从中取出的数据都是引用类型的,而 clone 方法在此处似乎也是行不通的,返回的仍然是一个 Box 的引用。目前所采用的路由注册方式,在代码的阅读上不太友好,后续考虑用其他方式进行优化。
当前节点向某个目标节点发送字节数(out)和从目标节点获取的字节数(in)大小:
当前节点使用 /ipfs/id/1.0.0 协议所发送(out)和接收(in)的数据包字节大小:
戴健伟,Netwarps 开发团队成员。涉猎过容器、密码学、区块链。喜欢钻研新领域,头很铁,永远都在挑战未知的路上。
Netwarps 由国内资深的云计算和分布式技术开发团队组成,该团队在金融、电力、通信及互联网行业有非常丰富的落地经验。Netwarps 目前在深圳、北京均设立了研发中心,团队规模30+,其中大部分为具备十年以上开发经验的技术人员,分别来自互联网、金融、云计算、区块链以及科研机构等专业领域。
Netwarps 专注于安全存储技术产品的研发与应用,主要产品有去中心化文件系统(DFS)、去中心化计算平台(DCP),致力于提供基于去中心化网络技术实现的分布式存储和分布式计算平台,具有高可用、低功耗和低网络的技术特点,适用于物联网、工业互联网等场景。
Netwarps
共筑家国之梦 共享安全存储