Perfect 网络框架的应用 —— 基本方法、MySQL连接和Cpp代码接入
发布于:2016-11-17 23:07,阅读数:3976,点赞数:27
> [「Perfect」](https://github.com/PerfectlySoft/Perfect)是一款基于 Swift 的服务端网络框架,并支持在 Linux 系统上部署。 > > [「我的博客」](https://blog.yuusann.com)的服务端就是使用`Perfect`框架搭建的。 # 引言 随着 Swift 语言开源,它被带到了 Linux 端。服务端软件广泛部署于 Linux 服务器中,Swift 被带到 Linux 端以后,基于 Swift 开发服务端软件也不再是梦想了。几款 Swift 语言的服务端框架应运而生,`Perfect`就是其中一款。关于`Perfect`的表现,可以看这篇文章:[「不服跑个分 - 顶级 Swift 服务端框架对决 Node.js」](http://gold.xitu.io/entry/57e296af0bd1d000570ee3b4) 简单的 Demo 已经被写烂了,本文除了简单的数据接口和Web服务的实现方法以外,还会涉及到`MySQL`数据库相关功能的实现,以及`C/C++`代码的接入方法。最后是在 Linux 服务器上部署的过程。所有使用的工具都由官方[「PerfectlySoft」](https://github.com/PerfectlySoft)提供。 # 开发环境 仅在以下环境测试通过,运行环境方面处处是坑,还望大家小心。 - 开发环境:macOS 10.12 + Xcode 8.1 with Swift 3.0 + MySQL 5.7 - 编译部署:Ubuntu 14.04 64-bit with Swift Package Manager + MySQL 5.6 # 服务端开发 项目首先将在 macOS 中开发,然后再讨论如何在 Linux 中编译部署。 官方提供的例子[「PerfectTemplate」](https://github.com/PerfectlySoft/PerfectTemplate)使用了`SPM(Swift Package Manager)`进行项目管理。本文的项目将会从修改这个官方例子开始。将项目克隆下来以后会发现并没有Xcode的工程文件,但是根目录下有一个`Package.swift`文件,它长这样: ```swift import PackageDescription let package = Package( name: "PerfectTemplate", targets: [], dependencies: [ .Package(url: "https://github.com/PerfectlySoft/Perfect-HTTPServer.git", majorVersion: 2, minor: 0) ] ) ``` 从代码中可以看出项目依赖了`Perfect-HTTPServer`,然而此时我们的源码中是不包含这个库的。怎么办呢,要手动去克隆`HTTPServer`库吗?并不用。打开终端,cd 进入该目录,使用命令: ```bash $ swift build ``` SPM 会自动从`git`上下载依赖,建立`Package`文件夹。对了,我们要用到`MySQL`相关的库,在依赖中需要加入一行内容。加完以后它是这个样子的: ```swift import PackageDescription let package = Package( name: "PerfectTemplate", targets: [], dependencies: [ .Package(url: "https://github.com/PerfectlySoft/Perfect-HTTPServer.git",majorVersion: 2, minor: 0), .Package(url:"https://github.com/PerfectlySoft/Perfect-MySQL.git",majorVersion: 2, minor: 0) ] ) ``` **坑1:`clone `过程中极易卡住,就算是翻出去了依然如此。如果在`clone`过程中卡住了,打开当前`Package`文件夹中当前克隆的项目的文件夹。如果该文件夹里有代码,`clone`其实已经完成了,ctrl+c 强制结束命令,然后再次`build`即可。** 这一步`build`的目的只是为了下载依赖包,失败了也并不要紧,只要依赖包都下载下来了就可以了。需要使用到的依赖库有: - PerfectHTTPServer - PerfectHTTP - PerfectLib - PerfectNet - COpenSSL - PerfectThread - MySQL - mysqlclient 检查每一个文件夹内都有代码以后,下一步建立 Xcode 工程文件。打开终端,在工程目录下使用: ```bash $ swift package generate-xcodeproj ``` `PerfectTemplate.xcodeproj`工程文件就建立好了。确保已经安装`MySQL`的情况下可以打开工程编译运行了。没有安装MySQL?编译报错?[「这篇文章」](http://www.jianshu.com/p/6496e53f1690)有你想要的答案。 **坑2:Xcode 请使用8.1版本。8.0在编译过程中会遇到上面链接文章中的问题3,文章底下还能看到我的留言呢,因为我没有找到解决方法。在Xcode 8.1 中不会报错。** 到这里应该是可以在 Mac 中跑起官方的例子了。噢,上面链接中的文章帮我把怎么添加数据接口和查询数据库也说完了,我大概再解释一下吧。我们可以直接修改官方例子中的`main.swift`来构造自己的服务器软件。 - 配置服务器 IP绑定、端口、根目录等配置在官方的例子中都有,自行查看代码。 - 添加接口的方法 构造`Routes`对象,在该对象中调用`add`方法添加接口,在`add`方法中有一个回调闭包,在该闭包中处理数据和返回结果。它大概的结构是这样子的: ```swift let server = HTTPServer.init() var routes = Routes.init() routes.add(method: ttp.get, uri: "/", handler: { request, response in //取得url中的参数,类型是`[(String, String)]`,遍历即可 let param = request.params() //返回数据头 response.setHeader(.contentType, value: "text/html") //返回数据体 response.appendBody(string: "数据") //返回 response.completed() }) server.addRoutes(routes) ``` - 访问数据库 这里使用自带的`MySQL`类进行访问。 **坑3:在连接数据库之前记得设置客户机的编码格式,否则将会看到中文全是问号。这里的`utf8mb4`是`utf8`的扩展编码。** ```swift private func initDataBase() -> Bool { dataMysql = MySQL.init() guard dataMysql.setOption(.MYSQL_OPT_RECONNECT, true) == true else { log.p("Failure setting option MYSQL_OPT_RECONNECT") return false } guard dataMysql.setOption(.MYSQL_SET_CHARSET_NAME, "utf8mb4") == true else { log.p("Failure setting option MYSQL_SET_CHARSET_NAME utf8mb4") return false } if (dataMysql.connect(host: testHost, user: testUser, password: testPassword) == true) { return true } else { log.p("Failure connecting to data server \(testHost)") return false } } ``` 查询结果会被存储在`storeResults`中,使用下面所示的`while`循环取出数据装入数组中。 ```swift @discardableResult func query(_ s: String) -> [[String?]]? { locker.lock() guard dataMysql.selectDatabase(named: testSchema) && dataMysql.query(statement: s) else { log.p("Failure: \(dataMysql.errorCode()) \(dataMysql.errorMessage())") locker.unlock() return nil } let results = dataMysql.storeResults() var resultArray = [[String?]]() while let row = results?.next() { resultArray.append(row) } locker.unlock() return resultArray } ``` 最后从数组中取出对应的字符串即可。 - 接入`C/C++`代码 使用桥接的方法。在我的[「这篇文章」](http://www.jianshu.com/p/b4529917dd99)中提到了桥接Objective-C代码。但是不幸的是[「官方网站」](https://swift.org/blog/swift-linux-port/)提到: >**Swift without the Objective-C Runtime**: Swift on Linux does not depend on the Objective-C runtime nor includes it. While Swift was designed to interoperate closely with Objective-C when it is present, it was also designed to work in environments where the Objective-C runtime does not exist. 在 Linux 中的 Swift 不再支持`Objective-C Runtime`了,因此我们无法在Linux中使用使用现有的 Objective-C 代码了。但是幸运的是我们可以桥接`C`代码: 目录结构为: ``` - bridge.h - include - CCode.h - MyObject.h - MyObject.cpp - CWrap.h - CWrap.cpp ``` 代码为: `bridge.h`: ```cpp #include "include/CCode.h" ``` `CCode.h`: ```cpp #include "../CWrap.h" ``` `CWrap.h`: ```cpp #ifdef __cplusplus extern "C" { #endif void initObj(); void objSayHello(); #ifdef __cplusplus } #endif ``` `CWrap.cpp`: ```cpp #include "CWrap.h" #include "MyObject.h" MyObject * obj; void initObj() { obj = new MyObject(); } void objSayHello() { obj->sayHello(); } ``` `MyObject.h`: ```cpp #include <stdio.h> class MyObject { public: MyObject(); ~MyObject(); void sayHello(); }; ``` `MyObject.cpp`: ```cpp #include "MyObject.h" MyObject::MyObject() { } MyObject::~MyObject() { } void MyObject::sayHello() { printf("hello\n"); } ``` 经过封装后的C接口直接在 Swift 中调用,像这样: ```swift initObj() objSayHello() ``` 至此,在 macOS 中整个流程就已经走通了。 # Linux 部署 在 Linux 中部署的文章还是比较少,因此在这里打算详细讲一下环境配置过程。测试的环境为 Ubuntu 14.04 64-bit。不要因为服务器配置较低内存较小而选用32位系统。Swift 仅支持64位 Linux 系统。 - Swift 环境 ```bash apt-get update //安装clang sudo apt-get install clang libicu-dev //导入keys wget -q -O - https://swift.org/keys/all-keys.asc | gpg --import - //下载对应系统的Swift:https://swift.org/download/#releases //解压后将目录添加到环境变量: echo "export PATH=%你的解压目录%/usr/bin:\"\${PATH}\"" >> ~/.basic source ~/.bashrc //查看环境变量是否配置正确 swift -version //Just do it sudo apt-get install openssl libssl-dev uuid-dev ``` 到这一步应该可以看到 Swift 的版本号了,下面步骤将克隆官方的例子来验证环境是否正常: ```bash //没安装git的先安装git sudo apt-get install git git clone https://github.com/PerfectlySoft/PerfectTemplate.git //cd到该目录后编译,运行 swift build .build/debug/PerfectTemplate ``` `build`过程可能会提示找不到`libcurl.so.4`之类的,安装`curl`解决: ```bash sudo apt-get install curl ``` 到这一步应该可以跑起官方的例子了。 - MySQL 环境 在[「这里」](http://dev.mysql.com/get/mysql-apt-config_0.8.0-1_all.deb)下载`mysql-apt-config`,安装时选择5.6版本。为什么要选择5.6版本? **坑4:使用`apt`安装的`MySQL`是5.5版本的,是不能用的。现在最新的是5.7版本的,同样也是有问题的。`MySQL`要安装5.6版本的。** 安装完之后`update`,然后安装`MySQL`服务即可。 ```bash sudo apt-get update sudo apt-get install mysql-server sudo apt-get install libmysqlclient-dev ``` 数据库管理方面,如果使用`MySQLWorkbench`远程登录的话,需要:解除IP绑定,编辑以下文件,将绑定IP字段注释,强制保存: ```bash sudo vi /etc/mysql/my.cnf ``` 在终端登录`mysql`后执行以下`SQL`开放权限: ```sql GRANT ALL PRIVILEGES ON *.* TO '你的用户名'@'%' IDENTIFIED BY '你的密码' WITH GRANT OPTION; ``` - 服务端代码 Linux 端的 Perfect 依赖是不太一样的,Linux 下会多一个`LinuxBridge`依赖。因此最佳方法是,在 Linux 上克隆官方的例子,让 SPM 自己下载依赖 ,下载完以后替换掉`Source`文件夹中的代码,换成自己的服务端程序,编译即可。 **坑5:C/C++ 接入方面,由于在 Linux 中由于没有 Xcode 那样的选项,似乎也没法直接操作编译器参数,因此无法设置桥接头文件。在这里要采用 SPM 的`Target`方式接入 C/C++ 代码。** 在`Source`文件夹中新建`SwiftCode`文件夹,将 Swift 代码放入。然后新建`CCode`文件夹,将 C/C++ 代码放入。最后在`Package.swift`的`targets`中加入: ```swift targets: [Target(name: "SwiftCode", dependencies:["CCode"])], ``` 最后在`main.swift`中加入: ```swift #if os(Linux) import CCode #endif ``` 即可完成 C/C++ 代码接入。 - 关于时区 - **坑6:Linux 下的时区识别问题。** Swift中`DateFormatter`提供了格式化时间的功能,但在 Linux 中有时区识别的问题。可以通过添加`Locale`解决: ```swift let formatter = DateFormatter.init() formatter.locale = Locale.init(identifier: "zh_CN") ``` 在我的 Ubuntu 虚拟机中问题得到了解决,但是云服务器仍然无效。终极的解决办法是,手动设置时差: ```swift let formatter = DateFormatter.init() formatter.timeZone = TimeZone.init(secondsFromGMT: 8 * 3600) ``` # 代码管理 由于开发工作在 macOS 中完成,但是 Linux 环境可能在虚拟机中,也可能在云端。来回复制代码固然是很麻烦的一件事。怎样管理两边的代码呢? 事实上 Xcode 有着更直观的文件管理功能,我们完全可以把代码放在 Linux 工程的目录中,在 Xcode中进行引用。但两端的依赖库是不一样的,所以依赖的目录还是要分开放。Linux 工程目录下放完整的工程代码,Xcode 工程目录下放 macOS 端的依赖库,业务代码直接引用 Linux 工程中的源码文件即可。在两端不同的设置上,例如数据库账户等,可以使用预编译命令`#if ... #endif`进行灵活管理。 # 结语 Swift 语言在业务开发的过程中效率是非常高的,写的也非常爽。Perfect 是非常优秀的框架,整个服务端的开发过程非常愉快。倒是在 Linux 部署问题上踩了不少坑。希望本文能给正在 Perfect 坑坑洼洼的道路上前进的人一点参考。
评论:0条