Giao tiếp liên tiến trình với gRPC và Named pipes
.NET hỗ trợ giao tiếp liên tiến trình (IPC - inter-process communication) sử dụng gRPC. Để biết thêm thông tin về cách bắt đầu sử dụng gRPC để giao tiếp giữa các tiến trình, xem Giao tiếp liên tiến trình với gRPC.
Named pipes (ống có tên) là một transport IPC được hỗ trợ trên tất cả các phiên bản Windows. Named pipes tích hợp tốt với bảo mật Windows để kiểm soát quyền truy cập của client vào pipe. Bài viết này trình bày cách cấu hình giao tiếp gRPC qua named pipes.
Yêu cầu tiên quyết
- .NET 8 hoặc mới hơn
- Windows
Cấu hình server
Named pipes được hỗ trợ bởi Kestrel, được cấu hình trong Program.cs:
var builder = WebApplication.CreateBuilder(args);
builder.WebHost.ConfigureKestrel(serverOptions =>
{
serverOptions.ListenNamedPipe("MyPipeName", listenOptions =>
{
listenOptions.Protocols = HttpProtocols.Http2;
});
});Ví dụ trên:
- Cấu hình các endpoint của Kestrel trong
ConfigureKestrel. - Gọi
ListenNamedPipeđể lắng nghe trên một named pipe với tên đã chỉ định. - Tạo một named pipe endpoint không được cấu hình để dùng HTTPS. Để biết thông tin về kích hoạt HTTPS, xem Cấu hình HTTPS endpoint của Kestrel.
Cấu hình PipeSecurity cho Named Pipes
Để kiểm soát người dùng hoặc nhóm nào có thể kết nối, sử dụng lớp NamedPipeTransportOptions. Điều này cho phép chỉ định một đối tượng PipeSecurity tùy chỉnh.
Ví dụ:
using Microsoft.AspNetCore.Server.Kestrel.Transport.NamedPipes;
using System.IO.Pipes;
using System.Security.AccessControl;
var builder = WebApplication.CreateBuilder(args);
builder.WebHost.ConfigureKestrel(serverOptions =>
{
serverOptions.ListenNamedPipe("MyPipeName", listenOptions =>
{
listenOptions.Protocols = HttpProtocols.Http2;
// Cấu hình PipeSecurity
listenOptions.UseNamedPipes(options =>
{
var pipeSecurity = new PipeSecurity();
// Cấp quyền đọc/ghi cho nhóm Users
pipeSecurity.AddAccessRule(new PipeAccessRule(
"Users",
PipeAccessRights.ReadWrite,
AccessControlType.Allow));
// Thêm các quy tắc bổ sung nếu cần
options.PipeSecurity = pipeSecurity;
});
});
});Ví dụ trên:
- Sử dụng
UseNamedPipesđể truy cập và cấu hìnhNamedPipeTransportOptions. - Đặt thuộc tính
PipeSecurityđể kiểm soát người dùng hoặc nhóm nào có thể kết nối vào named pipe. - Cấp quyền đọc/ghi cho nhóm
Users. Có thể thêm các quy tắc bảo mật bổ sung tùy theo yêu cầu.
Tùy chỉnh Kestrel named pipe endpoints
Hỗ trợ named pipe của Kestrel cho phép tùy chỉnh nâng cao, cho phép bạn cấu hình các thiết lập bảo mật khác nhau cho mỗi endpoint sử dụng tùy chọn CreateNamedPipeServerStream. Cách tiếp cận này lý tưởng cho các tình huống mà nhiều named pipe endpoint cần kiểm soát truy cập riêng biệt. Khả năng tùy chỉnh pipe theo từng endpoint có sẵn từ .NET 9.
Một ví dụ hữu ích là một ứng dụng Kestrel cần hai pipe endpoint với bảo mật truy cập khác nhau. Tùy chọn CreateNamedPipeServerStream có thể được sử dụng để tạo pipes với các thiết lập bảo mật tùy chỉnh, tùy theo tên pipe.
var builder = WebApplication.CreateBuilder();
builder.WebHost.ConfigureKestrel(options =>
{
options.ListenNamedPipe("pipe1");
options.ListenNamedPipe("pipe2");
});
builder.WebHost.UseNamedPipes(options =>
{
options.CreateNamedPipeServerStream = (context) =>
{
var pipeSecurity = CreatePipeSecurity(context.NamedPipeEndpoint.PipeName);
return NamedPipeServerStreamAcl.Create(context.NamedPipeEndpoint.PipeName, PipeDirection.InOut,
NamedPipeServerStream.MaxAllowedServerInstances, PipeTransmissionMode.Byte,
context.PipeOptions, inBufferSize: 0, outBufferSize: 0, pipeSecurity);
};
});Cấu hình client
GrpcChannel hỗ trợ thực hiện các gRPC call qua các transport tùy chỉnh. Khi một channel được tạo, nó có thể được cấu hình với một SocketsHttpHandler có ConnectCallback tùy chỉnh. Callback này cho phép client thực hiện kết nối qua các transport tùy chỉnh và sau đó gửi các HTTP request qua transport đó.
Lưu ý: Một số tính năng kết nối của GrpcChannel, như client side load balancing (cân bằng tải phía client) và channel status (trạng thái channel), không thể sử dụng cùng với named pipes.
Ví dụ về named pipes connection factory (nhà máy kết nối):
public class NamedPipesConnectionFactory
{
private readonly string pipeName;
public NamedPipesConnectionFactory(string pipeName)
{
this.pipeName = pipeName;
}
public async ValueTask<Stream> ConnectAsync(SocketsHttpConnectionContext _,
CancellationToken cancellationToken = default)
{
var clientStream = new NamedPipeClientStream(
serverName: ".",
pipeName: this.pipeName,
direction: PipeDirection.InOut,
options: PipeOptions.WriteThrough | PipeOptions.Asynchronous,
impersonationLevel: TokenImpersonationLevel.Anonymous);
try
{
await clientStream.ConnectAsync(cancellationToken).ConfigureAwait(false);
return clientStream;
}
catch
{
clientStream.Dispose();
throw;
}
}
}Sử dụng custom connection factory để tạo channel:
public static GrpcChannel CreateChannel()
{
var connectionFactory = new NamedPipesConnectionFactory("MyPipeName");
var socketsHttpHandler = new SocketsHttpHandler
{
ConnectCallback = connectionFactory.ConnectAsync
};
return GrpcChannel.ForAddress("http://localhost", new GrpcChannelOptions
{
HttpHandler = socketsHttpHandler
});
}Các channel được tạo bằng code trên sẽ gửi các gRPC call qua named pipes.