模块地址:https://github.com/netwarps/libp2p-rs/tree/master/infoserver
在上一篇文章的末尾有提到,会采用web server的方式提供相关的restful api,可以在外部观测网络收发包的情况。目前已设计完成,在这里简单分享一下设计过程。
实现构想
设计Metric时,为了减少与swarm通信的次数,我们在control中放了一份metric的clone。对于api server来说,我们完全可以借助control提供的metric相关操作方法,获得我们想要得到的网络流量数据,以及当前连接的一些相关情况。
框架介绍
Tide作为rust的一个web应用框架,实现了一系列相关的路由功能,可以很方便地构建API;同时,serde的序列化/反序列化功能,能帮助我们将数据格式化成json类型,更容易阅读和解析。
路由注册
以get方法为例,在Tide中,通过以下这种方式实现路由注册:
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) -> &mut Self {
self.method(http_types::Method::Get, ep);
self
}
可以看到,method参数实际上是一个impl Trait。
在这个实现了这个trait的类型中,有这样一种形式:
#[async_trait]
impl Endpoint for F
where
State: Clone + Send + Sync + 'static,
F: Send + Sync + 'static + Fn(Request) -> Fut,
Fut: Future
对应到我们的代码中,泛型State为Control,Fn我们可以实现为一个async的方法,传入参数是Request,返回值类型为tide::Result。
方法分析
以获取NetworkInfo的代码进行分析:
- 从request中取出Control,由于下一步需要可变引用,所以这里要进行clone。
- 调用control的retrieve_info()获取NetworkInfo数据。
- 由于ConnectionInfo包含了PeerId,而PeerId底层的Multihash尚未支持serde,因此在这里新建了NetworkConnectionInfo这个struct,PeerId设置为String类型,即可实现serde的格式化操作。
- 迭代network_info的connect_info,得到的vector与其他数据组合生成NetworkConnectionStatus。
- 调用Body::from_json()将数据格式化成json,作为body返回。
/// Get connection info
async fn get_connection_info(req: Request) -> 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的方式进行传递。
未解难点
在设计路由注册时,有尝试过这么一种方式:生成一个HashMap常量,key为path,value为method,统一管理所有的路由。执行new()方法的时候,迭代这个hashmap,将路由信息注册到server中。
这个方法的难点在于,我们的method实际上是一个返回值类型为future的闭包。假设以闭包的形式作为value,编译器会提示以下错误:
`impl Trait` not allowed outside of function and inherent method return types
意思是impl Trait无法作为函数以外的返回值类型。
如果value以动态分派作为类型,意味着我们需要以Box
部分效果展示
当前节点向某个目标节点发送字节数(out)和从目标节点获取的字节数(in)大小:
当前节点使用/ipfs/id/1.0.0协议所发送(out)和接收(in)的数据包字节大小:
Netwarps 由国内资深的云计算和分布式技术开发团队组成,该团队在金融、电力、通信及互联网行业有非常丰富的落地经验。Netwarps 目前在深圳、北京均设立了研发中心,团队规模30+,其中大部分为具备十年以上开发经验的技术人员,分别来自互联网、金融、云计算、区块链以及科研机构等专业领域。Netwarps 专注于安全存储技术产品的研发与应用,主要产品有去中心化文件系统(DFS)、去中心化计算平台(DCP),致力于提供基于去中心化网络技术实现的分布式存储和分布式计算平台,具有高可用、低功耗和低网络的技术特点,适用于物联网、工业互联网等场景。