libp2p-rs infoserver implementation

Module address: https://github.com/netwarps/libp2p-rs/tree/master/infoserver

At the end of the previous article , it was mentioned that the web server will be used to provide related restful apis, and the situation of receiving and sending packets on the network can be observed externally. The design has been completed so far, here is a brief share of the design process.

Realize the idea

When designing the metric, in order to reduce the number of communications with the swarm, we put a metric clone in the control. For the api server, we can use the metric-related operation methods provided by control to obtain the network traffic data we want, as well as some related information about the current connection.

Framework introduction

As a web application framework of rust, Tide implements a series of related routing functions, which can easily build API; at the same time, serde's serialization/deserialization function can help us format data into json type, which is easier to read And parse.

Route registration

Take the get method as an example. In Tide, route registration is implemented in the following way:

server.at(path).get(method)

The at method and get method are as follows:

    // 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
    }

As you can see, the method parameter is actually an impl Trait.
In this type that implements this trait, there is such a form:

#[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())
    }
}

Corresponding to our code, the generic State is Control, and Fn can be implemented as an async method, the incoming parameter is Request, and the return value type is tide::Result.

Method analysis

To get the code of NetworkInfo for analysis:

  1. Remove Control from the request. Since the variable reference is required in the next step, clone is required here.
  2. Call retrieve_info() of control to get NetworkInfo data.
  3. Since ConnectionInfo contains PeerId, and the Multihash underlying PeerId does not yet support serde, a new struct of NetworkConnectionInfo is created here, and PeerId is set to String type to realize the formatting operation of serde.
  4. Iterate the connect_info of network_info, and combine the obtained vector with other data to generate NetworkConnectionStatus.
  5. Call Body::from_json() to format the data into json and return it as 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)
    }

    Interface list

    The currently implemented interfaces are as follows:

无参数接口
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=_

Among them, the peer interface with parameters means that a specific PeerID needs to be passed. <br>
The ProtocolID is passed using param.

Unsolved problems

When designing route registration, there is one way to try: generate a HashMap constant, with the key as path and value as method, to manage all routes in a unified manner. When the new() method is executed, the hashmap is iterated and the routing information is registered in the server.

The difficulty with this method is that our method is actually a closure whose return value type is future. Assuming that the value is in the form of a closure, the compiler will prompt the following error:

`impl Trait` not allowed outside of function and inherent method return types

This means that impl Trait cannot be used as a return value type other than a function.

If the value is dynamically assigned as the type, it means that we need Box<dyn Endpoint<State>> as the value type. For HashMap, unless it is consumed directly, the data retrieved from it is of reference type, and the clone method does not seem to work here, and the return is still a Box reference. The routing registration method currently used is not very friendly in code reading, so we will consider other methods for optimization in the future.

Partial effect display

The number of bytes sent by the current node to a target node (out) and the number of bytes (in) obtained from the target node:
libp2p-rs infoserver implementation

The byte size of the data packet sent (out) and received (in) by the current node using the /ipfs/id/1.0.0 protocol:
libp2p-rs infoserver implementation


Netwarps is composed of a senior cloud computing and distributed technology development team in China. The team has very rich experience in the financial, power, communications and Internet industries. Netwarps currently has R&D centers in Shenzhen and Beijing, with a team size of 30+, most of which are technicians with more than ten years of development experience, from professional fields such as the Internet, finance, cloud computing, blockchain, and scientific research institutions.
Netwarps focuses on the development and application of secure storage technology products. The main products include decentralized file system (DFS) and decentralized computing platform (DCP), and are committed to providing distributed storage and distributed based on decentralized network technology. The computing platform has the technical characteristics of high availability, low power consumption and low network, and is suitable for scenarios such as the Internet of Things and Industrial Internet.
Official account: Netwarps

Guess you like

Origin blog.51cto.com/14915984/2583156