Modern web uygulamalarında kullanıcı deneyimini zirveye taşımak için anlık veri akışı hayati önem taşır. Peki, .NET Core ve Angular gibi güçlü teknolojileri SignalR ile birleştirerek gerçek zamanlı güncellemeleri nasıl sağlayabiliriz? Bu makale, sunucudan istemciye sorunsuz veri akışı sağlama sürecini adım adım açıklıyor.
Günümüzün dinamik dünyasında, kullanıcıların uygulamalarla etkileşim beklentileri hiç olmadığı kadar yüksek. Geleneksel web uygulamaları, kullanıcının bir eylemde bulunmasını (sayfayı yenileme, bir butona tıklama) bekleyerek sunucudan veri çeker. Bu yöntem, statik içerikler veya nadiren güncellenen veriler için yeterli olsa da, anlık değişimlerin olduğu senaryolarda yetersiz kalır. Örneğin, bir çevrimiçi sohbet uygulamasında her mesaj için sayfayı yenilemek veya bir hisse senedi takip uygulamasında her fiyat değişimi için manuel güncelleme yapmak hem kullanıcı deneyimini kötüleştirir hem de gereksiz sunucu yükü oluşturur.
İşte tam da bu noktada, gerçek zamanlı veri akışı çözümleri devreye giriyor. Anlık bildirimler, canlı sohbetler, borsa takibi, spor müsabakası skorları, çok kullanıcılı oyunlar veya interaktif dashboard’lar gibi uygulamalar, sunucudan istemciye anında ve otomatik veri iletimine ihtiyaç duyar. Bu sayede kullanıcılar, en güncel bilgilere anında erişebilir, karar alma süreçlerini hızlandırabilir ve çok daha akıcı bir deneyim yaşayabilirler. Bu tür bir iletişim modeli, uygulamanızın etkileşimini ve yanıt verme hızını önemli ölçüde artırır. Geleneksel HTTP request-response modelinin aksine, gerçek zamanlı sistemler, sunucunun istemcilere proaktif olarak bilgi göndermesini sağlar. Bu, özellikle veri hacminin yüksek olduğu ve güncellemelerin sık olduğu durumlarda performansı ve verimliliği artırır.
Bu gereksinimleri karşılamak için geliştirilen teknolojilerden biri de SignalR‘dır. SignalR, .NET ekosisteminde gerçek zamanlı web işlevselliği eklemek için güçlü ve kullanımı kolay bir kütüphanedir. WebSockets, Server-Sent Events ve Long Polling gibi farklı transport mekanizmalarını otomatik olarak yöneterek, geliştiricilerin tarayıcılar ve sunucular arasında kesintisiz, iki yönlü iletişimi kolayca kurmasını sağlar. .NET Core’un modüler yapısıyla birleştiğinde, SignalR modern, yüksek performanslı ve ölçeklenebilir gerçek zamanlı uygulamalar geliştirmek için ideal bir çözüm haline gelir. Bu makalede, .NET Core backend’i ve Angular frontend’i kullanarak SignalR ile nasıl gerçek zamanlı veri güncellemeleri yapacağımızı adım adım inceleyeceğiz. Böylece, kullanıcılarınıza sadece bilgi değil, aynı zamanda anlık, dinamik ve sürükleyici bir deneyim sunabileceksiniz.
SignalR Nedir ve Temelleri Nelerdir?
SignalR, Microsoft tarafından geliştirilen ve ASP.NET Core uygulamalarınıza gerçek zamanlı web işlevselliği eklemenizi sağlayan açık kaynaklı bir kütüphanedir. Peki, bu tam olarak ne anlama geliyor ve nasıl çalışıyor? Temel olarak, SignalR sunucunuzun bağlı istemcilere (web tarayıcıları, mobil uygulamalar vb.) anında veri göndermesini mümkün kılar. Geleneksel HTTP istek-yanıt modeli, istemcinin bir istek göndermesini ve sunucunun buna yanıt vermesini gerektirirken, SignalR sunucunun istemcilere aktif olarak “push” yapmasına olanak tanır.
SignalR’ın en büyük avantajlarından biri, farklı transport mekanizmalarını otomatik olarak yönetmesidir. Yani geliştiricilerin WebSockets, Server-Sent Events veya Long Polling gibi teknolojilerle uğraşmasına gerek kalmaz. SignalR, bağlantı kurmaya çalışırken istemci ve sunucunun yeteneklerine göre en iyi transport yöntemini otomatik olarak seçer. Eğer WebSockets destekleniyorsa, bu en verimli yöntem tercih edilir; aksi takdirde, daha eski tarayıcılar için Server-Sent Events veya Long Polling kullanılır. Bu “düşme” (fall-back) mekanizması, uygulamanızın geniş bir yelpazedeki cihaz ve tarayıcıda çalışmasını garanti eder.
SignalR’ın temel bileşenleri şunlardır:
-
Hub’lar (Hubs): SignalR’ın merkezidir. Bir Hub, istemcilerle sunucu arasındaki iletişim kanalını yöneten bir sınıftır. Hub’lar üzerinden istemciler sunucudaki metodları çağırabilir (RPC – Remote Procedure Call), sunucu da bağlı istemcilerdeki metodları çağırabilir. Örneğin, bir sohbet uygulamasında, bir Hub “mesaj gönder” veya “mesaj al” gibi işlevleri içerebilir. Hub’lar genellikle türetildiği
Hubsınıfından miras alınır. - İstemciler (Clients): Sunucuya bağlı olan tarayıcılar, masaüstü uygulamaları veya mobil cihazlardır. İstemciler, SignalR client kütüphanelerini kullanarak Hub’a bağlanır, Hub metodlarını çağırır ve Hub’dan gelen mesajları dinler. JavaScript, TypeScript (Angular için), .NET, Java gibi birçok dilde istemci kütüphaneleri bulunur.
- Bağlantılar (Connections): Bir istemci ile Hub arasında kurulan kalıcı bağlantıdır. Bu bağlantı, verinin iki yönlü akışını sağlar. SignalR, bağlantının kesilmesi durumunda otomatik yeniden bağlantı kurma yeteneğine de sahiptir, bu da uygulamaların daha kararlı olmasını sağlar.
SignalR’ın çalışma prensibi oldukça basittir: İstemci bir SignalR Hub’ına bağlanır. Bağlantı kurulduktan sonra, istemci sunucudaki Hub metodlarını çağırabilir. Aynı şekilde, sunucu da Hub aracılığıyla belirli bir istemciye, bir istemci grubuna veya tüm bağlı istemcilere veri gönderebilir. Bu işlem genellikle istemcide tanımlanmış JavaScript fonksiyonlarını veya diğer dildeki metodları çağırarak gerçekleştirilir. Bu iki yönlü iletişim yeteneği, anlık bildirimler, sohbet odaları, canlı veri tabloları ve çok kullanıcılı etkileşimli uygulamalar gibi senaryolar için SignalR’ı mükemmel bir seçim haline getirir.
ChatHub, NotificationHub, DataUpdateHub).
.NET Core ile SignalR Sunucusu Nasıl Kurulur?
Şimdi teoriden pratiğe geçelim ve .NET Core backend’inizde bir SignalR sunucusunu nasıl yapılandıracağınızı adım adım inceleyelim. Bu bölümde, projenizi oluşturmaktan bir Hub tanımlamaya ve bunu yapılandırmaya kadar tüm süreci ele alacağız. Amacımız, Angular istemcilerimizin bağlanabileceği ve gerçek zamanlı veri alışverişi yapabileceği basit bir API oluşturmak.
Adım 1: Yeni Bir .NET Core Web API Projesi Oluşturma
Öncelikle, Visual Studio veya .NET CLI kullanarak yeni bir ASP.NET Core Web API projesi oluşturalım. Bu, SignalR Hub’ımızı barındıracak temel yapımız olacak.
dotnet new webapi -n SignalRDemo.Api
cd SignalRDemo.Api
Bu komutlar, SignalRDemo.Api adında yeni bir Web API projesi oluşturacak ve proje dizinine geçiş yapacaktır.
Adım 2: SignalR NuGet Paketlerini Kurma
SignalR işlevselliğini projenize eklemek için gerekli NuGet paketini yüklemeniz gerekiyor. Genellikle ASP.NET Core projelerinde Microsoft.AspNetCore.SignalR paketi kullanılır.
dotnet add package Microsoft.AspNetCore.SignalR
Adım 3: Bir SignalR Hub'ı Oluşturma
Şimdi istemcilerimizle iletişim kuracak Hub sınıfını oluşturalım. Proje kök dizininde veya uygun bir klasörde (örn. Hubs klasörü altında) yeni bir C# sınıfı dosyası (örn. NotificationHub.cs) oluşturalım.
// Hubs/NotificationHub.cs
using Microsoft.AspNetCore.SignalR;
using System.Threading.Tasks;
namespace SignalRDemo.Api.Hubs
{
public class NotificationHub : Hub
{
public async Task SendNotification(string user, string message)
{
// Bağlı tüm istemcilere "ReceiveNotification" metodunu çağırır.
await Clients.All.SendAsync("ReceiveNotification", user, message);
}
public async Task SendNotificationToCaller(string message)
{
// Yalnızca mesajı gönderen istemciye "ReceiveNotification" metodunu çağırır.
await Clients.Caller.SendAsync("ReceiveNotification", "Server", message);
}
public override async Task OnConnectedAsync()
{
// Yeni bir istemci bağlandığında çalışır.
Console.WriteLine($"Client connected: {Context.ConnectionId}");
await Clients.Caller.SendAsync("ReceiveNotification", "Server", $"Hoş geldiniz, Bağlantı Kimliğiniz: {Context.ConnectionId}");
await base.OnConnectedAsync();
}
public override async Task OnDisconnectedAsync(Exception? exception)
{
// Bir istemcinin bağlantısı kesildiğinde çalışır.
Console.WriteLine($"Client disconnected: {Context.ConnectionId}");
await base.OnDisconnectedAsync(exception);
}
}
}
Yukarıdaki örnekte, NotificationHub sınıfı Hub sınıfından miras alır. SendNotification metodu, Hub'a bağlı tüm istemcilere bir mesaj gönderirken, SendNotificationToCaller sadece çağrıyı yapan istemciye mesaj gönderir. OnConnectedAsync ve OnDisconnectedAsync metodları, istemci bağlantıları durumunda tetiklenen yaşam döngüsü metodlarıdır.
Adım 4: Startup.cs (veya Program.cs) Dosyasını Yapılandırma
.NET 6 ve sonraki sürümlerde Program.cs dosyasında, önceki sürümlerde ise Startup.cs dosyasında SignalR servislerini ve Hub'ınızı uygulamanıza eklemeniz gerekir. Bu, SignalR'ın HTTP pipeline'ına entegre olmasını sağlar.
.NET 6+ (Minimal API) için Program.cs
// Program.cs
using SignalRDemo.Api.Hubs;
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.DependencyInjection;
var builder = WebApplication.CreateBuilder(args);
// SignalR servislerini ekle
builder.Services.AddSignalR();
builder.Services.AddControllers(); // Eğer API Controller kullanıyorsanız
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
// CORS yapılandırması - Angular uygulamanızın farklı bir kaynaktan bağlanabilmesi için önemli
builder.Services.AddCors(options =>
{
options.AddPolicy("CorsPolicy",
builder => builder
.WithOrigins("http://localhost:4200") // Angular uygulamanızın adresi
.AllowAnyMethod()
.AllowAnyHeader()
.AllowCredentials()); // Kimlik doğrulama gerektiren bağlantılar için
});
var app = builder.Build();
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}
app.UseHttpsRedirection();
app.UseCors("CorsPolicy"); // CORS politikasını etkinleştir
app.UseAuthorization();
app.MapControllers(); // Eğer API Controller kullanıyorsanız
// SignalR Hub'ını HTTP pipeline'ına map et
app.MapHub("/notificationHub"); // Angular'dan bu URL'ye bağlanacağız
app.Run();
Bu yapılandırma ile, /notificationHub adresine yapılan istekler NotificationHub sınıfınıza yönlendirilecektir. CORS ayarlarının doğru yapılması kritik öneme sahiptir, çünkü Angular uygulamanız genellikle farklı bir portta (örn. 4200) çalışacağı için cross-origin isteği yapacaktır.
Adım 5: Bir Controller'dan veya Servisten Mesaj Gönderme (Opsiyonel)
SignalR Hub'ınıza sadece istemcilerden değil, aynı zamanda backend'inizdeki diğer servislerden veya controller'lardan da mesaj gönderebilirsiniz. Bu, bir API isteği işlendikten sonra istemcilere bildirim göndermek istediğiniz durumlarda kullanışlıdır.
// Controllers/NotificationController.cs
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.SignalR;
using SignalRDemo.Api.Hubs;
using System.Threading.Tasks;
namespace SignalRDemo.Api.Controllers
{
[ApiController]
[Route("[controller]")]
public class NotificationController : ControllerBase
{
private readonly IHubContext _hubContext;
public NotificationController(IHubContext hubContext)
{
_hubContext = hubContext;
}
[HttpPost]
public async Task PostNotification([FromBody] NotificationMessage message)
{
// Tüm bağlı istemcilere notification gönder
await _hubContext.Clients.All.SendAsync("ReceiveNotification", message.User, message.Text);
return Ok();
}
}
public class NotificationMessage
{
public string User { get; set; }
public string Text { get; set; }
}
}
Bu örnekte, IHubContext interface'i inject edilerek, bir HTTP POST isteği geldiğinde SignalR Hub aracılığıyla tüm bağlı istemcilere bir bildirim gönderilir. Artık .NET Core SignalR sunucunuz hazır! Şimdi Angular kısmına geçebiliriz.
Angular Uygulamasında SignalR İstemcisi Nasıl Oluşturulur?
.NET Core backend'imizi yapılandırdığımıza göre, şimdi Angular uygulamasında SignalR istemcisini nasıl kuracağımızı ve backend'imizle nasıl iletişim kuracağımızı görelim. Bu bölüm, Angular CLI ile proje oluşturmaktan SignalR hizmetini yapılandırmaya ve bileşenlerinizde kullanmaya kadar uzanacak.
Adım 1: Yeni Bir Angular Projesi Oluşturma
Eğer henüz bir Angular projeniz yoksa, Angular CLI kullanarak yeni bir proje oluşturalım:
ng new signalr-angular-client
cd signalr-angular-client
Bu komutlar, yeni bir Angular projesi oluşturacak ve gerekli bağımlılıkları yükleyecektir.
Adım 2: SignalR İstemci Paketini Kurma
Angular uygulamanızda SignalR ile etkileşim kurabilmek için Microsoft'un SignalR JavaScript istemci kütüphanesini kurmanız gerekir:
npm install @microsoft/signalr
Adım 3: SignalR Bağlantısını Yönetecek Bir Hizmet Oluşturma
SignalR bağlantı mantığını ayrı bir hizmet içinde tutmak, uygulamanızın daha temiz ve yönetilebilir olmasını sağlar. Angular CLI kullanarak bir hizmet oluşturalım:
ng generate service signalr
Şimdi src/app/signalr.service.ts dosyasını aşağıdaki gibi düzenleyelim:
// src/app/signalr.service.ts
import { Injectable } from '@angular/core';
import * as signalR from '@microsoft/signalr';
import { Subject } from 'rxjs';
@Injectable({
providedIn: 'root'
})
export class SignalrService {
private hubConnection: signalR.HubConnection;
public data: Subject = new Subject(); // Sunucudan gelen verileri yaymak için
public connectionEstablished: Subject = new Subject();
private connectionIsEstablished = false;
constructor() { }
public startConnection = () => {
this.hubConnection = new signalR.HubConnectionBuilder()
.withUrl('https://localhost:7119/notificationHub', { // .NET Core API'nizin adresi ve Hub yolu
skipNegotiation: true,
transport: signalR.HttpTransportType.WebSockets // WebSockets tercih et
})
.configureLogging(signalR.LogLevel.Information)
.build();
this.hubConnection
.start()
.then(() => {
console.log('SignalR bağlantısı kuruldu.');
this.connectionIsEstablished = true;
this.connectionEstablished.next(true);
})
.catch(err => {
console.error('SignalR bağlantısı kurulurken hata oluştu: ' + err);
this.connectionIsEstablished = false;
this.connectionEstablished.next(false);
// Hata durumunda yeniden deneme mekanizması eklenebilir
setTimeout(() => this.startConnection(), 5000);
});
}
public addTransferDataListener = () => {
this.hubConnection.on('ReceiveNotification', (user: string, message: string) => {
this.data.next({ user, message }); // Gelen veriyi Subject üzerinden yayınla
});
}
public sendMessageToHub = (user: string, message: string) => {
if (this.hubConnection.state === signalR.HubConnectionState.Connected) {
this.hubConnection.invoke('SendNotification', user, message)
.catch(err => console.error('Hub\'a mesaj gönderilirken hata oluştu: ' + err));
} else {
console.warn('SignalR bağlantısı mevcut değil. Mesaj gönderilemedi.');
}
}
public stopConnection = () => {
if (this.hubConnection && this.hubConnection.state === signalR.HubConnectionState.Connected) {
this.hubConnection.stop()
.then(() => console.log('SignalR bağlantısı kesildi.'))
.catch(err => console.error('SignalR bağlantısı kesilirken hata oluştu: ' + err));
}
}
}
Yukarıdaki hizmet, SignalR bağlantısını yönetir. startConnection metodu sunucuya bağlanır. addTransferDataListener, sunucudan gelecek 'ReceiveNotification' metodunu dinler ve gelen veriyi data Subject'i aracılığıyla diğer bileşenlere yayar. sendMessageToHub ise istemciden Hub'a mesaj göndermek için kullanılır.
withUrl metodu içerisinde skipNegotiation: true ve transport: signalR.HttpTransportType.WebSockets kullanmak, SignalR'ın doğrudan WebSockets ile bağlantı kurmaya çalışmasını sağlar ve başlangıç bağlantı süresini kısaltabilir. Ancak, eski tarayıcılar için fall-back mekanizmasını devre dışı bırakır.
Adım 4: Bileşenlerde SignalR Hizmetini Kullanma
Şimdi SignalrService'imizi bir Angular bileşeninde nasıl kullanacağımızı görelim. Örneğin, AppComponent içerisinde:
// src/app/app.component.ts
import { Component, OnInit, OnDestroy } from '@angular/core';
import { SignalrService } from './signalr.service';
import { Subscription } from 'rxjs';
interface NotificationMessage {
user: string;
message: string;
}
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit, OnDestroy {
title = 'SignalR Real-Time Demo';
notifications: NotificationMessage[] = [];
newMessage: string = '';
userName: string = 'AngularClient';
private dataSubscription: Subscription;
private connectionSubscription: Subscription;
constructor(public signalrService: SignalrService) { }
ngOnInit() {
this.signalrService.startConnection();
this.connectionSubscription = this.signalrService.connectionEstablished.subscribe(connected => {
if (connected) {
this.signalrService.addTransferDataListener();
this.dataSubscription = this.signalrService.data.subscribe((message: NotificationMessage) => {
this.notifications.push(message);
console.log('Alınan bildirim:', message);
});
}
});
}
sendMessage() {
if (this.newMessage.trim()) {
this.signalrService.sendMessageToHub(this.userName, this.newMessage);
this.newMessage = ''; // Mesajı gönderdikten sonra giriş alanını temizle
}
}
ngOnDestroy() {
this.dataSubscription?.unsubscribe();
this.connectionSubscription?.unsubscribe();
this.signalrService.stopConnection(); // Bağlantıyı temizlemeyi unutmayın
}
}
Ve src/app/app.component.html dosyasını da basit bir UI ile güncelleyelim:
{{ title }}
Alınan Bildirimler:
-
{{ notif.user }}: {{ notif.message }}
Bu örnekte, AppComponent, SignalrService'i inject eder ve Angular uygulamanız başladığında SignalR bağlantısını başlatır. Sunucudan gelen mesajlar notifications dizisine eklenir ve UI'da gösterilir. Kullanıcılar ayrıca kendi mesajlarını da Hub'a gönderebilirler. OnDestroy yaşam döngüsü metodunda bağlantıyı ve abonelikleri temizlemek, hafıza sızıntılarını önlemek için önemlidir. Bu kurulumla, Angular istemciniz artık .NET Core SignalR sunucunuzla gerçek zamanlı olarak iletişim kurmaya hazırdır!
Gerçek Dünya Senaryosu: Anlık Borsa Takip Uygulaması
Şimdiye kadar SignalR'ın temellerini ve .NET Core ile Angular'da nasıl kurulacağını öğrendik. Peki, bu bilgiyi gerçek bir senaryoda nasıl uygularız? Bir anlık borsa takip uygulaması, gerçek zamanlı veri akışının gücünü sergilemek için mükemmel bir örnektir. Kullanıcılar, hisse senedi fiyatlarındaki anlık değişimleri gecikmesiz bir şekilde görebilirler.
Senaryo: Borsa Verilerini Anlık Güncelleme
Hayal edelim ki bir hisse senedi takip uygulamasında, sunucu tarafında sürekli olarak değişen hisse senedi fiyatları var ve bu fiyatların anlık olarak tüm bağlı istemcilere yansıtılması gerekiyor. Bu senaryoda SignalR, sunucu ve istemciler arasındaki köprü görevi görecektir. Backend'imiz sahte (mock) borsa verileri üretecek ve bunları düzenli aralıklarla SignalR Hub'ına gönderecek, Angular frontend'imiz ise bu verileri alıp UI'da gösterecektir.
.NET Core Backend Tarafında Uygulama
Öncelikle, hisse senedi fiyatlarını simüle eden ve bunları SignalR Hub'ına yayınlayan bir servis oluşturalım. Bunun için StockTickerHub.cs adında yeni bir Hub ve bir de arka plan servisi (BackgroundService) tanımlayacağız.
// Hubs/StockTickerHub.cs
using Microsoft.AspNetCore.SignalR;
using System.Threading.Tasks;
namespace SignalRDemo.Api.Hubs
{
public class StockTickerHub : Hub
{
// İstemciler bu Hub'a bağlanabilir ve metodları çağırabilir.
// Bu örnekte, sunucudan istemciye push odaklıyız.
// Ancak istemcilerin de sunucuya özel istekler göndermesi için metodlar eklenebilir.
public async Task JoinStockGroup(string stockSymbol)
{
await Groups.AddToGroupAsync(Context.ConnectionId, stockSymbol);
await Clients.Caller.SendAsync("ReceiveNotification", "Server", $"{stockSymbol} grubuna katıldınız.");
}
public async Task LeaveStockGroup(string stockSymbol)
{
await Groups.RemoveFromGroupAsync(Context.ConnectionId, stockSymbol);
await Clients.Caller.SendAsync("ReceiveNotification", "Server", $"{stockSymbol} grubundan ayrıldınız.");
}
}
public class StockPrice
{
public string Symbol { get; set; }
public decimal Price { get; set; }
public DateTime LastUpdated { get; set; }
}
}
Şimdi bu Hub'a düzenli olarak veri gönderecek bir arka plan servisi oluşturalım:
// Services/StockSimulatorService.cs
using Microsoft.AspNetCore.SignalR;
using SignalRDemo.Api.Hubs;
using System.Collections.Concurrent;
using System.Threading;
using System.Threading.Tasks;
using System;
using Microsoft.Extensions.Hosting;
namespace SignalRDemo.Api.Services
{
public class StockSimulatorService : BackgroundService
{
private readonly IHubContext _hubContext;
private readonly ConcurrentDictionary _stockPrices;
private readonly Random _random;
public StockSimulatorService(IHubContext hubContext)
{
_hubContext = hubContext;
_stockPrices = new ConcurrentDictionary();
_random = new Random();
// Başlangıç hisse senetleri
_stockPrices.TryAdd("MSFT", new StockPrice { Symbol = "MSFT", Price = 250.00m, LastUpdated = DateTime.UtcNow });
_stockPrices.TryAdd("GOOG", new StockPrice { Symbol = "GOOG", Price = 1500.00m, LastUpdated = DateTime.UtcNow });
_stockPrices.TryAdd("AAPL", new StockPrice { Symbol = "AAPL", Price = 170.00m, LastUpdated = DateTime.UtcNow });
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
while (!stoppingToken.IsCancellationRequested)
{
await Task.Delay(2000, stoppingToken); // Her 2 saniyede bir güncelle
foreach (var symbol in _stockPrices.Keys)
{
// Fiyatları rastgele güncelle
_stockPrices.TryGetValue(symbol, out var currentPrice);
if (currentPrice != null)
{
var change = (decimal)(_random.NextDouble() * 10 - 5); // -5 ile +5 arasında değişim
currentPrice.Price += change;
currentPrice.LastUpdated = DateTime.UtcNow;
// Tüm bağlı istemcilere güncellenmiş fiyatı gönder
await _hubContext.Clients.All.SendAsync("ReceiveStockUpdate", currentPrice);
// Veya sadece ilgili gruba göndermek için:
// await _hubContext.Clients.Group(symbol).SendAsync("ReceiveStockUpdate", currentPrice);
}
}
}
}
}
}
Program.cs dosyanıza hem StockTickerHub'ı map etmeyi hem de StockSimulatorService'i eklemeyi unutmayın:
// Program.cs
// ... (önceki kodlar)
using SignalRDemo.Api.Services;
// SignalR servislerini ekle
builder.Services.AddSignalR();
builder.Services.AddHostedService(); // Arka plan servisini ekle
// ... (diğer servisler)
var app = builder.Build();
// ... (orta katmanlar)
app.MapHub("/notificationHub");
app.MapHub("/stockTickerHub"); // Yeni StockTickerHub'ı map et
app.Run();
Angular Frontend Tarafında Uygulama
Angular tarafında, yeni bir hizmet (StockTickerService) oluşturarak StockTickerHub'a bağlanacağız ve gelen hisse senedi güncellemelerini işleyeceğiz.
// src/app/stock-ticker.service.ts
import { Injectable } from '@angular/core';
import * as signalR from '@microsoft/signalr';
import { Subject } from 'rxjs';
export interface StockPrice {
symbol: string;
price: number;
lastUpdated: string;
}
@Injectable({
providedIn: 'root'
})
export class StockTickerService {
private hubConnection: signalR.HubConnection;
public stockUpdates: Subject = new Subject();
public connectionStatus: Subject = new Subject();
constructor() { }
public startConnection = () => {
this.hubConnection = new signalR.HubConnectionBuilder()
.withUrl('https://localhost:7119/stockTickerHub', {
skipNegotiation: true,
transport: signalR.HttpTransportType.WebSockets
})
.configureLogging(signalR.LogLevel.Information)
.build();
this.hubConnection
.start()
.then(() => {
console.log('Stock Ticker SignalR bağlantısı kuruldu.');
this.connectionStatus.next(true);
this.addStockUpdateListener();
})
.catch(err => {
console.error('Stock Ticker SignalR bağlantısı kurulurken hata oluştu: ' + err);
this.connectionStatus.next(false);
setTimeout(() => this.startConnection(), 5000); // Bağlantı hatasında yeniden dene
});
}
private addStockUpdateListener = () => {
this.hubConnection.on('ReceiveStockUpdate', (stock: StockPrice) => {
this.stockUpdates.next(stock);
});
}
public stopConnection = () => {
if (this.hubConnection && this.hubConnection.state === signalR.HubConnectionState.Connected) {
this.hubConnection.stop()
.then(() => console.log('Stock Ticker SignalR bağlantısı kesildi.'))
.catch(err => console.error('Stock Ticker SignalR bağlantısı kesilirken hata oluştu: ' + err));
}
}
public joinStockGroup(symbol: string) {
if (this.hubConnection.state === signalR.HubConnectionState.Connected) {
this.hubConnection.invoke('JoinStockGroup', symbol)
.catch(err => console.error('Hisse senedi grubuna katılırken hata oluştu: ' + err));
}
}
public leaveStockGroup(symbol: string) {
if (this.hubConnection.state === signalR.HubConnectionState.Connected) {
this.hubConnection.invoke('LeaveStockGroup', symbol)
.catch(err => console.error('Hisse senedi grubundan ayrılırken hata oluştu: ' + err));
}
}
}
Şimdi bu hizmeti bir Angular bileşeninde (örn. StockTickerComponent) kullanalım:
// src/app/stock-ticker/stock-ticker.component.ts
import { Component, OnInit, OnDestroy } from '@angular/core';
import { StockTickerService, StockPrice } from '../stock-ticker.service';
import { Subscription } from 'rxjs';
@Component({
selector: 'app-stock-ticker',
templateUrl: './stock-ticker.component.html',
styleUrls: ['./stock-ticker.component.css']
})
export class StockTickerComponent implements OnInit, OnDestroy {
currentStockPrices: { [key: string]: StockPrice } = {};
private stockUpdateSubscription: Subscription;
private connectionStatusSubscription: Subscription;
isConnected: boolean = false;
constructor(private stockTickerService: StockTickerService) { }
ngOnInit(): void {
this.stockTickerService.startConnection();
this.connectionStatusSubscription = this.stockTickerService.connectionStatus.subscribe(status => {
this.isConnected = status;
if (status) {
this.stockUpdateSubscription = this.stockTickerService.stockUpdates.subscribe(stock => {
this.currentStockPrices[stock.symbol] = stock;
});
} else {
this.stockUpdateSubscription?.unsubscribe();
}
});
}
ngOnDestroy(): void {
this.stockUpdateSubscription?.unsubscribe();
this.connectionStatusSubscription?.unsubscribe();
this.stockTickerService.stopConnection();
}
// İstemcinin belirli hisse senetlerine abone olmasını sağlayan metodlar
joinStockGroup(symbol: string) {
this.stockTickerService.joinStockGroup(symbol);
}
leaveStockGroup(symbol: string) {
this.stockTickerService.leaveStockGroup(symbol);
}
}
Ve basit bir UI için stock-ticker.component.html:
Anlık Borsa Takip
Bağlantı Durumu: {{ isConnected ? 'Bağlı' : 'Bağlantı Yok' }}
Sembol
Fiyat
Son Güncelleme
İşlem
{{ stockSymbol.value.symbol }}
(currentStockPrices[stockSymbol.key]?.price || 0)" [class.price-down]="stockSymbol.value.price < (currentStockPrices[stockSymbol.key]?.price || 0)">
{{ stockSymbol.value.price | currency:'USD':'symbol':'1.2-2' }}
{{ stockSymbol.value.lastUpdated | date:'mediumTime' }}
Bu senaryo, SignalR'ın sadece mesajlaşma için değil, aynı zamanda sürekli güncellenen ve kritik verilerin anında iletilmesi gereken durumlar için de ne kadar güçlü olduğunu göstermektedir. Kullanıcılar artık hisse senedi fiyatlarını anında takip edebilir ve uygulamadan ayrılmadan en güncel bilgilere erişebilirler.
Performans ve Ölçeklenebilirlik İçin İpuçları
Gerçek zamanlı uygulamalar geliştirirken performans ve ölçeklenebilirlik her zaman ön planda olmalıdır. İşte SignalR uygulamalarınızı daha performanslı ve ölçeklenebilir hale getirmek için bazı önemli ipuçları:
1. Azure SignalR Service Kullanımı
Azure SignalR Service, özellikle yüksek trafikli veya birden fazla sunucuya yayılmış uygulamalar için vazgeçilmez bir çözümdür. Geleneksel olarak, SignalR'ı kendi sunucularınızda barındırdığınızda, birden fazla sunucu arasında istemci bağlantılarını ve mesajlarını yönetmek için bir "backplane" (örn. Redis) kullanmanız gerekir. Azure SignalR Service, bu backplane yönetimini sizin için üstlenir, sunucu kaynaklarınızı boşaltır ve uygulamanızın ölçeklenmesini çok daha kolay hale getirir. Sunucularınız sadece iş mantığınıza odaklanırken, bağlantı yönetimi, mesajlaşma ve ölçeklendirme Azure tarafından yönetilir.
// Azure SignalR Service entegrasyonu için Program.cs'de yapılan değişiklikler
// builder.Services.AddSignalR().AddAzureSignalR(); // Bu satırı kullanın
builder.Services.AddSignalR()
.AddAzureSignalR(options =>
{
options.ConnectionString = builder.Configuration["Azure:SignalR:ConnectionString"];
// Ayrıca service mode'u veya diğer ayarları da yapılandırabilirsiniz
});
2. Mesaj Boyutunu ve Sıklığını Optimize Etme
Gönderdiğiniz mesajların boyutu ve gönderilme sıklığı performansı doğrudan etkiler. Büyük veya çok sık gönderilen mesajlar ağ trafiğini artırır, istemci ve sunucu kaynaklarını tüketir. Gereksiz verileri filtreleyerek sadece ihtiyacınız olanı gönderin. Örneğin, bir hisse senedi güncellemesinde sadece değişen alanları gönderebilirsiniz. Ayrıca, hızlı değişen veriler için her değişiklikte mesaj göndermek yerine, belirli bir zaman aralığında (örn. her 200 ms) en son durumu toplu olarak göndermeyi düşünebilirsiniz (throttle/debounce).
3. Grup Yönetimi ve Hedefli Mesajlaşma
Clients.All.SendAsync() metodunu kullanmak tüm bağlı istemcilere mesaj gönderir ve bu, büyük ölçekli uygulamalarda performans sorunlarına yol açabilir. Bunun yerine, istemcileri mantıksal gruplara ayırarak sadece ilgili gruplara mesaj göndermeyi tercih edin. Örneğin, bir sohbet uygulamasında kullanıcıları sohbet odalarına göre gruplayabilir, bir borsa uygulamasında ise belirli hisse senetlerini takip eden kullanıcıları ayrı gruplara alabilirsiniz. Bu, gereksiz mesaj trafiğini azaltır ve sunucu yükünü hafifletir.
// Belirli bir gruba mesaj gönderme
await _hubContext.Clients.Group("MSFT_StockUsers").SendAsync("ReceiveStockUpdate", stockPrice);
// İstemcinin bir gruba katılması
public async Task JoinGroup(string groupName)
{
await Groups.AddToGroupAsync(Context.ConnectionId, groupName);
await Clients.Caller.SendAsync("Message", $"'{groupName}' grubuna katıldınız.");
}
4. Yetkilendirme ve Kimlik Doğrulama
SignalR bağlantılarınızın güvenliğini sağlamak için yetkilendirme ve kimlik doğrulama mekanizmalarını kullanın. .NET Core'un Identity ve JWT (JSON Web Token) entegrasyonları ile SignalR Hub'larınıza erişimi kısıtlayabilirsiniz. Sadece yetkili kullanıcıların belirli Hub'lara veya metodlara erişmesine izin vermek, hem güvenliği artırır hem de gereksiz bağlantıları ve kaynak tüketimini önler.
// Hub üzerinde yetkilendirme attribute'u kullanma
[Authorize] // Bu Hub'a erişim için yetkilendirme gereklidir
public class SecuredChatHub : Hub
{
// ...
}
// İstemci tarafında JWT ile bağlantı kurma (Angular örneği)
const connection = new signalR.HubConnectionBuilder()
.withUrl('https://localhost:7119/securedHub', {
accessTokenFactory: () => {
// JWT token'ınızı buradan döndürün
return localStorage.getItem('jwtToken');
}
})
.build();
5. Hata Yönetimi ve Yeniden Bağlantı Stratejileri
Ağ kesintileri veya sunucu problemleri nedeniyle SignalR bağlantıları kesilebilir. Hem sunucu hem de istemci tarafında sağlam hata yönetimi ve otomatik yeniden bağlantı stratejileri uygulamak önemlidir. SignalR istemcisi, varsayılan olarak yeniden bağlantı dener, ancak bu davranışı özelleştirebilirsiniz. Sunucu tarafında ise bağlantı kesintilerini loglayarak sorunları tespit edebilirsiniz.
// İstemci tarafında özel yeniden bağlantı stratejisi (Angular)
this.hubConnection = new signalR.HubConnectionBuilder()
.withUrl('https://localhost:7119/notificationHub')
.withAutomaticReconnect({
nextRetryDelayInMilliseconds: retryContext => {
if (retryContext.elapsedMilliseconds < 60000) {
// İlk dakika içinde 0, 2, 10, 30 saniye sonra tekrar dene
return [0, 2000, 10000, 30000][retryContext.previousRetryCount];
}
// Bir dakikadan sonra her zaman 30 saniye sonra tekrar dene
return 30000;
}
})
.build();
Bu ipuçları, SignalR uygulamalarınızın daha sağlam, güvenli ve performanslı olmasını sağlayarak gerçek dünya senaryolarına hazır hale gelmesine yardımcı olacaktır.
Sonuç ve Gelecek Perspektifi
Bu makale boyunca, .NET Core ve Angular uygulamalarınızda gerçek zamanlı veri akışı sağlamak için SignalR'ın nasıl kullanılacağını detaylı bir şekilde inceledik. Geleneksel istek-yanıt modelinin sınırlılıklarından, SignalR'ın temel bileşenlerine, adım adım kurulumundan, gerçek dünya borsa takip senaryosuna ve nihayet performans ile ölçeklenebilirlik ipuçlarına kadar geniş bir yelpazede bilgi edindik. Gördüğümüz gibi, SignalR, geliştiricilere WebSockets ve diğer transport mekanizmalarını soyutlayarak, sunucudan istemciye anlık veri push etme yeteneği sunar.
Bugünün modern web uygulamalarında anlık bildirimler, canlı sohbetler, dinamik dashboard'lar ve çok kullanıcılı etkileşimler vazgeçilmez hale gelmiştir. SignalR, .NET Core'un gücü ve Angular'ın esnekliği ile birleştiğinde, bu beklentileri karşılamak için sağlam, hızlı ve ölçeklenebilir bir temel oluşturur. Azure SignalR Service gibi bulut çözümleriyle entegre edildiğinde ise, uygulamanızın milyonlarca kullanıcıya hizmet verebilecek kapasiteye ulaşması oldukça kolaylaşır.
Gelecekte, gerçek zamanlı veri akışının önemi daha da artacak. Nesnelerin İnterneti (IoT), yapay zeka destekli akıllı sistemler ve giderek daha fazla interaktif hale gelen kullanıcı arayüzleri, anlık ve düşük gecikmeli iletişimi daha da kritik hale getirecek. SignalR gibi teknolojiler, bu yeni nesil uygulamaların geliştirilmesinde kilit bir rol oynamaya devam edecektir. Bu makaledeki bilgilerle donanarak, kendi gerçek zamanlı uygulamalarınızı başarıyla inşa edebilir ve kullanıcılarınıza olağanüstü deneyimler sunabilirsiniz.
Sıkça Sorulan Sorular (SSS)
1. SignalR yerine doğrudan WebSockets kullanmak daha mı iyi?
SignalR, WebSockets, Server-Sent Events ve Long Polling gibi alt düzey transport mekanizmalarını soyutlayan bir kütüphanedir. Doğrudan WebSockets kullanmak, tam kontrol sağlasa da, bağlantı yönetimi, yeniden bağlantı, gruplama ve hata yönetimi gibi karmaşık durumları manuel olarak halletmenizi gerektirir. SignalR bu zorlukları sizin için yönetir, böylece geliştiriciler daha çok iş mantığına odaklanabilir. Çoğu senaryo için SignalR, hem kolaylık hem de performans açısından daha iyi bir tercihtir. Sadece çok özel performans veya kontrol gereksinimleriniz varsa doğrudan WebSockets düşünebilirsiniz.
2. SignalR güvenlik konusunda neler sunuyor?
SignalR, ASP.NET Core güvenlik özellikleriyle tam entegredir. Kimlik doğrulama (Authentication) ve yetkilendirme (Authorization) mekanizmalarını kullanarak Hub'larınıza ve Hub metodlarınıza erişimi kısıtlayabilirsiniz. Örneğin, JWT token'ları ile kullanıcıları doğrulayabilir ve [Authorize] attribute'u ile sadece belirli rollere sahip kullanıcıların belirli metodları çağırmasına izin verebilirsiniz. Ayrıca, HTTPS üzerinden bağlantı kurarak iletişim kanalının güvenliğini sağlamak her zaman önerilir.
3. SignalR'ı hangi durumlarda kullanmalıyım?
SignalR, sunucudan istemcilere anında veri iletimi gereken her türlü senaryo için idealdir. Başlıca kullanım durumları şunlardır:
- Canlı sohbet uygulamaları
- Anlık bildirim sistemleri
- Borsa takip, spor skorları gibi canlı veri akışları
- Çok kullanıcılı çevrimiçi oyunlar
- Gerçek zamanlı gösterge panoları (dashboard'lar) ve izleme sistemleri
- Coğrafi konum takibi
- Anket ve oylama sistemleri
4. SignalR'ın ölçeklenebilirlik limitleri nelerdir?
Tek başına bir SignalR sunucusu, binlerce eşzamanlı bağlantıyı yönetebilir. Ancak, uygulamanız daha fazla trafik ve bağlantı gerektirdiğinde, ölçeklenebilirlik bir endişe haline gelebilir. Birden fazla sunucuya yayılmış bir SignalR kurulumunda, sunucular arası iletişimi sağlamak için bir "backplane" (örn. Redis veya SQL Server tabanlı bir backplane) kullanmanız gerekir. En iyi ve en kolay ölçeklenebilirlik çözümü ise Azure SignalR Service kullanmaktır. Azure SignalR Service, backplane yönetimini, bağlantı yük dengelemesini ve yüksek kullanılabilirliği otomatik olarak sağlayarak uygulamanızın milyonlarca eşzamanlı kullanıcıya ulaşmasını destekler.
5. Angular dışında başka framework'lerle SignalR kullanılabilir mi?
Evet, kesinlikle! SignalR'ın çeşitli platformlar için istemci kütüphaneleri bulunmaktadır. JavaScript istemci kütüphanesi, Angular'ın yanı sıra React, Vue.js veya Vanilla JavaScript ile de kullanılabilir. Ayrıca, .NET (WPF, WinForms, Blazor), Java, Python ve daha birçok platform için istemci kütüphaneleri veya topluluk tarafından geliştirilmiş adaptörler mevcuttur. Bu sayede, .NET Core SignalR backend'inizi kullanarak farklı teknolojilerdeki birçok frontend veya istemci uygulamasına gerçek zamanlı yetenekler ekleyebilirsiniz.
