当它们出现在主循环中的单个线程中时,该库会为我提供事件
“单线程”仅适用于小型机器人。一旦达到2500 行会限制,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
接口为代表,用于向外发送事件。
您几乎可以从任何地方发送事件。从任何线程。甚至可能来自您的计时器。
Discord
是Sync
。将其包装在 中Arc
并与您的计时器和线程共享。
选项 2. 抓住机会。
即使recv_event
没有超时,Discord 也会不断地向您发送新事件。用户正在登录、退出、打字、发布消息、启动视频游戏、编辑内容等等。实际上,如果事件流停止,那么您的 Discord 连接就会出现问题(对于我的机器人,我已经根据该信号实现了高可用性故障转移)。
您可以与您的线程和计时器共享一个双端队列。一旦计时器完成,它将向双端队列发布一些东西,然后一旦 Discord 用新事件唤醒它,偶数循环将检查双端队列是否有新的事情要做。
选项 3. 鸟瞰图。
正如belst指出的那样,您可以启动一个通用事件循环,一个“统治所有事件”的循环,然后将 Discord 事件提升到该循环中。这特别有趣,因为使用分片,您将拥有多个事件循环。
因此,Discord 事件循环 -> 简单事件过滤器 -> 通道 -> 主事件循环。
选项 4. 分片。
如果您希望您的机器人在代码升级和重新启动期间保持在线,那么您应该提供一种方法来分别重新启动每个分片(或者像我一样在分片级别实现高可用性故障转移)。因为您无法在进程重新启动后立即连接所有分片,所以 Discord 不会让您这样做。
如果您的所有分片共享同一个进程,则在该进程重新启动后,您必须等待五秒钟才能附加新分片。使用 10 个分片,机器人停机时间几乎是一分钟。
分离分片重启的一种方法是为每个分片分配一个进程。然后,当您需要升级机器人时,您将分别重新启动每个进程。这样你仍然需要等待每个分片 5 到 6 秒,但你的用户不需要。
更好的是,您现在只需要为 discord-rs 升级和类似的维护相关任务重新启动 Discord 事件循环进程。另一方面,您的主事件循环可以立即重新启动,并且可以根据需要随时重新启动。这应该会大大加快编译-运行-测试循环。
因此,Discord 事件循环,在一个单独的分片进程中 -> 简单事件过滤器 -> RPC 或数据库 -> 主事件循环,在一个单独的进程中。