Как я могу создать SwarmEvent::Behavior из производного NetworkBehaviour?
Я хочу написать поведение сети, которое использует mDNS, floodsub и kademlia DHT. До сих пор я заставил каждую из этих служб работать, но не смог использовать ответы этих служб для чего-то значимого.
В идеале я мог бы передавать данные, поступающие из события процесса поведения, например, того, которое будет реализовано для Kad DHT, в основной Swarm. poll
петля. Например, в моем случае у меня есть ссылка на структуру, представляющую граф, который сохраняется на диске черезsled
база данных. Право собственности на эту структуру существует в методе, опрашивающем рой. Как мне обновить этот график (например, добавить запись), когдаKademliaEvent
получено?
Решения, которые я пробовал:
- Передача права собственности на структуру данных, которую я хочу обновить, на
ClientBehavior
структура так что я могу простоself.my_data_structure.add(result);
изKademliaEvent
inject_event
метод#[derive(NetworkBehaviour)]
это совсем НЕ нравитсяGraph
это просто структура и не генерирует никаких событий / реализуетNetworkBehaviour
- Создание
Context
структура, чтоderive
sNetworkBehaviour
и может использоваться для передачи ответа между методом опроса и соответствующимinject_event
метод#[derive(NetworkBehaviour)]
не место хорошо сArc
с /Mutex
es
Вот как выглядит мое 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>,
}
а какая моя кадемля NetworkBehaviourEventProceess
реализация выглядит так:
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
себя?
1 ответ
Немного поздно ответить на ваш вопрос, но, возможно, это поможет:
Передача права собственности на структуру данных. Я хочу обновить структуру ClientBehavior, чтобы я мог просто self.my_data_structure.add(result); из метода KademliaEvent inject_event
- #[derive(NetworkBehaviour)] это вообще НЕ нравится
- График - это просто структура и не генерирует никаких событий / реализует NetworkBehaviour
В документации по этому поводу говорится следующее:
#[поведение (игнорировать)] можно добавить в поле структуры, чтобы отключить создание делегирования для полей, которые не реализуют NetworkBehaviour.
Поскольку в противном случае для реализации свойства NetworkBehaviour требуются все поля. Это должно сделать это жизнеспособным решением.
В любом случае, если вы пытаетесь изменить структуру, которая выходит за рамки вашего Mdns, я считаю, что наиболее идиоматическим решением для всего этого было бы генерирование настраиваемого события через функцию опроса (вернутьPoll::Ready(GenerateEvent(TOut))
), Которые затем можно прослушивать через другую NetworkBehaviourEventProcess осущий для соответствующей структуры, которая должна затем модифицировать структуры. Подобно тому, как реализация libp2p-ping использует очередь событий VecDeque, которая затем распространяется во времяpoll()
у вас может быть похожая очередь (отмечена behaviour(ignore)]
) в вашем ClientBehaviour, добавьте к нему, испустите событие вpoll()
и как-то поймать это конкретное событие в вашем вызывающем коде / цикле ожидания?
Изменить: вы также должны иметь возможность расширить метод опроса, чтобы таким образом можно было возвращать дополнительные события.
¹: Я далеко не достаточно разбираюсь в rust-libp2p и далеко не достаточно опытен, чтобы приводить неопровержимые факты, на самом деле ваш вопрос был для меня своего рода руководством