【问题标题】:How can I combine discord-rs events with other events from Twitter or timers?如何将 discord-rs 事件与来自 Twitter 或计时器的其他事件结合起来?
【发布时间】:2017-04-26 11:39:40
【问题描述】:

我使用 discord-rs 库为 Discord 聊天服务编写了一个机器人。当它们出现在主循环中的单个线程中时,该库会为我提供事件:

fn start() {
    // ...
    loop {
        let event = match connection.recv_event() {
            Ok(event) => event,
            Err(err) => { ... },
        }
    }
}

我想添加一些计时器和其他在它们自己的线程中计算的东西,它们必须通知我在主循环的线程中做某事。我还想添加 Twitter 支持。所以它可能看起来像这样:

(Discord's network connection, Twitter network connection, some timer in another thread) -> main loop

这看起来像这样:

fn start() {
    // ...
    loop {
        let event = match recv_events() {
            // 1. if Discord - do something with discord
            // 2. if timer - handle timer's notification
            // 3. if Twitter network connection - handle twitter
        }
    }
}

在原始 C 和 C 套接字中,可以通过 (e) 轮询它们来完成,但在这里我不知道如何在 Rust 中做到这一点,或者是否可能。我想我想要像poll 这样的几个不同来源的东西,它们可以为我提供不同类型的对象。

我想如果我为 mio 的 Evented 特征提供一个包装器并使用 Deadline 示例中描述的 mio 的民意调查,这可以实现。

有没有办法把这些事件结合起来?

【问题讨论】:

    标签: multithreading rust mio


    【解决方案1】:

    当它们出现在主循环的单个线程中时,这个库会为我提供事件

    “单线程”仅适用于小型机器人。一旦您到达2500 guilds limit,Discord 将拒绝以正常方式连接您的机器人。你必须使用分片。而且我你不会为你的机器人分片配置新的虚拟服务器。机会是,您将生成新的线程,每个分片一个事件循环。

    这是我的做法,顺便说一句:

    fn event_loop(shard_id: u8, total_shards: u8){
        loop {
            let bot = Discord::from_bot_token("...").expect("!from_bot_token");
            let (mut dc, ready_ev) = bot.connect_sharded(shard_id, total_shards).expect("!connect");
            // ...
        }
    }
    
    fn main() {
        let total_shards = 10;
        for shard_id in 0..total_shards {
            sleep(Duration::from_secs(6)); // There must be a five-second pause between connections from one IP.
            ThreadBuilder::new().name (fomat! ("shard " (shard_id)))
              .spawn (move || {
                loop {
                  if let Err (err) = catch_unwind (move || event_loop (shard_id, total_shards)) {
                    log! ("shard " (shard_id) " panic: " (gstuff::any_to_str (&*err) .unwrap_or ("")));
                    sleep (Duration::from_secs (10));
                    continue}  // Panic restarts the shard.
                  break}
              }) .expect ("!spawn");
        }
    }
    

    我想添加一些计时器和其他在它们自己的线程中计算的东西,它们必须通知我在主循环的线程中做某事

    选项 1。不要。

    机会是,您真的不需要返回到 Discord 事件循环!假设您要发布回复、更新嵌入等。您不需要 Discord 事件循环来执行此操作!

    Discord API 分为两部分:
    1) Websocket API,由Connection 表示,用于获取 Discord 中的事件。
    2) REST API,由Discord 接口表示,用于发送事件。

    您几乎可以从任何地方发送事件。从任何线程。甚至可能来自您的计时器。

    DiscordSync。将其包装在 Arc 中并与您的计时器和线程共享。

    选项 2. 抓住机会。

    即使recv_event 没有超时,Discord 也会不断向您发送新事件。用户正在登录、退出、打字、发布消息、启动视频游戏、编辑内容等等。实际上,如果事件流停止,那么您的 Discord 连接就会出现问题(对于我的机器人,我已经根据该信号实现了高可用性故障转移)。

    您可以与您的线程和计时器共享deque。一旦计时器完成,它将向双端队列发布一些内容,然后一旦 Discord 用新事件唤醒它,偶数循环将检查双端队列是否有新的事情要做。

    选项 3. 鸟瞰图。

    正如belst 所指出的,您可以启动一个通用事件循环,一个“统治所有事件”的循环,然后将 Discord 事件提升到该循环中。这特别有趣,因为通过分片,您将拥有多个事件循环。

    所以,Discord 事件循环 -> 简单事件过滤器 -> 通道 -> 主事件循环。

    选项 4。分片。

    如果您希望您的机器人在代码升级和重新启动期间保持在线,那么您应该提供一种方法来分别重新启动每个分片(或者像我一样在分片级别实施高可用性故障转移)。因为您无法在进程重新启动后立即连接所有分片,所以 Discord 不会让您这样做。

    如果您的所有分片共享同一个进程,则在该进程重新启动后,您必须等待 5 秒钟才能附加新分片。使用 10 个分片,机器人停机时间几乎是一分钟。

    分离分片重启的一种方法是为每个分片分配一个进程。然后,当您需要升级机器人时,您将分别重新启动每个进程。这样仍然需要等待每个分片五到六秒,但你的用户的不需要。

    更好的是,您现在只需为 discord-rs 升级和类似的维护相关任务重新启动 Discord 事件循环进程。另一方面,您的主事件循环可以立即重新启动,并且可以根据需要随时重新启动。这应该会大大加快编译-运行-测试循环。

    所以,Discord 事件循环,在一个单独的分片进程中 -> 简单事件过滤器 -> RPC 或数据库 -> 主事件循环,在一个单独的进程中。

    【讨论】:

      【解决方案2】:

      在您的情况下,我将为您需要的每个服务启动一个线程,然后使用 mpsc 通道将事件发送到主循环。

      例子:

      use std::thread;
      use std::sync::mpsc::channel;
      
      enum Event {
          Discord(()),
          Twitter(()),
          Timer(()),
      }
      
      fn main() {
          let (tx, rx) = channel();
          // discord
          let txprime = tx.clone();
          thread::spawn(move || {
              loop {
                  // discord loop
                  txprime.send(Event::Discord(())).unwrap()
              }
          });
      
          // twitter
          let txprime = tx.clone();
          thread::spawn(move || {
              loop {
                  // twitter loop
                  txprime.send(Event::Twitter(())).unwrap()
              }
          });
      
          // timer
          let txprime = tx.clone();
          thread::spawn(move || {
              loop {
                  // timer loop
                  txprime.send(Event::Timer(())).unwrap()
              }
          });
      
          // Main loop
          loop {
              match rx.recv().unwrap() {
                  Event::Discord(d) => unimplemented!(),
                  Event::Twitter(t) => unimplemented!(),
                  Event::Timer(t)   => unimplemented!(),
              }
          }
      }
      

      【讨论】:

        猜你喜欢
        • 2020-09-09
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2014-01-21
        • 1970-01-01
        • 2020-03-09
        • 1970-01-01
        相关资源
        最近更新 更多