Net Core gRPC - 08. 客户端工厂集成与进程间通信

Net Core gRPC 系列

1. 什么是客户端工厂集成

gRPC 与 HttpClientFactory 的集成提供了一种创建 gRPC 客户端的集中方式。 它可用作配置独立 gRPC 客户端实例的替代方法。 Grpc.Net.ClientFactory NuGet 包中提供了工厂集成。

工厂具有以下优势:

提供了用于配置逻辑 gRPC 客户端实例的中心位置
可管理基础 HttpClientMessageHandler 的生存期
ASP.NET Core gRPC 服务中自动传播截止时间和取消

1
2
3
4
services.AddGrpcClient<Greeter.GreeterClient>(o =>
{
o.Address = new Uri("https://localhost:5001");
});

2. 进程间通信

不同计算机上的客户端和服务器之间的 gRPC 调用通常通过 TCP 套接字发送。 TCP 是跨网络或 Internet 进行通信的绝佳选择。 但是,当客户端和服务器位于同一台计算机上时,IPC 传输可以提供性能优势,更好地与 OS 功能集成。

.NET 支持多个 IPC 传输:

Unix 域套接字 (UDS) 是一种广泛受支持的 IPC 技术。 UDS 是生成跨平台应用的最佳选择,可在 Linux、macOS 和 Windows 10/Windows Server 2019 或更高版本上使用。
所有版本的 Windows 都支持命名管道。 命名管道与 Windows 安全性良好集成,可用于控制客户端对管道的访问。
通过在应用启动时实现 IConnectionListenerFactory 和注册实现进行的其他 IPC 传输。

❗ 备注

ASP.NET Core 中对命名管道的内置支持需要 .NET 8 或更高版本。 .NET 8 目前为预览版,将于 2023 年底发布。

Unix进程间通信
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
public class UnixDomainSocketsConnectionFactory
{
private readonly EndPoint endPoint;

public UnixDomainSocketsConnectionFactory(EndPoint endPoint)
{
this.endPoint = endPoint;
}

public async ValueTask<Stream> ConnectAsync(SocketsHttpConnectionContext _,
CancellationToken cancellationToken = default)
{
var socket = new Socket(AddressFamily.Unix, SocketType.Stream, ProtocolType.Unspecified);

try
{
await socket.ConnectAsync(this.endPoint, cancellationToken).ConfigureAwait(false);
return new NetworkStream(socket, true);
}
catch
{
socket.Dispose();
throw;
}
}
}
windows 进程间通信
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
/// <summary>
/// Grpc IPC进程内通信 只支持 .NET 8以上的版本(含)
/// </summary>
public class NamedPipesConnectionFactory
{
private readonly string _pipeName;

public NamedPipesConnectionFactory(string pipeName)
{
_pipeName = pipeName;
}

public async ValueTask<Stream> ConnectAsync(SocketsHttpConnectionContext _,
CancellationToken cancellationToken = default)
{
var clientStream = new NamedPipeClientStream(
serverName: ".",
pipeName: _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;
}
}
}

3. 使用策略模式创建Grpc channel

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
public interface IColaGrpc
{
GrpcChannel CreateChannel(string grpcUrl, string? pipeNameOrSocketPath=null);
}

public class ColaWindowsGrpc : IColaGrpc
{
public GrpcChannel CreateChannel(string grpcUrl, string? pipeNameOrSocketPath=null)
{
// windows IPC 需要8版本支出
// SystemHelper.NetCoreVersion 实现
// public static Version? NetCoreVersion => typeof(object).GetTypeInfo().Assembly.GetName().Version;
if (SystemHelper.NetCoreVersion!.Major > 8)
{
var connectionFactory = new NamedPipesConnectionFactory(pipeNameOrSocketPath!);
var socketsHttpHandler = new SocketsHttpHandler
{
ConnectCallback = connectionFactory.ConnectAsync
};
return GrpcChannel.ForAddress(grpcUrl, new GrpcChannelOptions
{
HttpHandler = socketsHttpHandler
});
}
return GrpcChannel.ForAddress(grpcUrl);
}
}
public class ColaUnixGrpc : IColaGrpc
{
public GrpcChannel CreateChannel(string grpcUrl, string? pipeNameOrSocketPath=null)
{
var udsEndPoint = new UnixDomainSocketEndPoint(pipeNameOrSocketPath!);
var connectionFactory = new UnixDomainSocketsConnectionFactory(udsEndPoint);
var socketsHttpHandler = new SocketsHttpHandler
{
ConnectCallback = connectionFactory.ConnectAsync
};
return GrpcChannel.ForAddress(grpcUrl, new GrpcChannelOptions
{
HttpHandler = socketsHttpHandler
});
}
}
public class ColaGrpcHelper
{
private readonly IColaGrpc _colaGrpc;

public ColaGrpcHelper(IColaGrpc colaGrpc)
{
_colaGrpc = colaGrpc;
}

public GrpcChannel CreateChannel(string grpcUrl, string? pipeNameOrSocketPath=null)
{
return _colaGrpc.CreateChannel(grpcUrl, pipeNameOrSocketPath);
}
}

创建channel

1
var channel = new ColaGrpcHelper(new ColaWindowsGrpc()).CreateChannel("https://localhost:5005");

完整代码可以在 GitHub

WebApplication1Test 整合了gRPC,ConsoleApp1Test有具体调用示例