写点什么

苹果公司开源的 Swift 版 Netty:SwiftNIO

2018 年 4 月 02 日

SwfitNIO 是由苹果公司开源的一款基于事件驱动的跨平台网络应用程序开发框架,其目标是帮助开发者快速开发出高性能且易于维护的服务器端和客户端应用协议。

它有点类似 Netty,但开发语言使用的是 Swift。

基本概念

SwfitNIO 实际上是一个底层工具,用于开发高性能的网络应用程序,作为“每连接一个线程”的替代方案。

为了提升性能,SwfitNIO 使用了非阻塞 IO,这从它的名字就可以看出来。非阻塞 IO 与阻塞式 IO 非常不一样,因为不管是往网络上发送数据还是从网络上接收数据,应用程序都无需等待,系统内核会在有可操作的 IO 时通知 SwfitNIO。

SwfitNIO 并不会提供类似 Web 框架那样的解决方案,而是致力于为上层框架提供底层的构建块。在开发 Web 应用程序时,大部分开发者不会直接使用 SwfitNIO,他们会从 Swift 生态系统众多的 Web 框架中选择一个。不过,这些框架中的大部分都使用了 SwfitNIO。

受支持的平台

SwfitNIO 的目标是支持所有可以运行 Swift 的平台。目前,SwfitNIO 可以在 macOS 和 Linux 上运行,包括:

  • Ubuntu 14.04+
  • macOS 10.12+

基本架构

SwfitNIO 包含了几种基本构建块,所有的 SwfitNIO 应用程序都是由这几种组件组成的。

  • EventLoopGroup
  • EventLoop
  • Channel
  • ChannelHandler
  • Bootstrap
  • ByteBuffer
  • EventLoopPromise 和 EventLoopFuture

EventLoopPromise 和 EventLoopFuture

EventLoop 是 SwfitNIO 最基本的 IO 原语,它等待事件的发生,在发生事件时触发某种回调操作。在大部分 SwfitNIO 应用程序中,EventLoop 对象的数量并不多,通常每个 CPU 核数对应一到两个 EventLoop 对象。一般来说,EventLoop 会在应用程序的整个生命周期中存在,进行无限的事件分发。

EventLoop 可以组合成 EventLoopGroup,EventLoopGroup 提供了一种机制用于在各个 EventLoop 间分发工作负载。例如,服务器在监听外部连接时,用于监听连接的 socket 会被注册到一个 EventLoop 上。但我们不希望这个 EventLoop 承担所有的连接负载,那么就可以通过 EventLoopGroup 在多个 EventLoop 间分摊连接负载。

目前,SwiftNIO 提供了一个 EventLoopGroup 实现(MultiThreadedEventLoopGroup)和两个 EventLoop 实现(SelectableEventLoop 和 EmbeddedEventLoop)。

MultiThreadedEventLoopGroup 会创建多个线程(使用 POSIX 的 pthreads 库),并为每个线程分配一个 SelectableEventLoop 对象。

SelectableEventLoop 使用选择器(基于 kqueue 或 epoll)来管理来自文件和网络 IO 事件。EmbeddedEventLoop 是一个空的 EventLoop,什么事也不做,主要用于测试。

Channels、ChannelHandler、ChannelPipeline 和 ChannelHandlerContext

尽管 EventLoop 非常重要,但大部分开发者并不会与它有太多的交互,最多就是用它创建 EventLoopPromise 和调度作业。开发者经常用到的是 Channel 和 ChannelHandler。

每个文件描述符对应一个 Channel,Channel 负责管理文件描述符的生命周期,并处理发生在文件描述符上的事件:每当 EventLoop 检测到一个与相应的文件描述符相关的事件,就会通知 Channel。

ChannelPipeline 由一系列 ChannelHandler 组成,ChannelHandler 负责按顺序处理 Channel 中的事件。ChannelPipeline 就像数据处理管道一样,所以才有了这个名字。

ChannelHandler 要么是 Inbound,要么是 Outbound,要么两者兼有。Inbound 的 ChannelHandler 负责处理“inbound”事件,例如从 socket 读取数据、关闭 socket 或者其他由远程发起的事件。Outbound 的 ChannelHandler 负责处理“outbound”事件,例如写数据、发起连接以及关闭本地 socket。

ChannelHandler 按照一定顺序处理事件,例如,读取事件从管道的前面传到后面,而写入事件则从管道的后面传到前面。每个 ChannelHandler 都会在处理完一个事件后生成一个新的事件给下一个 ChannelHandler。

ChannelHandler 是高度可重用的组件,所以尽可能设计得轻量级,每个 ChannelHandler 只处理一种数据转换,这样就可以灵活组合各种 ChannelHandler,提升代码的可重用性和封装性。

我们可以通过 ChannelHandlerContext 来跟踪 ChannelHandler 在 ChannelPipeline 中的位置。ChannelHandlerContext 包含了当前 ChannelHandler 到上一个和下一个 ChannelHandler 的引用,因此,在任何时候,只要 ChannelHandler 还在管道当中,就能触发新事件。

SwiftNIO 内置了多种 ChannelHandler,包括 HTTP 解析器。另外,SwiftNIO 还提供了一些 Channel 实现,比如 ServerSocketChannel(用于接收连接)、SocketChannel(用于 TCP 连接)、DatagramChannel(用于 UDP socket)和 EmbeddedChannel(用于测试)。

Bootstrap

SwiftNIO 提供了一些 Bootstrap 对象,用于简化 Channel 的创建。有些 Bootstrap 对象还提供了其他的一些功能,比如支持 Happy Eyeballs。

目前 SwiftNIO 提供了三种 Bootstrap:ServerBootstrap(用于监听 Channel),ClientBootstrap(用于 TCP Channel)和 DatagramBootstrap(用于 UDP Channel)。

ByteBuffer

SwiftNIO 提供了 ByteBuffer,一种快速的 Copy-On-Write 字节缓冲器,是大部分 SwiftNIO 应用程序的关键构建块。

ByteBuffer 提供了很多有用的特性以及一些“钩子”,通过这些钩子,我们可以在“unsafe”的模式下使用 ByteBuffer。这种方式可以获得更好的性能,代价是应用程序有可能出现内存问题。在一般情况下,还是建议在安全模式下使用 ByteBuffer。

EventLoopPromise 和 EventLoopFuture

并发代码和同步代码之间最主要的区别在于并非所有的动作都能够立即完成。例如,在向一个 Channel 写入数据时,EventLoop 有可能不会立即将数据冲刷到网络上。为此,SwiftNIO 提供了 EventLoopPromise和 EventLoopFuture,用于管理异步操作。

EventLoopFuture实际上是一个容器,用于存放函数在未来某个时刻的返回值。每个 EventLoopFuture对象都有一个对应的 EventLoopPromise,用于存放实际的结果。只要 EventLoopPromise 执行成功,EventLoopFuture 也就完成了。

通过轮询的方式检查 EventLoopFuture 是否完成是一种非常低效的方式,所以 EventLoopFuture 被设计成可以接收回调函数。也就是说,在有结果的时候回调函数会被执行。

EventLoopFuture负责处理调度工作,确保回调函数是在最初创建 EventLoopPromise 的那个 EventLoop 上执行,所以就没有必要再针对回调函数做任何同步操作。

SwiftNIO 的设计哲学

SwiftNIO 的目标是要成为强大的网络应用程序开发框架,但并不想为所有的层次抽象提供完美的解决方案。SwiftNIO 主要专注在基本的 IO 原语和底层的协议实现上,将其他层次的抽象留给广大的社区去构建。SwiftNIO 将成为服务器端应用程序的构建块,但不一定就是应用程序直接拿来使用的框架。

对性能有很高要求的应用程序可能会直接使用 SwiftNIO,减少上层抽象所带来的开销。SwiftNIO 可以帮助这些应用程序在提升性能的同时降低维护成本。SwiftNIO 还为某些场景提供了有用的抽象,高性能的网络服务器可以直接使用这些抽象。

SwiftNIO 的核心仓库提供了一些非常重要的协议实现,比如 HTTP。不过,我们认为,大部分协议的实现应该要与底层的网络栈分开,因为它们的发布节奏是很不一样的。为此,我们鼓励社区自己去实现和维护他们的协议实现。实际上,SwiftNIO 提供的一些协议实现最初就是由社区开发的,比如 TLS 和 HTTP/2。

有用的协议文档

下面的项目提供了非常有用的协议实现文档:

文档

API 文档( https://apple.github.io/swift-nio/docs/current/NIO/index.html )。

运行示例

目前有一些例子,演示了如何使用 SwiftNIO。

开始使用 SwiftNIO

SwiftNIO 使用 SwiftPM 作为构建工具,要想在项目中使用 SwiftNIO,只需要在 Package.swift 中加入依赖:

复制代码
dependencies: [
  .package(url: "https://github.com/apple/swift-nio.git", from: "1.0.0")
]

然后将相应的 SwiftNIO 模块加入到依赖列表当中即可。

可以将 SwiftNIO 代码库复制到本地,然后使用 SwiftPM 来构建它。例如,通过以下的命令来编译和运行 Echo 服务器:

复制代码
swift build
swift test
swift run NIOEchoServer

然后通过控制台验证服务器是否运行正常:

echo "Hello SwiftNIO" | nc localhost 9999如果一切正常,就会看到控制台上打印出来的消息。

另外,还可以通过 docker-compose 来构建和运行示例代码。

首先要确保在本地安装好了 Docker,然后切换到 docker 目录,并运行下面的命令:

docker-compose up test这个命令将创建一个包含 Swift 4.0 的基础镜像,并编译 SwiftNIO 以及运行测试用例。

docker-compose up echo这个命令将创建一个基础镜像,编译 SwiftNIO,并在 localhost:9999 上运行 NIOEchoServer。可以通过echo Hellp SwiftNIO | nc localhost 9999来测试它。

docker-compose up http这个命令将创建一个基础镜像,编译 SwiftNIO,并在 localhost:8888 上运行 NIOHTTP1Server。可以通过curl http://localhost:8888 来测试它。

原文链接 https://github.com/apple/swift-nio

感谢覃云对本文的审校。

2018 年 4 月 02 日 18:143563
用户头像

发布了 731 篇内容, 共 361.0 次阅读, 收获喜欢 1832 次。

关注

评论

发布
暂无评论
发现更多内容

一款高仿 Eyepetizer | 开眼短视频的 MVVM 开源项目

vipyinzhiwei

android kotlin 短视频 eyepetizer 开眼

架构师训练营高可用作业

qihuajun

刘华:弹性便是一切

刘华Kenneth

架构 DevOps 敏捷 弹性

LeetCode题解:25. K 个一组翻转链表,迭代,JavaScript,详细注释

Lee Chen

LeetCode 前端进阶训练营

透过兴趣爱好看本质

escray

学习 面试现场

JavaScript基础语法

Java

第11周作业

小胖子

我在项目中不可或缺么?

escray

学习 面试 面试现场

再谈引入YAPI接口平台的好处

Man

DevOps YAPI API接口管理

MySQL备份与恢复场景示例

Simon

MySQL

【数据结构与算法】力扣实战之移动零、盛最多的水、爬楼梯

三钻

算法 前端 LeetCode 数据结构与算法

都2020了,你的APP还不能运行小程序?

fino星君

小程序生态 私有小程序技术

Java 约束注解

HeGuang

我能讲明白哪些技术?

escray

学习 面试 沟通 面试现场

博文翻译丨提高编程技能的关键(The Key To Accelerating Your Coding Skills 中文翻译)

Liuchengz.

学习 程序员 翻译

MySQL DDL详情揭露

Simon

MySQL

面试28k职位,老乡面试官从HashCode到HashMap给我讲了一下午!「回家赶忙整理出1.6万字的面试材料」

小傅哥

数据结构 hashmap 面试题 面试官 红黑树

我的缺点就是做事太认真

escray

学习 面试 面试现场

oeasy教您玩转linux010102查看发行版

o

国产数据库的经济民族

郭华

数据库 商业

我,一个当代普通大学生的自述

有梦的咸鱼

个人成长 大学生日常 个人感悟 讨论写作

如何提升系统可用性

码猿外

可用性 持续交付 工程能力 团队文化

学会反射后,我被录取了(干货)

cxuan

Java 后端 反射

乘风破浪,Spring Boot入门

Simon郎

Spring Boot

看门狗 | 分布式锁架构设计方案-02

高翔龙

redis 分布式锁 Jedis RedLock

MySQL系列(二):MySQL是怎么处理并发操作的?

z小赵

MySQL 数据库 事务

Golang领域模型-六边形架构

奔奔奔跑

golang 领域驱动设计 微服务拆分 架构设计

Flink中的数据传输-5

小知识点

scala 大数据 flink

架构师训练营高可用学习总结

qihuajun

PySpark RDD 基础运算和操作总结

是老郭啊

spark pyspark RDD

看门狗 | 分布式锁架构设计方案-01

高翔龙

redis 分布式锁 RedLock WatchDog

InfoQ 极客传媒开发者生态共创计划线上发布会

InfoQ 极客传媒开发者生态共创计划线上发布会

苹果公司开源的Swift版Netty:SwiftNIO-InfoQ