ServicePointManager.ReusePort 和 SO_REUSE_UNICASTPORT 如何缓解临时端口耗尽?
How does ServicePointManager.ReusePort and SO_REUSE_UNICASTPORT alleviate ephemeral port exhaustion?
Windows 10 和 Windows Server 2016 引入了 SO_REUSE_UNICASTPORT
套接字选项。从 4.6 版开始,它通过 ServicePointManager.ReusePort
静态 属性 在 .NET 中可用。在非常高的负载期间(通过 HttpClient
的许多并发传出请求),我在 .NET 应用程序上遇到短暂的端口耗尽问题,我正在考虑使用此选项来处理它。我知道处理该问题的其他方法(例如编辑 Windows 注册表以修改临时端口的最大数量或缩短 TIME_WAIT),但我想完全采用此解决方案也是。
ServicePointManager.ReusePort
的文档非常少:
Setting this property value to true causes all outbound TCP connections from HttpWebRequest to use the native socket option SO_REUSE_UNICASTPORT on the socket. This causes the underlying outgoing ports to be shared. This is useful for scenarios where a large number of outgoing connections are made in a short time, and the app risks running out of ports.
查看 SO_REUSE_UNICASTPORT
的文档没有提供任何额外的见解:
When set, allow ephemeral port reuse for Winsock API connection functions which require an explicit bind, such as ConnectEx. Note that connection functions with an implicit bind (such as connect without an explicit bind) have this option set by default. Use this option instead of SO_PORT_SCALABILITY on platforms where both are available.
我无法在网络上找到任何关于此 "ephemeral port reuse" 是如何实现的、它在技术层面上的工作原理以及它如何降低临时端口耗尽风险的任何解释。我可以期待多少改进?使用此功能,我如何计算我的应用程序的新限制?启用此功能有什么缺点吗?
这一切都笼罩在神秘之中,如果有人能解释这个新机制及其含义,我会很高兴。
TCP 连接由(本地IP、本地端口、远程IP、远程端口)唯一标识。这意味着完全有可能对连接到不同远程端点的多个套接字使用相同的(本地 IP、本地端口)对。假设你想向 "site1.com" 和 "site2.com" 发出 http 请求。您正在使用带有以下代码的套接字:
using (var socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp)) {
socket.Bind(new IPEndPoint(IPAddress.Parse("some local ip"), 45455));
socket.Connect(server, port);
socket.Send(someBytes);
// ...
}
因此您将套接字绑定到端口为 45455 的特定本地端点。如果您现在尝试同时向 "site1.com" 和 "site2.com" 发出请求,您将得到 "an address already in use" 异常.
但是,如果您在绑定之前添加 ReuseAddress
选项(请注意,这不是您的问题所涉及的选项):
socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
您将能够将套接字绑定到同一本地(ip、端口),并且您将在 netstat 中看到两个已建立的连接。
以上所有内容表明,理论上没有什么可以阻止人们重复使用临时端口来建立与不同 远程端点的多个连接。但是,当您绑定到临时端口 (0
) 时 - 尚不知道您要连接到哪个远程端点。假设所有临时端口都在使用中并且您绑定到 0。OS 为您提供一些端口以供在绑定阶段重用,有一个套接字使用此端口已经连接到 "site1.com"。您也尝试连接到 "site1.com" 但失败了(因为标识 tcp 连接的所有 4 个值对于两个套接字都是相同的)。
SO_REUSE_UNICASTPORT
的作用是在绑定到 0 时延迟选择临时端口,直到实际连接阶段(例如 Connect()
调用)。在此阶段(与绑定不同),您已经知道要连接到的本地 ip、远程 ip、远程端口,并且需要选择临时端口。假设所有端口都在使用中。现在,您可以选择连接到 不同 远程端点(与当前套接字尝试连接的不同).
例如,您可以在 MS support article:
处确认这一点
SO_REUSE_UNICASTPORT
For a connection scenario to be implemented, the socket option must be
set before binding a socket. This option instructs the system to
postpone port allocation until connection time when the 4-tuple
(quadruple) for the connection is known.
请注意,SO_REUSE_UNICASTPORT
仅对显式绑定有效(如您的问题引用中所述,但仍值得重复)。如果您隐式绑定(例如当您 Connect()
没有绑定时) - 默认情况下已经设置了此选项(当然支持)。
关于这对您的特定应用程序有何影响。首先从上面应该清楚,如果您的应用程序向同一个远程端点(例如对同一个 http 服务器)发出大量请求 - 此选项将无效。如果您向不同的端点发出大量请求 - 它应该有助于防止端口耗尽。我猜 ServicePointManager.ReusePort
本身的效果仍然取决于 HttpClient
如何在内部使用套接字。如果它只是 Connect()
而没有显式绑定 - 默认情况下应该启用此选项(在支持的系统上),因此将 ServicePointManager.ReusePort
设置为 true
不会有额外的效果,否则会有。由于您不知道(也不应依赖)其内部实现,因此值得在您的特定场景中启用 ServicePointManager.ReusePort
。
您还可以使用此选项 on\off 执行测试,方法是将临时端口的范围(使用 netsh int ipv4 set dynamicport tcp
之类的命令)限制在一些小范围内,然后看看效果如何。
Windows 10 和 Windows Server 2016 引入了 SO_REUSE_UNICASTPORT
套接字选项。从 4.6 版开始,它通过 ServicePointManager.ReusePort
静态 属性 在 .NET 中可用。在非常高的负载期间(通过 HttpClient
的许多并发传出请求),我在 .NET 应用程序上遇到短暂的端口耗尽问题,我正在考虑使用此选项来处理它。我知道处理该问题的其他方法(例如编辑 Windows 注册表以修改临时端口的最大数量或缩短 TIME_WAIT),但我想完全采用此解决方案也是。
ServicePointManager.ReusePort
的文档非常少:
Setting this property value to true causes all outbound TCP connections from HttpWebRequest to use the native socket option SO_REUSE_UNICASTPORT on the socket. This causes the underlying outgoing ports to be shared. This is useful for scenarios where a large number of outgoing connections are made in a short time, and the app risks running out of ports.
查看 SO_REUSE_UNICASTPORT
的文档没有提供任何额外的见解:
When set, allow ephemeral port reuse for Winsock API connection functions which require an explicit bind, such as ConnectEx. Note that connection functions with an implicit bind (such as connect without an explicit bind) have this option set by default. Use this option instead of SO_PORT_SCALABILITY on platforms where both are available.
我无法在网络上找到任何关于此 "ephemeral port reuse" 是如何实现的、它在技术层面上的工作原理以及它如何降低临时端口耗尽风险的任何解释。我可以期待多少改进?使用此功能,我如何计算我的应用程序的新限制?启用此功能有什么缺点吗?
这一切都笼罩在神秘之中,如果有人能解释这个新机制及其含义,我会很高兴。
TCP 连接由(本地IP、本地端口、远程IP、远程端口)唯一标识。这意味着完全有可能对连接到不同远程端点的多个套接字使用相同的(本地 IP、本地端口)对。假设你想向 "site1.com" 和 "site2.com" 发出 http 请求。您正在使用带有以下代码的套接字:
using (var socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp)) {
socket.Bind(new IPEndPoint(IPAddress.Parse("some local ip"), 45455));
socket.Connect(server, port);
socket.Send(someBytes);
// ...
}
因此您将套接字绑定到端口为 45455 的特定本地端点。如果您现在尝试同时向 "site1.com" 和 "site2.com" 发出请求,您将得到 "an address already in use" 异常.
但是,如果您在绑定之前添加 ReuseAddress
选项(请注意,这不是您的问题所涉及的选项):
socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
您将能够将套接字绑定到同一本地(ip、端口),并且您将在 netstat 中看到两个已建立的连接。
以上所有内容表明,理论上没有什么可以阻止人们重复使用临时端口来建立与不同 远程端点的多个连接。但是,当您绑定到临时端口 (0
) 时 - 尚不知道您要连接到哪个远程端点。假设所有临时端口都在使用中并且您绑定到 0。OS 为您提供一些端口以供在绑定阶段重用,有一个套接字使用此端口已经连接到 "site1.com"。您也尝试连接到 "site1.com" 但失败了(因为标识 tcp 连接的所有 4 个值对于两个套接字都是相同的)。
SO_REUSE_UNICASTPORT
的作用是在绑定到 0 时延迟选择临时端口,直到实际连接阶段(例如 Connect()
调用)。在此阶段(与绑定不同),您已经知道要连接到的本地 ip、远程 ip、远程端口,并且需要选择临时端口。假设所有端口都在使用中。现在,您可以选择连接到 不同 远程端点(与当前套接字尝试连接的不同).
例如,您可以在 MS support article:
处确认这一点SO_REUSE_UNICASTPORT
For a connection scenario to be implemented, the socket option must be set before binding a socket. This option instructs the system to postpone port allocation until connection time when the 4-tuple (quadruple) for the connection is known.
请注意,SO_REUSE_UNICASTPORT
仅对显式绑定有效(如您的问题引用中所述,但仍值得重复)。如果您隐式绑定(例如当您 Connect()
没有绑定时) - 默认情况下已经设置了此选项(当然支持)。
关于这对您的特定应用程序有何影响。首先从上面应该清楚,如果您的应用程序向同一个远程端点(例如对同一个 http 服务器)发出大量请求 - 此选项将无效。如果您向不同的端点发出大量请求 - 它应该有助于防止端口耗尽。我猜 ServicePointManager.ReusePort
本身的效果仍然取决于 HttpClient
如何在内部使用套接字。如果它只是 Connect()
而没有显式绑定 - 默认情况下应该启用此选项(在支持的系统上),因此将 ServicePointManager.ReusePort
设置为 true
不会有额外的效果,否则会有。由于您不知道(也不应依赖)其内部实现,因此值得在您的特定场景中启用 ServicePointManager.ReusePort
。
您还可以使用此选项 on\off 执行测试,方法是将临时端口的范围(使用 netsh int ipv4 set dynamicport tcp
之类的命令)限制在一些小范围内,然后看看效果如何。