使用类似于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。

langrequest/seccrystal(amethyst)2272.13golang(gin)1278.75

根据结构晶体情况来看,它压倒性地胜出。
然而,如果使用所有核心,我认为golang会超过它。

使用 nginx 进行传递的话,通过 nginx 的速度大约为 2700req/sec 左右。顺便提一下,如果使用 rails ,速度大约为 200-300req/sec (工作线程数量:2-8)。

如果能够轻松地实现这么高的速度提升,也许重新使用Crystal编写公开的API可能是一个好主意。

鉴于这个样本略有不同,但我在Github上也有公开,您可以参考一下。

bannerAds