Nguon: Microsoft Learn · .NET 8.0

Giao tiếp liên tiến trình với gRPC và Named pipes

Nguồn: Inter-process communication with gRPC and Named pipes

Bởi James Newton-King

.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

Cấu hình server

Named pipes được hỗ trợ bởi Kestrel, được cấu hình trong Program.cs:

csharp
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 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ụ:

csharp
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:

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.

csharp
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 SocketsHttpHandlerConnectCallback 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):

csharp
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:

csharp
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.