1

我正在寻找使用 mDNS、floodsub 和 kademlia DHT 编写网络行为。到目前为止,我已经让这些服务中的每一个都可以工作,但是还不能将这些服务的响应用于任何有意义的事情。

理想情况下,我能够将来自行为过程事件的数据(例如为 Kad DHT 实现的事件)传输到主 Swarmpoll循环中。例如,在我的例子中,我引用了一个结构,该结构表示通过sled数据库持久化到磁盘的图形。该结构的所有权存在于轮询 swarm 的方法中。收到a 后,我将如何更新此图表(例如添加条目)KademliaEvent

我尝试过的解决方案:

  • 将我想要更新的数据结构的所有权转移到ClientBehavior结构中,这样我就可以self.my_data_structure.add(result);KademliaEvent inject_event方法 中获取
    • #[derive(NetworkBehaviour)]根本不喜欢这个
    • Graph只是一个结构,不会发出任何事件/实现NetworkBehaviour
  • 创建一个Context结构体,derive该结构体可用于在轮询方法和相应方法 NetworkBehaviour之间来回传递响应inject_event
    • #[derive(NetworkBehaviour)]不适合Arcs / Mutexes

这是我的 NetworkBehavior 的样子:

/// A network behavior describing a client connected to a pub-sub compatible,
/// optionally mDNS-compatible network. Such a "behavior" may be implemented for
/// any libp2p transport, but any transport used with this behavior must implement
/// asynchronous reading & writing capabilities.
#[derive(NetworkBehaviour)]
pub struct ClientBehavior<TSubstream: AsyncRead + AsyncWrite + Send + Unpin + 'static> {
    /// Some pubsub mechanism bound to the above transport
    pub floodsub: Floodsub<TSubstream>,

    /// Some mDNS service bound to the above transport
    pub mdns: Mdns<TSubstream>,

    /// Allow for the client to do some external discovery on the global network through a KAD DHT
    pub kad_dht: Kademlia<TSubstream, MemoryStore>,
}

以及我的 KademliaNetworkBehaviourEventProceess实现是什么样的:

impl<TSubstream: AsyncRead + AsyncWrite + Send + Unpin + 'static>
    NetworkBehaviourEventProcess<KademliaEvent> for ClientBehavior<TSubstream>
{
    fn inject_event(&mut self, event: KademliaEvent) {
        // Some behavior logic, nothing special, really, just a bunch of matches
        // NOTE: this is when I'd want to update the `Graph`, since KademliaEvent will contain data that I need to put in the `Graph` instance
    }
}

以及我如何产生我Swarm的和我的ClientBehavior

// Initialize a new behavior for a client that we will generate in the not-so-distant future with the given peerId, alongside
// an mDNS service handler as well as a floodsub instance targeted at the given peer
let mut behavior = ClientBehavior {
    floodsub: Floodsub::new(self.peer_id.clone()),
    mdns: Mdns::new().await?,
    kad_dht: Kademlia::new(self.peer_id.clone(), store),
};

// Iterate through bootstrap addresses
for bootstrap_peer in bootstrap_addresses {
    // NOTE: add_address is a method that isn't provided by #[derive(NetworkBehaviour)].
    // It's just a helper method I wrote that adds the peer to the Floodsub & DHT views.
    behavior.add_address(&bootstrap_peer.0, bootstrap_peer.1); // Add the bootstrap peer to the DHT
}

// Bootstrap the behavior's DHT
behavior.kad_dht.bootstrap();

// Note: here, `self` is a reference to a configuration struct holding a peer ID, and a keypair
let mut swarm = Swarm::new(
    libp2p::build_tcp_ws_secio_mplex_yamux(self.keypair.clone())?,
    behavior,
    self.peer_id.clone(),
); // Initialize a swarm

以及我如何轮询群体:

// NOTE: Here, `self` has ownership of the aforementioned `Graph` instance. I'd like to update this
// instance from this block, or from the ClientBehavior itself--as long as I'm able to update it once a `KademliaEvent` is received.

// Try to get the address we'll listen on
if let Ok(addr) = format!("/ip4/0.0.0.0/tcp/{}", port).parse::<Multiaddr>() {
    // Try to tell the swarm to listen on this address, return an error if this doesn't work
    if let Err(e) = Swarm::listen_on(&mut swarm, addr.clone()) {
        // Convert the addr err into an io error
        let e: std::io::Error = io::ErrorKind::AddrNotAvailable.into();

        // Return an error
        return Err(e.into());
    };

    // Fetch the hash of the first transaction in the DAG from the network
    swarm
        .kad_dht
        .get_record(&Key::new(&sync::ROOT_TRANSACTION_KEY), Quorum::Majority);

    loop {
        // Poll the swarm
        match swarm.next_event().await {
            // NOTE: Initially, I was under the impression that I would be able to intercept
            // events from the ClientBehavior here. Yet, the below info! call is never reached.
            // This remains the case in libp2p example code that I have experimented with.
            SwarmEvent::Behaviour(e) => info!("idk: {:?}", e),

            _ => debug!("Some other event; this is handled specifically in the actual codebase, but for this question, all I really care about is the above behavior event."),
        };
    }
}

我只需要实现NetworkBehaviour自己吗?

4

1 回答 1

0

你的问题有点晚了,但也许这会有所帮助:

将我要更新的数据结构的所有权转移到 ClientBehavior 结构,这样我就可以 self.my_data_structure.add(result); 来自 KademliaEvent inject_event 方法

  • #[derive(NetworkBehaviour)] 根本不喜欢这个
  • Graph 只是一个结构,不会发出任何事件/实现 NetworkBehaviour

文档对此有以下说法:

#[behaviour(ignore)] 可以添加到结构字段上,以禁用对未实现 NetworkBehaviour 的字段的委托生成。

因为它需要所有字段来实现NetworkBehaviour特征。这应该是一个可行的解决方案。

不管怎样,如果你试图修改一个超出你的Mdns范围的结构,我相信¹所有这一切的最惯用的解决方案是通过poll函数(返回 a Poll::Ready(GenerateEvent(TOut)))发出一个自定义事件,然后你可以听通过不同的NetworkBehaviourEventProcess impl 用于相应的结构,然后应该修改该结构。类似于libp2p-ping 实现如何使用 VecDeque 事件队列,然后在您的ClientBehaviourpoll()中有一个类似的队列(标记为behaviour(ignore)]),附加到它,发出事件并在调用代码/等待中捕获该特定事件以某种方式循环?poll()

编辑:您还应该能够扩展 poll 方法以允许以这种方式返回其他事件。

¹:我对 rust-libp2p 的能力还远远不够,而且到目前为止还没有足够的经验来提供确凿的事实,你的问题实际上对我来说是一个教程

于 2020-08-01T23:48:47.893 回答