libp2p-rs infoserver 实现
模块地址: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<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。
方法分析
以获取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<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。<br>
而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<dyn Endpoint<State>>作为value类型。对于HashMap而言,除非直接消耗掉,不然从中取出的数据都是引用类型的,而clone方法在此处似乎也是行不通的,返回的仍然是一个Box的引用。目前所采用的路由注册方式,在代码的阅读上不太友好,后续考虑用其他方式进行优化。
部分效果展示
当前节点向某个目标节点发送字节数(out)和从目标节点获取的字节数(in)大小:
当前节点使用/ipfs/id/1.0.0协议所发送(out)和接收(in)的数据包字节大小:
Netwarps 由国内资深的云计算和分布式技术开发团队组成,该团队在金融、电力、通信及互联网行业有非常丰富的落地经验。Netwarps 目前在深圳、北京均设立了研发中心,团队规模30+,其中大部分为具备十年以上开发经验的技术人员,分别来自互联网、金融、云计算、区块链以及科研机构等专业领域。
Netwarps 专注于安全存储技术产品的研发与应用,主要产品有去中心化文件系统(DFS)、去中心化计算平台(DCP),致力于提供基于去中心化网络技术实现的分布式存储和分布式计算平台,具有高可用、低功耗和低网络的技术特点,适用于物联网、工业互联网等场景。
公众号:Netwarps
- RTMFP---基于CumulusServer的P2P功能实现
- 在InfoPath 2010中利用ServerInfo类实现相对URL
- libp2p-rs swarm 拨号设计与实现
- 一个轻量级Actor并发框架的c++实现, libgsc(Game Server Communication Library)(一)
- P2P之UDP穿透NAT的原理与实现(补充)
- Android BLE 蓝牙低功耗教程,中央BluetoothGatt和周边BluetoothGattServer的实现
- CoAP学习笔记——STM32平台上实现CoAP Server
- MFC 配合 protobuff libevent 实现的Socket 的GM工具 框架
- 人工智能几行代码实现换脸,python+dlib实现图文教程 4000
- git clone 提示 did you run git update-server-info on the server?
- 在实现ReportServer的时候,如何判断当前传入的参数值为null
- libevent 入门教程:Echo Server based on libevent(转)
- Windows平台上实现P2P服务(四)
- ArcGIS.Server.9.3和ArcGIS API for JavaScript实现距离量算和面积量算(九)
- 【MS SQL Server】SQL删除数据(分段实现大量数据的删除操作)
- P2P 之 UDP穿透NAT的原理与实现
- python matplotlib实现双Y轴的实例
- [VC++]点对点(P2P)多线程断点续传的实现
- p2p的基本实现过程
- 【转载】 TCP实现P2P通信、TCP穿越NAT的方法、TCP打洞