用 Swift 的框架 Vapor 写服务器这事儿怎么样?

基石

就 Swift 写服务器这件事来说,需要一个非常现实的理由为什么用 Swift, 不然 Python
Ruby Go 随便抓一个,可能都好上一百倍。

“快”并不是第一因,真的在生产环境跑起来,往往数据库的 IO 才是第一瓶颈。为了解答自己的这个疑惑,我研究了一番 Vapor 的实现方式。

HTTP+Server.swift 这个文件里,能看到服务器处理请求的方式

private let queue = DispatchQueue(label: "codes.vapor.server", qos: .userInteractive, attributes: .concurrent)

利用 Swift 标准库 libdispatch 实现的 DispatchQueue 开了个并行队列,每个请求由 Dispatch 进行调度。

好吧,没有发现有说服力的理由用这个写服务器,强行找一个吧——

“我没用 Swift 写过服务器,写一个体验体验又何妨!”

在继续之前,可能需要阅读这篇文章 “使用 Visual Studio Code 开发 Swift” 把开发环境配置起来 。

Vapor!

通过一个简单的命令就可以完成 Vapor 的安装

curl -sL toolbox.vapor.sh | bash

安装完成后,使用 vapor new Hello --template=light 创建第一个项目

Hello
├── Sources
│   └── App
│       └── main.swift
└── Package.swift

这个目录的结构和 light-template 是一样的, Vapor 引入了一个项目模版的概念,可以通过 --template 去指定想要的项目模版,如果不加这个参数,就会使用默认模版 basic-template

为了简化理解,使用 light-template 这个模版。

想要运行服务器,需要先 vapor build 下载依赖并编译整个项目

➜  vapor build
No Packages folder, fetch may take a while...
Fetching Dependencies [     •                   ]

配置服务器

Vapor 有一些约定项目,如果项目根目录里有 Config/servers.json 那么他会用这个配置文件来配置服务器,通过这个文件,我们可以指定服务器的 host 地址和端口

{
  "http": {
    "host": "0.0.0.0",
    "port": 8000
  }
}

0.0.0.0127.0.0.1 都表示本机,使用 0.0.0.0 的原因是,一个机器可能有多个 IP 地址,0.0.0.0 表示监听每个 IP 8000 端口收到的请求。

127.0.0.1 则表示只接受本机发给本机的请求,从网络上其他电脑发过来的请求,不论是请求的哪个 IP,都是不被处理的。

配置完成之后,使用 vapor run 来开启服务器

➜  vapor run
Running myworld...
No command supplied, defaulting to serve...
No preparations.
Server 'http' starting at 0.0.0.0:8000

此时服务器已经启动,并且执行 Sources/App/main.swift 中的逻辑

import Vapor

let drop = Droplet()

drop.get { request in request.description }

drop.run()

需要关注的一点是 drop.get { request in request.description } 是指,当服务器收到 GET 请求,返回请求本身的内容。

关于 HTTP

现在你在浏览器里输入 http://0.0.0.0:8000/ 可以看到服务器返回的一段有趣的文字

Request
- GET / HTTP/1.1
- Headers:
	Host: 0.0.0.0:8000
	Upgrade-Insecure-Requests: 1
	Connection: keep-alive
	User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/57.0.2987.98 Safari/537.36
	Accept-Language: en-US,en;q=0.8,zh-CN;q=0.6,zh;q=0.4,zh-TW;q=0.2
	Accept-Encoding: gzip, deflate, sdch
	Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
- Body:

这段就代表了在网络上发出的每个 HTTP 请求的基本构成

HTTP 方法

GET / HTTP/1.1

GET 代表请求方法,在浏览器打开一个网址使用的就是 GET,除了 GET 之外,还有很多方法

  • OPTIONS:这个方法可使服务器传回该资源所支持的所有HTTP请求方法。
  • HEAD:与GET方法一样,都是向服务器发出指定资源的请求。只不过服务器将不传回资源的本文部分。
  • GET:向指定的资源发出“显示”请求。
  • POST:向指定资源提交数据。
  • PUT:向指定资源位置上传其最新内容。
  • DELETE:请求服务器删除Request-URI所标识的资源。
  • TRACE:回显服务器收到的请求,主要用于测试或诊断。
  • CONNECT:HTTP/1.1协议中预留给能够将连接改为管道方式的代理服务器。

紧跟着 GET 后面的 表示请求的路径,如果你请求的是 http://0.0.0.0:8000/hello 这里就会变成 /hello

HTTP/1.1 则是指 HTTP 协议的版本。

Headers 是什么?

Headers 是请求元数据,一般由发出请求的客户端 (User-Agent) 自动填充,Headers 里是一些标准化的数据, 约束整个互联网的网络请求行为,像护照一样方便各方可以顺利进行响应和统计。

User-Agent 里你可以看到我电脑的系统信息,浏览器的内核版本,以及这个请求是由 Chrome 发出的。

Accept-Language 表示 User-Agent 即客户端 Chrome 建议服务器返回的语言。

Accept-Encoding 表示客户端可以接受的内容编码方式。

Accept 表示客户端接受返回内容的格式。

除了这些之外,还有大量支持的字段,你也可以自定义请求头的字段。

Body

Body 里可以存放数据,图片,音乐等任何数据。但现在我们的服务器只能接受 GET 请求,一起来修改下 main.swift 让他可以接受 POST 请求。

import Vapor

let drop = Droplet()

drop.get { request in request.description }

drop.post { request in request.description }

drop.run()

在终端通过 curl -X POST --data "data=apple" 0.0.0.0:8000 命令发送一个 POST 请求,在这个请求里,我们把 data=apple 作为请求的 body 发了过去。

收到请求后,服务器返回了我们请求的内容

➜  myworld curl -X POST --data "data=apple" 0.0.0.0:8000
Request
- POST / HTTP/1.1
- Headers:
	Content-Length: 8
	Content-Type: application/x-www-form-urlencoded
	User-Agent: curl/7.51.0
	Accept: */*
	Host: 0.0.0.0:8000
- Body:
	data=apple

果然,data=apple 被插了进去 :)

此时安利给你一个 Chrome 扩展,Postman 我的最爱,可以模拟各种 HTTP 请求。

20170316148964571114669.png

接下来呢?

到目前为止,我们了解了 HTTP 请求的格式,用 Swift 写了个简单的处理 GET 和 POST 请求的服务器,但还没有涉及到数据库,也没有任何实际的用途。

接下来,做一个 Telegram 的机器人来更深入的试探 Swift 写服务器怎么样。

用 Swift 的框架 Vapor 写服务器这事儿怎么样?
Share this