Edge
Swift 中的服务器端非阻塞 IO
在我们的 Slack 频道中提问!

Lightning

(原名 Edge)

Swift Build Status codecov Slack Status

Node

Lightning 是一个用 Swift 编写的 HTTP 服务器和 TCP 客户端/服务器框架,灵感来源于 Node.js。它可以在 OS X 和 Linux 上运行。与 Node.js 类似,Lightning 使用事件驱动、非阻塞 I/O 模型。正如 Node.js 使用 libuv 来实现这个模型一样,Lightning 使用 libdispatch

这使得 Lightning 快速、高效,并且最关键的是默认情况下是单线程的。如果您有服务器端状态,则根本不需要担心锁/互斥锁/信号量/等等。当然,如果需要,Lightning 应用程序可以利用 libdispatch 轻松地将繁重的处理卸载到后台线程。

响应式编程

Lightning 的事件 API 通过概括熟悉的 promises 概念,拥抱了函数式响应式编程。这个 API 被称为 StreamKit

StreamKit 的架构灵感来源于 ReactiveCocoaRxSwift

我们为什么要重新实现?

FRP 极大地简化了异步事件的管理。一般概念是我们可以构建一个 spout,它在异步事件发生时将其推送出去。然后我们连接一个转换管道,该管道对事件进行操作并将转换后的值传递下去。我们甚至可以做一些有趣的事情,比如合并流!看看这些 操作,或者观看 这个关于 Netflix 如何使用 FRP 的演讲

安装

Lightning 可以作为 Swift 3/4 包使用。只需将 Lightning 添加为您的 Swift Package 的依赖项即可。

Swift 3

import PackageDescription

let package = Package(
    name: "MyProject",
    dependencies: [
        .Package(url: "https://github.com/skylab-inc/Lightning.git", majorVersion: 0, minor: 3)
    ]
)

Swift 4

// swift-tools-version:4.0
// The swift-tools-version declares the minimum version of Swift required to build this package.
import PackageDescription

let package = Package(
    name: "MyProject",
    dependencies: [
        .package(url: "https://github.com/skylab-inc/Lightning.git", from: "0.3.0"),
    ]
)

用法

路由

import Lightning
import Foundation

// Create an API router.
let api = Router()

// Add a GET "/users" endpoint.
api.get("/users") { request in
    return Response(status: .ok)
}

// NOTE: Equivalent to `api.post("/auth/login")`
let auth = api.subrouter("/auth")
auth.post("/login") { request in
    return Response(status: .ok)
}

// Middleware to log all requests
// NOTE: Middleware is a simple as a map function or closure!
let app = Router()
app.map { request in
    print(request)
    return request
}

// Mount the API router under "/v1.0".
app.add("/v1.0", api)

// NOTE: Warnings on all unhandled requests. No more hanging clients!
app.any { _ in
    return Response(status: .notFound)
}

// Start the application.
app.start(host: "0.0.0.0", port: 3000)

原始 HTTP

import Lightning
import Foundation

func handleRequest(request: Request) -> Response {
    print(String(bytes: request.body, encoding: .utf8)!)
    return try! Response(json: ["message": "Message received!"])
}

let server = HTTP.Server()
server.listen(host: "0.0.0.0", port: 3000).startWithNext { client in

    let requestStream = client.read()
    requestStream.map(handleRequest).onNext{ response in
        client.write(response).start()
    }

    requestStream.onFailed { clientError in
        print("Oh no, there was an error! \(clientError)")
    }

    requestStream.onCompleted {
        print("Goodbye \(client)!")
    }

    requestStream.start()
}

RunLoop.runAll()

TCP

import Lightning
import Foundation

let server = try! TCP.Server()
try! server.bind(host: "0.0.0.0", port: 50000)
    
server.listen().startWithNext { connection in
    let byteStream = connection.read()
    let strings = byteStream.map { String(bytes: $0, encoding: .utf8)! }
    
    strings.onNext { message in
        print("Client \(connection) says \"\(message)\"!")
    }
    
    strings.onFailed { error in
        print("Oh no, there was an error! \(error)")
    }
    
    strings.onCompleted {
        print("Goodbye \(connection)!")
    }
    
    strings.start()
}

RunLoop.runAll()

Lightning 不是 Node.js

Lightning 并非旨在履行 Node.js 的所有角色。Node.js 是一个 JavaScript 运行时环境,而 Lightning 是一个 TCP/Web 服务器框架。Swift 编译器和包管理器,结合第三方的 Swift 包,使得没有必要将这些功能构建到 Lightning 中。