aslain.dev
0%
01 Hizmetler 02 Hakkımda 03 Projeler 04 Stack 05 Blog 06 İletişim
← Tüm makaleler Discord Botları

Discord Sharding: Büyük Botları Ölçeklendirme

Botun büyüdükçe bir gün Discord'dan şu uyarıyı alırsın: bot çok fazla sunucuya katıldı ve tek bir bağlantı (shard) artık yetmiyor. İşte tam burada Discord sharding devreye girer. Discord, bir botun gateway bağlantısı başına en fazla 2500 sunucuyu desteklemesine izin verir; bu sınırı geçtiğinde botu birden fazla shard'a bölmek zorunlu hale gelir. Bu rehberde sharding'in ne olduğunu, discord.js'in ShardingManager sınıfıyla botu nasıl ölçeklendireceğini ve shard'lar arasında veri toplamanın inceliklerini adım adım anlatıyorum.

Sharding nedir ve ne zaman gerekir?

Bir shard, botunun Discord gateway'ine açtığı tek bir WebSocket bağlantısıdır. Her shard, sunucuların bir alt kümesinden sorumludur; gelen olayları (mesaj, üyelik, ses durumu) yalnızca kendi sunucuları için alır. Discord sunucuları shard'lara şu formülle dağıtır:

shard_id = (guild_id >> 22) % toplam_shard_sayisi

Yani hangi sunucunun hangi shard'a düşeceği, sunucunun ID'sine göre belirlenir; tesadüfi değildir. Sharding'e ne zaman ihtiyacın olduğu nettir:

  • Zorunlu sınır: Bot 2500 sunucuyu geçtiğinde Discord tek shard ile bağlanmana izin vermez.
  • Performans: 2500'ün altında bile, tek bir Node.js süreci binlerce sunucunun olay trafiğini ve önbelleğini taşımakta zorlanabilir; sharding yükü dağıtır.
  • Dayanıklılık: Bir shard çökse bile diğerleri çalışmaya devam eder.

Önemli bir nokta: 2500'ün çok altındaki küçük botlar için sharding gereksiz bir karmaşıklıktır. Erken optimize etme; ihtiyaç doğmadan eklersen sadece geliştirmeyi zorlaştırırsın.

ShardingManager ile başlamak

discord.js, sharding'i senin yerine yöneten hazır bir ShardingManager sınıfı sunar. Mantık şudur: botun ana dosyasını (örneğin bot.js) doğrudan çalıştırmak yerine, bir yönetici betiği (manager.js) yazarsın. Bu yönetici, botunu birden fazla ayrı süreç olarak başlatır; her süreç bir shard'dır.

Önce yönetici dosyası:

// manager.js
const { ShardingManager } = require('discord.js');
require('dotenv').config();

const manager = new ShardingManager('./bot.js', {
  token: process.env.DISCORD_TOKEN,
  totalShards: 'auto',
});

manager.on('shardCreate', shard => {
  console.log(`Shard ${shard.id} başlatıldı`);
});

manager.spawn();

totalShards: 'auto' ayarı, discord.js'in Discord'dan önerilen shard sayısını sorup uygun adette süreç açmasını sağlar. Bunu elle de verebilirsin (totalShards: 4), ama 'auto' çoğu durumda en güvenli seçimdir.

Asıl bot dosyan ise neredeyse hiç değişmez:

// bot.js
const { Client, GatewayIntentBits } = require('discord.js');

const client = new Client({
  intents: [GatewayIntentBits.Guilds],
});

client.once('ready', () => {
  console.log(`Giriş yapıldı: ${client.user.tag} | Shard: ${client.shard.ids}`);
});

client.login(process.env.DISCORD_TOKEN);

Dikkat et: bot.js içinde client.login'a hâlâ token veriyorsun ama hangi shard olduğunu kendin söylemiyorsun. ShardingManager, her süreci başlatırken shard kimliklerini ortam değişkenleri üzerinden geçirir ve discord.js bunları otomatik okur. Artık botu node manager.js ile başlatırsın, node bot.js ile değil.

Shard'lar arası veri toplama: broadcastEval

Sharding'in en kafa karıştıran yanı şudur: her shard ayrı bir süreç olduğundan, kendi client.guilds.cache önbelleği vardır ve diğer shard'lardaki sunucuları göremez. Botun toplam sunucu sayısını öğrenmek istediğinde tek bir shard'a bakmak yanıltıcı olur. Bunun için tüm shard'larda aynı kodu çalıştırıp sonuçları toplaman gerekir. discord.js bunu broadcastEval ile yapar:

// Bir komut handler'ı içinde
const sonuclar = await client.shard.broadcastEval(c => c.guilds.cache.size);
const toplamSunucu = sonuclar.reduce((toplam, deger) => toplam + deger, 0);

console.log(`Toplam sunucu: ${toplamSunucu}`);

broadcastEval, verdiğin fonksiyonu her shard'ın kendi sürecinde çalıştırır ve bir dizi sonuç döner; her eleman bir shard'a aittir. Ardından reduce ile bunları birleştirirsin. Toplam kullanıcı sayısı için de aynı desen geçerlidir:

const uyeSonuclari = await client.shard.broadcastEval(
  c => c.guilds.cache.reduce((acc, g) => acc + g.memberCount, 0)
);
const toplamUye = uyeSonuclari.reduce((a, b) => a + b, 0);

Burada kritik bir kural var: broadcastEval'e verdiğin fonksiyonun dışındaki değişkenlere doğrudan erişemezsin, çünkü fonksiyon başka bir süreçte çalışır. Dışarıdan değer geçirmek için context kullanırsın:

const guildId = '123456789012345678';

const isimler = await client.shard.broadcastEval(
  (c, { hedefId }) => {
    const guild = c.guilds.cache.get(hedefId);
    return guild ? guild.name : null;
  },
  { context: { hedefId: guildId } }
);

const bulunan = isimler.find(isim => isim !== null);

Belirli bir sunucu yalnızca tek bir shard'da bulunduğu için sonuçların çoğu null döner; find ile dolu olanı yakalarsın.

Bellek, süreç sayısı ve hybrid sharding

Her shard ayrı bir Node.js süreci olduğundan, RAM kullanımı shard sayısıyla doğru orantılı artar. 16 shard, kabaca 16 ayrı bot örneği demektir. Çok büyük botlarda (on binlerce sunucu) bu, sunucunun belleğini tüketebilir.

Çözüm hybrid sharding'dir: birden fazla shard'ı tek bir süreçte (cluster) gruplamak. discord.js'in dahili ShardingManager'ı bunu doğrudan sunmaz; bunun için topluluk paketi discord-hybrid-sharding kullanılır. Mantığı şudur: 32 shard'ı 4 cluster'a bölersen yalnızca 4 süreç açılır, her cluster 8 shard taşır. Böylece bellek ayak izi ciddi şekilde düşer. Küçük ve orta ölçekli botlar için buna gerek yoktur; standart ShardingManager fazlasıyla yeter.

Süreç sayısını planlarken birkaç pratik nokta:

  • Önbelleği kıs: Gereksiz intents ve cache açma; her shard kendi önbelleğini tuttuğu için israf çarpan etkisi yaratır.
  • Process mode vs worker: ShardingManager varsayılan olarak ayrı süreç açar; mode: 'worker' ile worker_threads da kullanılabilir, ama izolasyon süreç modunda daha güçlüdür.
  • Yeniden başlatma: respawn seçeneği açıkken çöken bir shard otomatik yeniden ayağa kalkar.

Sık Sorulan Sorular

Botum 2500 sunucuya ulaşmadan sharding yapmalı mıyım?

Hayır, gerek yok. Discord tek shard ile 2500 sunucuya kadar bağlanmana izin verir. Bu sınıra yaklaşana kadar sharding sadece gereksiz karmaşıklık ve bellek tüketimi getirir. Yaklaştığında ShardingManager'a geçmek birkaç dosyalık iştir; erkenden uğraşma.

Shard sayısını elle mi vermeliyim yoksa 'auto' mu?

Çoğu durumda totalShards: 'auto' en doğrusudur; discord.js, Discord'un önerdiği sayıyı sorar ve ona göre süreç açar. Elle sayı vermeyi yalnızca özel bir dağıtım stratejin (örneğin çoklu makineye yayma) varsa düşün.

Bir shard çökerse tüm bot mu durur?

Hayır. Shard'lar bağımsız süreçlerdir; biri çökse diğerleri çalışmaya devam eder. Yalnızca o shard'a düşen sunucular geçici olarak yanıt vermez. respawn açıkken ShardingManager çöken shard'ı otomatik yeniden başlatır, böylece kesinti kısa sürer.

Botun ölçeklenme zamanı mı geldi? Sharding mimarisi kurma, broadcastEval ile veri toplama veya hybrid sharding'e geçiş konusunda yardıma ihtiyacın varsa benimle iletişime geç — botunu birlikte büyük ölçeğe taşıyalım.

Bu kategorideki tüm yazılar →

Devamı için