Ana içeriğe geç

SignalR Scaling

SignalR ölçeklenmesi, birden fazla sunucuda gerçek zamanlı iletişimin tutarlı çalışmasını sağlar; yanlış yapılandırma mesaj kaybına ve bağlantı sorunlarına yol açar.


1. Backplane Olmadan Çoklu Sunucu

Yanlış Kullanım: Birden fazla sunucuda backplane kullanmamak.

builder.Services.AddSignalR(); // Tek sunucuda çalışır
// Load balancer arkasında Server1'deki kullanıcı Server2'dekine mesaj gönderemez

İdeal Kullanım: Redis backplane ile sunucular arası mesajlaşma sağlayın.

builder.Services.AddSignalR()
    .AddStackExchangeRedis(builder.Configuration.GetConnectionString("Redis"), options =>
    {
        options.Configuration.ChannelPrefix = RedisChannel.Literal("MyApp");
    });

2. Sticky Sessions Yönetmemek

Yanlış Kullanım: WebSocket bağlantısını load balancer’da dengelememek.

// Load balancer her isteği farklı sunucuya yönlendirir
// WebSocket handshake ve bağlantı farklı sunuculara düşer - başarısız

İdeal Kullanım: Sticky session veya WebSocket desteği olan load balancer kullanın.

// Azure SignalR Service ile ölçekleme sorununu tamamen ortadan kaldırın
builder.Services.AddSignalR()
    .AddAzureSignalR(builder.Configuration["Azure:SignalR:ConnectionString"]);

// Veya Nginx sticky session
// upstream backend {
//     ip_hash;
//     server app1:5000;
//     server app2:5000;
// }

3. MessagePack Kullanmamak

Yanlış Kullanım: Büyük veri transferinde JSON kullanmak.

builder.Services.AddSignalR(); // Varsayılan JSON protokolü
// Büyük mesajlarda yüksek bandwidth ve yavaş serialization

İdeal Kullanım: MessagePack ile daha hızlı ve küçük mesajlar gönderin.

builder.Services.AddSignalR()
    .AddMessagePackProtocol();

// İstemci tarafı
var connection = new HubConnectionBuilder()
    .WithUrl("/chatHub")
    .AddMessagePackProtocol()
    .Build();

4. Reconnection Stratejisi Olmamak

Yanlış Kullanım: Bağlantı koptuğunda yeniden bağlanma yapmamak.

var connection = new HubConnectionBuilder()
    .WithUrl("/chatHub")
    .Build();

await connection.StartAsync();
// Bağlantı koparsa kullanıcı kalıcı olarak bağlantısını kaybeder

İdeal Kullanım: Otomatik yeniden bağlanma stratejisi tanımlayın.

var connection = new HubConnectionBuilder()
    .WithUrl("/chatHub")
    .WithAutomaticReconnect(new[] { TimeSpan.Zero, TimeSpan.FromSeconds(2),
        TimeSpan.FromSeconds(5), TimeSpan.FromSeconds(10) })
    .Build();

connection.Reconnecting += error =>
{
    // UI'da "Yeniden bağlanılıyor..." göster
    return Task.CompletedTask;
};

connection.Reconnected += connectionId =>
{
    // Kaçırılan mesajları yeniden yükle
    return Task.CompletedTask;
};

connection.Closed += async error =>
{
    await Task.Delay(5000);
    await connection.StartAsync(); // Son çare: manuel yeniden başlat
};

5. Authentication Yapmamak

Yanlış Kullanım: Hub’a kimlik doğrulaması olmadan erişim izni vermek.

public class AdminHub : Hub
{
    public async Task DeleteUser(int userId)
    {
        await _userService.DeleteAsync(userId); // Herkes admin işlemi yapabilir!
    }
}

İdeal Kullanım: Hub’a authentication ve authorization uygulayın.

[Authorize]
public class AdminHub : Hub
{
    [Authorize(Roles = "Admin")]
    public async Task DeleteUser(int userId)
    {
        await _userService.DeleteAsync(userId);
        await Clients.All.SendAsync("UserDeleted", userId);
    }
}

// JWT ile SignalR authentication
builder.Services.AddAuthentication().AddJwtBearer(options =>
{
    options.Events = new JwtBearerEvents
    {
        OnMessageReceived = context =>
        {
            var accessToken = context.Request.Query["access_token"];
            var path = context.HttpContext.Request.Path;
            if (!string.IsNullOrEmpty(accessToken) && path.StartsWithSegments("/hubs"))
            {
                context.Token = accessToken;
            }
            return Task.CompletedTask;
        }
    };
});