[英]Best practice for sending to connected clients
我正在尝试设计一个 SwiftNIO 服务器,其中多个客户端(如 2 个或 3 个)可以连接到服务器,并且在连接后,它们都可以从服务器接收信息。
为此,我创建了一个ServerHandler
class,它被共享并添加到连接的客户端的每个管道中。
let group = MultiThreadedEventLoopGroup(numberOfThreads: 2)
let handler = ServerHandler()
let bootstrap = ServerBootstrap(group: group)
.serverChannelOption(ChannelOptions.backlog, value: 2)
.serverChannelOption(ChannelOptions.socketOption(.so_reuseaddr), value: 1)
.childChannelInitializer { $0.pipeline.addHandler(handler) }
.childChannelOption(ChannelOptions.socketOption(.so_reuseaddr), value: 1)
上面的代码灵感来自https://github.com/apple/swift-nio/blob/main/Sources/NIOChatServer/main.swift
在ServerHandler
class 中,每当有新客户端连接时,该通道就会添加到数组中。 然后,当我准备好向所有客户端发送数据时,我只需遍历ServerHandler
中的通道,然后调用writeAndFlush
。
这似乎工作得很好,但有几件事我担心:
Channel.write
似乎什么也没做? 如果我在服务器中使用Channel.write
而不是writeAndFlush
,我的客户端将无法接收任何数据。 如果这些问题很愚蠢,我深表歉意,我最近才开始使用SwiftNIO
和一般网络。
如果有人能给我一些见解,那就太棒了。
你的问题一点都不傻!
是的,共享ChannelHandler
可能算作“不推荐”。 但不是因为它不起作用,更多的是它不寻常,可能不是其他 NIO 程序员所期望的。 但是,如果您对此感到满意,那很好。 如果您足够高性能以至于您担心每个Channel
的确切分配数量,那么您可以通过共享处理程序来节省一些。 但我真的不会过早地优化。
如果您不想共享处理程序,那么您可以使用多个处理程序来共享对某种协调器 object 的引用。 不要误会我的意思,它实际上仍然是同一件事:跨多个网络连接的一个共享引用。 唯一真正的区别是测试可能更容易一些,并且对于其他 NIO 程序员来说可能感觉更自然。 (在任何情况下,都要小心确保所有这些Channel
都在同一个EventLoop
上或使用外部同步(比如说一个锁,从性能的角度来看这可能不是理想的)。
write
只是将一些要写入的数据排入队列。 flush
使 SwiftNIO 尝试发送所有先前写入的数据。 writeAndFlush
只需调用write
然后flush
。
为什么 NIO 完全区分write
和flush
? 在高性能网络应用程序中,最大的开销可能是系统调用开销。 为了通过 TCP 发送数据,SwiftNIO 必须进行系统调用( write
、 writev
、 send
......)。
如果您忽略write
和flush
并始终使用writeAndFlush
,任何 SwiftNIO 程序都可以工作。 但是,如果网络跟上,那么每次writeAndFlush
调用都会花费你一个系统调用。 然而,在许多情况下,使用 SwiftNIO 的库/应用程序已经知道它想要将多位数据排入队列以通过网络发送。 在那种情况下,连续说三个writeAndFlush
会很浪费。 如果累积三位数据然后使用“向量写入”(例如writev
系统调用)在一个系统调用中发送它们会更好。 如果你确实说write
, write
, write
, flush
,这正是 SwiftNIO 会做的事情。 因此,三个写入都将使用一个writev
系统调用发送。 SwiftNIO 将简单地获取指向数据位的三个指针并将它们交给 kernel,然后它会尝试通过网络发送它们。
你可以更进一步。 假设您是一台高性能服务器,并且您想要响应大量传入请求。 您将通过channelRead
从客户端获取您的请求。 如果您现在能够同步回复,您可以只为它们write
响应(这将使它们排队)。 一旦获得channelReadComplete
(这标志着“读取突发”的结束),您就可以flush
。 这将允许您仅使用一个writev
系统调用在一次读取突发中响应尽可能多的请求。 在某些情况下,这可能是非常重要的优化。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.