SignalR trong ASP.NET Core 2026: Hướng Dẫn Toàn Diện về Real-Time, Hubs và Câu Hỏi Phỏng Vấn
Tìm hiểu SignalR trong ASP.NET Core để xây dựng giao tiếp real-time hai chiều. Hướng dẫn chi tiết về Hubs, Groups, xác thực, mở rộng với Redis và các câu hỏi phỏng vấn kỹ thuật.

SignalR cho phép giao tiếp hai chiều real-time giữa server và client trong các ứng dụng ASP.NET Core. Khác với mô hình request-response HTTP truyền thống, SignalR duy trì kết nối liên tục cho phép server đẩy cập nhật đến client ngay lập tức—thiết yếu cho các ứng dụng chat, dashboard trực tiếp, chỉnh sửa cộng tác và thông báo real-time.
SignalR tự động chọn transport tốt nhất có sẵn: WebSockets trước, sau đó Server-Sent Events, rồi Long Polling. Cơ chế fallback này đảm bảo tương thích trên tất cả trình duyệt và cấu hình mạng mà không cần thay đổi code.
Cách SignalR Hubs Hoạt Động cho Giao Tiếp Server-Client
Hub đóng vai trò như một pipeline cấp cao xử lý việc gọi method giữa client và server. Client gọi method trên hub, và hub có thể gọi method trên các client đã kết nối. Sự trừu tượng này loại bỏ độ phức tạp của việc quản lý kết nối WebSocket thủ công.
Ví dụ sau minh họa một chat hub cơ bản phát tin nhắn đến tất cả client đã kết nối:
using Microsoft.AspNetCore.SignalR;
public class ChatHub : Hub
{
// Called when a client sends a message
public async Task SendMessage(string user, string message)
{
// Broadcast to ALL connected clients
await Clients.All.SendAsync("ReceiveMessage", user, message);
}
// Called automatically when a client connects
public override async Task OnConnectedAsync()
{
await Clients.Caller.SendAsync("Connected", Context.ConnectionId);
await base.OnConnectedAsync();
}
// Called automatically when a client disconnects
public override async Task OnDisconnectedAsync(Exception? exception)
{
// Clean up resources, notify other users, etc.
await base.OnDisconnectedAsync(exception);
}
}Property Clients cung cấp truy cập đến tất cả client đã kết nối thông qua nhiều tùy chọn targeting: All, Caller, Others, Group, và User.
Cấu Hình SignalR trong ASP.NET Core 9
Đăng ký SignalR trong ASP.NET Core 9 tuân theo mô hình middleware chuẩn. Cấu hình dưới đây kích hoạt giao thức JSON và MessagePack với kích thước buffer tùy chỉnh:
var builder = WebApplication.CreateBuilder(args);
// Add SignalR services with configuration
builder.Services.AddSignalR(options =>
{
options.EnableDetailedErrors = builder.Environment.IsDevelopment();
options.MaximumReceiveMessageSize = 64 * 1024; // 64 KB
options.StreamBufferCapacity = 10;
options.KeepAliveInterval = TimeSpan.FromSeconds(15);
options.ClientTimeoutInterval = TimeSpan.FromSeconds(30);
})
.AddJsonProtocol(options =>
{
options.PayloadSerializerOptions.PropertyNamingPolicy = null; // PascalCase
});
var app = builder.Build();
// Map the hub endpoint
app.MapHub<ChatHub>("/chathub");
app.Run();Các thiết lập KeepAliveInterval và ClientTimeoutInterval kiểm soát việc giám sát tình trạng kết nối. Client không phản hồi trong khoảng timeout được coi là đã ngắt kết nối.
Quản Lý Groups để Gửi Tin Nhắn Có Mục Tiêu
Groups cung cấp cơ chế để tổ chức các kết nối và gửi tin nhắn đến tập con của client. Trường hợp sử dụng điển hình liên quan đến chat room, nơi người dùng tham gia các phòng cụ thể và chỉ nhận tin nhắn từ những phòng đó.
public class GroupChatHub : Hub
{
// Add the current connection to a group
public async Task JoinRoom(string roomName)
{
await Groups.AddToGroupAsync(Context.ConnectionId, roomName);
// Notify the group that someone joined
await Clients.Group(roomName).SendAsync(
"UserJoined",
Context.User?.Identity?.Name ?? "Anonymous"
);
}
// Remove the current connection from a group
public async Task LeaveRoom(string roomName)
{
await Groups.RemoveFromGroupAsync(Context.ConnectionId, roomName);
await Clients.Group(roomName).SendAsync(
"UserLeft",
Context.User?.Identity?.Name ?? "Anonymous"
);
}
// Send a message to a specific group only
public async Task SendToRoom(string roomName, string message)
{
await Clients.Group(roomName).SendAsync(
"ReceiveMessage",
Context.User?.Identity?.Name,
message
);
}
}Tư cách thành viên group gắn liền với kết nối, không phải người dùng. Khi người dùng reconnect, họ phải tham gia lại các group. Để duy trì tư cách thành viên group lâu dài, lưu trữ liên kết group trong database và tự động tham gia lại trong OnConnectedAsync.
Groups chỉ tồn tại trong memory và không được lưu trữ sau khi server khởi động lại hoặc trong các tình huống scaled-out không có backplane. Đối với deployment production với nhiều server, sử dụng Redis hoặc Azure SignalR Service làm backplane.
Bảo Mật Kết Nối SignalR với Xác Thực
SignalR tích hợp với xác thực ASP.NET Core. Thuộc tính [Authorize] giới hạn truy cập hub cho người dùng đã xác thực, và Context.User cung cấp truy cập đến claims của người dùng đã xác thực.
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.SignalR;
[Authorize] // Require authentication for the entire hub
public class SecureHub : Hub
{
// Access user claims through Context.User
public async Task SendPrivateMessage(string recipientUserId, string message)
{
var senderName = Context.User?.Identity?.Name
?? throw new HubException("User not authenticated");
// Send to a specific user (all their connections)
await Clients.User(recipientUserId).SendAsync(
"PrivateMessage",
senderName,
message
);
}
[Authorize(Roles = "Admin")] // Role-based authorization on method
public async Task BroadcastAnnouncement(string announcement)
{
await Clients.All.SendAsync("Announcement", announcement);
}
}Đối với kết nối WebSocket, token xác thực phải được truyền qua query string vì WebSocket không hỗ trợ custom headers. Cấu hình client JavaScript để bao gồm token:
import * as signalR from "@microsoft/signalr";
const connection = new signalR.HubConnectionBuilder()
.withUrl("/securehub", {
accessTokenFactory: () => localStorage.getItem("authToken") || ""
})
.withAutomaticReconnect()
.build();
await connection.start();Trên server, cấu hình xác thực JWT để đọc token từ query string:
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options =>
{
options.Events = new JwtBearerEvents
{
OnMessageReceived = context =>
{
// Read token from query string for SignalR
var accessToken = context.Request.Query["access_token"];
var path = context.HttpContext.Request.Path;
if (!string.IsNullOrEmpty(accessToken) &&
path.StartsWithSegments("/securehub"))
{
context.Token = accessToken;
}
return Task.CompletedTask;
}
};
});Sẵn sàng chinh phục phỏng vấn .NET?
Luyện tập với mô phỏng tương tác, flashcards và bài kiểm tra kỹ thuật.
Mở Rộng SignalR với Redis Backplane
Trong môi trường load-balanced, client có thể kết nối đến các instance server khác nhau. Nếu không có backplane, tin nhắn gửi từ một server chỉ đến được client kết nối với server đó. Redis đóng vai trò backplane để đồng bộ hóa tin nhắn trên tất cả các instance server.
builder.Services.AddSignalR()
.AddStackExchangeRedis(options =>
{
options.Configuration = builder.Configuration
.GetConnectionString("Redis");
// Optional: configure Redis-specific options
options.Configuration.ChannelPrefix =
RedisChannel.Literal("MyApp_SignalR_");
});Redis backplane publish tất cả tin nhắn SignalR đến các channel Redis. Mỗi instance server subscribe đến các channel này và gửi tin nhắn đến client cục bộ của nó. Cách tiếp cận này thêm độ trễ tối thiểu (thường 1-2ms) trong khi cho phép horizontal scaling.
Đối với deployment enterprise, Azure SignalR Service cung cấp giải pháp được quản lý xử lý scaling, quản lý kết nối và tính khả dụng tự động.
Strongly-Typed Hubs để An Toàn Compile-Time
Strongly-typed hubs thay thế magic strings bằng các method interface, cho phép xác minh compile-time các lệnh gọi method client:
public interface INotificationClient
{
Task ReceiveNotification(string title, string message, string severity);
Task UpdateProgress(int percentage);
Task TaskCompleted(Guid taskId, bool success);
}
// NotificationHub.cs
public class NotificationHub : Hub<INotificationClient>
{
public async Task NotifyAll(string title, string message)
{
// Compile-time checking - no magic strings
await Clients.All.ReceiveNotification(title, message, "info");
}
public async Task ReportProgress(string groupName, int percentage)
{
// IDE autocomplete works here
await Clients.Group(groupName).UpdateProgress(percentage);
}
}Refactoring trở nên an toàn hơn vì đổi tên method trong interface ngay lập tức hiển thị tất cả các call sites cần cập nhật.
Streaming Dữ Liệu với SignalR Channels
SignalR hỗ trợ streaming cho các tình huống dữ liệu được tạo ra dần dần, như cập nhật tiến độ, log tailing, hoặc dữ liệu cảm biến real-time. Cả streaming server-to-client và client-to-server đều được hỗ trợ.
public class StreamingHub : Hub
{
// Server-to-client streaming with IAsyncEnumerable
public async IAsyncEnumerable<StockPrice> StreamStockPrices(
string[] symbols,
[EnumeratorCancellation] CancellationToken cancellationToken)
{
var random = new Random();
while (!cancellationToken.IsCancellationRequested)
{
foreach (var symbol in symbols)
{
yield return new StockPrice
{
Symbol = symbol,
Price = random.NextDouble() * 1000,
Timestamp = DateTime.UtcNow
};
}
await Task.Delay(1000, cancellationToken);
}
}
// Client-to-server streaming
public async Task UploadStream(IAsyncEnumerable<LogEntry> stream)
{
await foreach (var entry in stream)
{
// Process each log entry as it arrives
await ProcessLogEntry(entry);
}
}
}
public record StockPrice(string Symbol, double Price, DateTime Timestamp);
public record LogEntry(string Level, string Message, DateTime Timestamp);Client JavaScript tiêu thụ stream sử dụng async iterator:
const stream = connection.stream("StreamStockPrices", ["AAPL", "MSFT"]);
stream.subscribe({
next: (price) => console.log(`${price.symbol}: $${price.price}`),
error: (err) => console.error(err),
complete: () => console.log("Stream completed")
});Câu Hỏi Phỏng Vấn SignalR Thường Gặp
Phỏng vấn kỹ thuật thường đề cập đến kiến trúc SignalR và các tình huống thực tế. Các câu hỏi sau thường xuất hiện trong các buổi phỏng vấn .NET:
Giải thích quy trình đàm phán transport: SignalR đầu tiên thử WebSockets, fallback đến Server-Sent Events, sau đó Long Polling. Hiểu rõ khi nào mỗi transport được sử dụng và hạn chế của nó.
H: SignalR xử lý trạng thái kết nối như thế nào khi client tạm thời mất kết nối?
SignalR duy trì trạng thái kết nối trên server trong khoảng thời gian có thể cấu hình. Với automatic reconnection được bật, client cố gắng reconnect sử dụng cùng connection ID. Nếu thành công, kết nối tiếp tục mà không mất tư cách thành viên group. Nếu disconnect vượt quá timeout, kết nối mới được thiết lập và client phải tham gia lại các groups.
H: Sự khác biệt giữa Clients.User() và Clients.Client() là gì?
Clients.User(userId) nhắm đến tất cả kết nối thuộc về một người dùng đã xác thực cụ thể—hữu ích khi một người dùng có nhiều browser tabs mở. Clients.Client(connectionId) nhắm đến một kết nối cụ thể duy nhất. Targeting dựa trên user yêu cầu xác thực và sử dụng IUserIdProvider để ánh xạ kết nối đến user IDs.
H: Làm thế nào để triển khai presence detection (hiển thị ai đang online)?
Theo dõi kết nối trong OnConnectedAsync và OnDisconnectedAsync. Lưu trữ mapping user-to-connection trong concurrent dictionary hoặc distributed cache. Đối với deployment scaled-out, sử dụng shared store như Redis. Broadcast cập nhật presence đến các groups liên quan khi người dùng connect hoặc disconnect.
H: Giải thích mục đích của SignalR backplane.
Backplane đồng bộ hóa tin nhắn trên nhiều server instances trong môi trường load-balanced. Nếu không có nó, tin nhắn gửi từ server A chỉ đến được client kết nối với server A. Redis và Azure SignalR Service là các tùy chọn backplane phổ biến. Backplane publish tin nhắn đến shared channel mà tất cả server subscribe.
Chiến Lược Tối Ưu Hiệu Suất
Hiệu suất SignalR phụ thuộc vào kích thước tin nhắn, tần suất và số lượng kết nối. Tài liệu best practices hiệu suất ASP.NET Core cung cấp benchmark chi tiết.
- Sử dụng giao thức MessagePack để serialization nhị phân—giảm kích thước payload 30-50% so với JSON
- Batch tin nhắn khi có thể thay vì gửi nhiều tin nhắn nhỏ
- Giới hạn kích thước group ở hàng nghìn thành viên; cho khán giả lớn hơn, xem xét mô hình pub/sub
- Bật nén cho payload nặng text:
.AddJsonProtocol().AddHubOptions(o => o.EnableDetailedErrors = false) - Đặt timeout phù hợp để giải phóng resources từ stale connections nhanh chóng
// MessagePack configuration for smaller payloads
builder.Services.AddSignalR()
.AddMessagePackProtocol(options =>
{
options.SerializerOptions =
MessagePackSerializerOptions.Standard
.WithCompression(MessagePackCompression.Lz4Block);
});Kết Luận
- SignalR trừu tượng hóa việc chọn transport (WebSockets, SSE, Long Polling) và cung cấp mô hình lập trình nhất quán cho giao tiếp real-time
- Hubs tập trung quản lý kết nối; strongly-typed hubs thêm an toàn compile-time cho các lệnh gọi method client
- Groups cho phép gửi tin nhắn có mục tiêu đến tập con kết nối—tham gia lại groups sau reconnection
- Xác thực tích hợp với ASP.NET Core identity; truyền token qua query string cho kết nối WebSocket
- Mở rộng horizontal với Redis backplane hoặc Azure SignalR Service để đồng bộ hóa tin nhắn trên tất cả server instances
- Streaming hỗ trợ gửi dữ liệu dần dần với
IAsyncEnumerablecho cả tình huống server-to-client và client-to-server
Bắt đầu luyện tập!
Kiểm tra kiến thức với mô phỏng phỏng vấn và bài kiểm tra kỹ thuật.

Viết bởi
Anthony Fillion-MailletLập trình viên fullstack, người sáng lập SharpSkill
Lập trình viên fullstack hơn 10 năm. Anh điều hành SharpSkill và chịu trách nhiệm về mọi nội dung đăng tại đây.
Cập nhật ngày 12 tháng 8, 2026
Chia sẻ
Bài viết liên quan

ASP.NET Core Minimal APIs 2026: Kiến Trúc, Hiệu Năng và Câu Hỏi Phỏng Vấn
Hướng dẫn toàn diện về ASP.NET Core Minimal APIs với kiến trúc hiện đại, tối ưu hiệu năng Native AOT và các câu hỏi phỏng vấn thường gặp cho developer .NET.

.NET 10 năm 2026: Tính năng mới, Native AOT và câu hỏi phỏng vấn
Tổng hợp tính năng mới của .NET 10 và C# 14 năm 2026: biên dịch Native AOT sẵn sàng cho sản xuất, extension members, từ khóa field, ứng dụng dạng file đơn và câu hỏi phỏng vấn kỹ thuật.

.NET MAUI trong 2026: Phát triển Cross-Platform và Câu hỏi Phỏng vấn
Hướng dẫn .NET MAUI toàn diện về phát triển cross-platform với .NET 10, handler architecture, MVVM, HybridWebView và các câu hỏi phỏng vấn quan trọng năm 2026.