Fragmentation d'Application
Quand fragmenter
Avant de plonger dans cette section, veuillez noter que la fragmentation peut ne pas être nécessaire pour vous. La fragmentation n'est requise qu'à 2 500 guildes — à ce moment-là, Discord ne permettra pas à votre bot de se connecter sans fragmentation. Cela étant dit, vous devriez considérer cela quand votre bot compte environ 2 000 guildes, ce qui devrait être suffisant pour que cela fonctionne. Contrairement à la croyance populaire, la fragmentation en elle-même est très simple. Cependant, cela peut être compliqué en fonction des besoins de votre bot.
Si votre bot est dans un total de 2 000 serveurs ou plus, veuillez continuer avec ce guide. Sinon, il serait peut-être bon d'attendre à ce moment.
La fragmentation n'est pertinente que si votre application utilise des événements de passerelle. Pour les rappels de webhook, c'est complètement hors de propos !
Comment fonctionne la fragmentation ?
Au fur et à mesure qu'une application se développe, un développeur peut juger nécessaire de diviser son processus pour s'exécuter en parallèle afin de maximiser l'efficacité. À une échelle beaucoup plus grande, le développeur pourrait remarquer que son processus ralentit, parmi d'autres problèmes. Consultez la documentation officielle Discord sur le sujet.
Ce guide explique uniquement les bases de la fragmentation en utilisant le ShardingManager intégré, qui peut exécuter des fragments comme des processus séparés ou des threads sur une seule machine.
Si vous avez besoin d'évoluer au-delà (par exemple, en exécutant des fragments sur plusieurs machines/conteneurs), vous pouvez toujours le faire avec discord.js en transmettant les options appropriées au constructeur Client. Néanmoins, vous serez seul responsable de la gestion des fragments et du partage des informations entre eux.
Apart du ShardingManager, discord.js supporte également un mode de fragmentation connu sous le nom de fragmentation interne. La fragmentation interne crée plusieurs connexions websocket à partir du même processus et ne nécessite pas de modifications majeures du code. Pour l'activer, passez simplement shards: 'auto' en tant que ClientOptions au constructeur Client.
Cependant, la fragmentation interne n'est pas idéale pour les gros bots en raison de la haute utilisation de la mémoire du processus principal unique et ne sera pas abordée davantage dans ce guide.
Fichier de fragmentation
D'abord, vous aurez besoin d'un fichier que vous lançerez désormais, plutôt que votre fichier index.js d'origine. Il est fortement recommandé de le renommer en bot.js et de nommer ce nouveau fichier en index.js à la place. Copiez-collez l'extrait suivant dans votre nouveau fichier index.js.
const { ShardingManager } = require('discord.js');
const manager = new ShardingManager('./bot.js', { token: 'your-token-goes-here' });
manager.on('shardCreate', (shard) => console.log(`Launched shard ${shard.id}`));
manager.spawn();Le code ci-dessus utilise le gestionnaire de fragmentation discord.js pour générer le nombre recommandé de fragments pour votre bot. Le nombre recommandé devrait être d'environ 1 000 guildes par fragment. Notez que vous devez attacher l'écouteur d'événement à shardCreate avant d'appeler .spawn() pour éviter une condition de course qui pourrait empêcher le fragment 0 de consigner le lancement réussi. Même si vous fournissez le token ici, vous devrez toujours le transmettre au fichier bot principal dans client.login(), alors n'oubliez pas de le faire.
Vous pouvez trouver les méthodes disponibles pour la classe ShardingManager ShardingManager. Bien que vous ne fassiez pas
beaucoup usage de cette section, contrairement à la prochaine fonctionnalité que nous explorerons, que vous pouvez apprendre en cliquant ce
lien.
Démarrage
Vous devrez très probablement modifier un code pour que votre bot fragimté fonctionne. Si votre bot est très basique, vous avez de la chance ! Nous supposons que vous avez probablement une forme de commande stats, par laquelle vous pouvez visualiser rapidement les statistiques de votre bot, comme son nombre de serveurs. Nous l'utiliserons comme exemple qui doit s'adapter pour fonctionner avec des fragments.
Dans ce code, vous avez probablement l'extrait client.guilds.cache.size, qui compte le nombre de guildes en cache attachées à ce client. Étant donné que la fragmentation lancera plusieurs processus, chaque processus (chaque fragment) aura maintenant sa collection de sous-ensemble de guildes dont il est responsable. Cela signifie que votre code d'origine ne fonctionnera pas comme vous vous y attendiez.
Here is some sample code for a stats command, without sharding taken into consideration:
const { Client, Events, GatewayIntentBits } = require('discord.js');
const client = new Client({ intents: [GatewayIntentBits.Guilds] });
client.on(Events.InteractionCreate, (interaction) => {
if (!interaction.isChatInputCommand()) return;
const { commandName } = interaction;
if (commandName === 'stats') {
return interaction.reply(`Server count: ${client.guilds.cache.size}.`);
}
});
client.login('your-token-goes-here');Let's say your bot is in a total of 3,600 guilds. Using the recommended shard count, you might end up at four shards, each containing approximately 900 guilds. If a guild is on a specific shard (shard #2, for example) and receives this command, the guild count will be close to 900, which is not the "correct" number of guilds for your bot. Let's take a look at how to fix that.
FetchClientValues
One of the most common sharding utility methods you'll be using is ShardClientUtil#fetchClientValues. This method retrieves a property on the Client object of all shards.
Take the following snippet of code:
client.shard.fetchClientValues('guilds.cache.size').then(console.log); If you run it, you will notice an output like [898, 901, 900, 901]. You will be correct in assuming that that's the total number of guilds per shard stored in an array in the Promise. This probably isn't the ideal output for guild count, so let's use Array.reduce() to provide a better output.
It's highly recommended for you to visit the
documentation to understand
how the reduce() method works, as you will probably find great use of it in sharding.
In this case, this method iterates through the array and adds each current value to the total amount:
client.shard
.fetchClientValues('guilds.cache.size')
.then((results) => {
console.log(`${results.reduce((acc, guildCount) => acc + guildCount, 0)} total guilds`);
})
.catch(console.error);While it's a bit unattractive to have more nesting in your commands, it is necessary when not using async/await. Now, the code at the top should look something like the below:
client.on(Events.InteractionCreate, (interaction) => {
if (!interaction.isChatInputCommand()) return;
const { commandName } = interaction;
if (commandName === 'stats') {
return interaction.reply(`Server count: ${client.guilds.cache.size}.`);
return client.shard
.fetchClientValues('guilds.cache.size')
.then((results) => {
return interaction.reply(`Server count: ${results.reduce((acc, guildCount) => acc + guildCount, 0)}`);
})
.catch(console.error);
}
});
// ...BroadcastEval
Next, check out another handy sharding method known as ShardClientUtil#broadcastEval. This method makes all of the shards evaluate a given method, which receives a client and a context argument. The client argument refers to the Client object of the shard evaluating it. You can read about the context argument here.
client.shard
.broadcastEval((c) =>
c.guilds.cache
.reduce((acc, guild) => acc + guild.memberCount, 0),
)
.then(console.log);This will run the code given to broadcastEval on each shard and return the results to the Promise as an array, once again. You should see something like [9001, 16658, 13337, 15687] logged. The code sent to each shard adds up the memberCount property of every guild that shard is handling and returns it, so each shard's total guild member count. Of course, if you want to total up the member count of every shard, you can do the same thing again on the Promise results.
client.shard
.broadcastEval((c) => c.guilds.cache.reduce((acc, guild) => acc + guild.memberCount, 0))
.then((results) => {
return interaction.reply(`Total member count: ${results.reduce((acc, memberCount) => acc + memberCount, 0)}`);
})
.catch(console.error);Putting them together
You'd likely want to output both pieces of information in the stats command. You can combine these two results with Promise.all():
const promises = [
client.shard.fetchClientValues('guilds.cache.size'),
client.shard.broadcastEval((c) => c.guilds.cache.reduce((acc, guild) => acc + guild.memberCount, 0)),
];
Promise.all(promises)
.then((results) => {
const totalGuilds = results[0].reduce((acc, guildCount) => acc + guildCount, 0);
const totalMembers = results[1].reduce((acc, memberCount) => acc + memberCount, 0);
return interaction.reply(`Server count: ${totalGuilds}\nMember count: ${totalMembers}`);
})
.catch(console.error);Promise.all() runs every Promise you pass inside an array in parallel and waits for each to finish before returning their results simultaneously. The result is an array that corresponds with the array of Promises you pass–so the first result element will be from the first Promise. With that, your stats command should look something like this:
client.on(Events.InteractionCreate, (interaction) => {
// ...
if (commandName === 'stats') {
return client.shard
.fetchClientValues('guilds.cache.size')
.then((results) => {
return interaction.reply(`Server count: ${results.reduce((acc, guildCount) => acc + guildCount, 0)}`);
})
.catch(console.error);
const promises = [
client.shard.fetchClientValues('guilds.cache.size'),
client.shard.broadcastEval((c) => c.guilds.cache.reduce((acc, guild) => acc + guild.memberCount, 0)),
];
return Promise.all(promises)
.then((results) => {
const totalGuilds = results[0].reduce((acc, guildCount) => acc + guildCount, 0);
const totalMembers = results[1].reduce((acc, memberCount) => acc + memberCount, 0);
return interaction.reply(`Server count: ${totalGuilds}\nMember count: ${totalMembers}`);
})
.catch(console.error);
}
});The next section contains additional changes you might want to consider.
OAuth2
OAuth2 permet aux développeurs d'applications de créer des applications qui utilisent l'authentification et les données de l'API Discord. Les développeurs...
Informations Supplémentaires
Ici, nous aborderons certains sujets supplémentaires sur la fragmentation qui auraient pu soulever des préoccupations.