使用类似于Ruby的Crystal语言,利用Amethyst创建一个API服务器
水晶
-
- rubyライクに書ける
-
- 静的型付け
-
- golangの有力対抗馬(だと思ってる)
- コンパイルしてバイナリ実行
制作的API是从数据库中拉取信息,并以JSON格式返回的内容。这次我们只制作一个返回用户数量的API。
Go语言
作为一个平常使用Ruby的人,我觉得Golang虽然简单,但是有一些C的感觉…一开始我用Golang写了一部分,但后来尝试用crystal重写发现写起来更容易!所以我选择了在这里写。
由于Crystal相当类似于Ruby,所以学习成本非常低。
另外,据说与golang不同的是无法增加核心数量(将来可能会进行适配)。
紫水晶
因为这次是API服务器,所以没有使用这个完整的堆栈框架Rails的理由,但是我想快速编写路由部分,所以决定使用它。
与几乎不使用的情况相比,处理速度降低到了原来的2/3左右。
安装软件包
我会快速制作。
$ mkdir api_sample
$ cd api_sample
$ touch Projectfile
Projectfile在Ruby中相当于Gemfile。
开始安装时会生成一个.deps.lock文件,并固定版本。
以下是必要的软件包清单。
deps do
github "will/crystal-pg"
github "Codcore/amethyst"
github "spalger/crystal-mime"
end
通过使用Crystal Deps进行安装,并且通过代码中的require语句来进行加载。
接下来,我们将开始创建API部分。
这次我们以简单的方式创建了,但是可以将控制器等分离出来,以类似Rails的方式编写,详情请参考官方存储库中的示例。
$ mkdir src
$ vim src/app.cr
require "amethyst"
require "pg"
DB = PG.connect("postgres://user@host:port/db_name")
class UsersController < Base::Controller
actions :index
def index
result = DB.exec({Int32}, "SELECT count(id) FROM users")
hash = Hash(String, Int32).new
hash["users"] = result.rows[0].to_a.first
json hash.to_json
end
end
class ApiApp < Base::App
routes.draw do
get "/api/v1/users", "users#index"
register UsersController
end
end
app = ApiApp.new
app.serve
只需要这么做。
只需在users#index的最后一行将format设置为json,将hash转换为to_json并返回。
非常类似Ruby,很容易写。
返回值会像这样。
{users:100}
只需构建并使用给定的实际环境。
$ crystal build --release src/app.cr
$ ./app
在Ubuntu 14.04上发生的问题
Crystal-PG出现了一个故障导致错误。看起来好像之前也提过问题,但似乎还没有解决?
阅读上一期的内容后,我发现将postgres升级到9.4版本后,它可以正常运行。于是我将postgres升级到9.4版本后,它成功地开始运行。
添加备注
这个gem似乎是根据pg_config参数来确定链接的,但是经过调查发现,postgres的9.3系和9.4系之间有以下不同之处。
# postgres 9.3.9
INCLUDEDIR = /usr/include/postgresql
LIBDIR = /usr/lib
# postgres 9.4.4
INCLUDEDIR = /usr/include/postgresql
LIBDIR = /usr/lib/x86_64-linux-gnu
尝试修正LIBDIR位置后,测试发现程序可以正常运行,因此似乎直接修改crystal-pg也可以使其正常运行。
module PG
@[Link(ldflags: "-lpq -I`pg_config --includedir` -L /usr/lib/x86_64-linux-gnu")]
lib LibPQ
文件位于root/.deps/will-crystal-pg/src/pg/libpq.cr。
nginx的配置
因为在crystal中无法确定如何设置Access-Control-Allow-Origin,所以我们选择在nginx内进行设置。
upstream crystal {
server localhost:8080;
keepalive 300;
}
server {
location /api {
access_log off;
error_log /dev/null crit;
add_header Access-Control-Allow-Origin *;
proxy_pass http://crystal;
proxy_http_version 1.1;
proxy_set_header Connection "";
}
}
与 Golang 的基准测试
我使用golang和gin框架来制作了同样的东西,并进行了基准测试。
在本地运行 ab 命令,使用以下选项:-k -c 10 -n 10000,测试请求地址为 http://127.0.0.1/api/v1/users。
根据结构晶体情况来看,它压倒性地胜出。
然而,如果使用所有核心,我认为golang会超过它。
使用 nginx 进行传递的话,通过 nginx 的速度大约为 2700req/sec 左右。顺便提一下,如果使用 rails ,速度大约为 200-300req/sec (工作线程数量:2-8)。
如果能够轻松地实现这么高的速度提升,也许重新使用Crystal编写公开的API可能是一个好主意。
鉴于这个样本略有不同,但我在Github上也有公开,您可以参考一下。