まだプログラマーですが何か?

プログラマーネタ中心。たまに作成したウェブサービス関連の話も https://twitter.com/dotnsf

タグ:loopback

Node.js サーバーのリバースプロキシとして、高性能な Nginx を利用する方法を紹介します。以下では CentOS 環境を前提とした方法を紹介します。

まずは Node.js + アプリケーションの環境を構築します。特に今回は Node.js 上で StrongLoop LoopBack アプリケーションを動かすという前提としてます(独自のアプリケーションでも構いません)。この環境を整えるための手順はこちらを参照ください:
CentOS に StrongLoop をインストールする

アプリケーションが準備できたら Node.js を起動しておきます。実際には forever を使って Node.js をデーモン化しておくと便利だと思います。この辺りの手順はこちらを参照ください:
Forever で Node.js をデーモン化する

(↓ 3000 番ポートで Node.js アプリが動いている様子)
2016061601



この環境にリバースプロキシとして利用する Nginx を導入します。Nginx の導入手順はここの内容を参考にしています:
CentOS に Nginx をインストールして PHP を使う
# rpm -ivh http://nginx.org/packages/centos/6/noarch/RPMS/nginx-release-centos-6-0.el6.ngx.noarch.rpm
# yum install nginx

Nginx の設定ファイル(/etc/nginx/conf.d/default.conf)を以下のように編集します(青字が追加部分、赤字がコメントです):
# backend_node に LoopBack サーバー(localhost:3000)を追加
upstream backend_node { ip_hash; server 127.0.0.1:3000; } server { listen 80; server_name localhost; #charset koi8-r; #access_log /var/log/nginx/log/host.access.log main; location / { root /usr/share/nginx/html; index index.html index.htm;

# 存在するファイルパスが指定された場合はそのまま Nginx で処理 if ( -f $request_filename ) { break; }
# 存在しないファイルパスが指定された場合は backend_node で処理 if ( !-f $request_filename ) { proxy_pass http://backend_node; break; } } : :

これで準備完了です。この状態で Nginx を起動します:
# /etc/init.d/nginx start

改めて同サーバーにウェブブラウザでアクセスします。まずドキュメントルートにアクセスすると、これは Nginx が処理できるパスなので、Nginx のウェルカムページが表示されます:
2016061603


次に /explorer というパスを指定してアクセスしてみます。すると、このファイルやディレクトリは存在しないので Nginx は処理せず、backend_node で指定した 3000 番ポートのサーバー(つまり Node.js)に内部転送して、その結果が表示されます:
2016061602


これで 3000 番ポートを指定しなくても(Node.js 上で動いている)LoopBack サーバーにアクセスできるようになっただけでなく、HTML や画像、CSS、JavaScript といった静的ファイルは Node.js ではなく(高速な)Nginx に任せる、ということもできるようになります。


 

StrongLoopLoopBack を使うと、データベースのモデルを定義するだけで CRUD の REST API を生成し、OpenAPI(Swagger) スタイルのドキュメントと併せて簡単に公開できます。この辺りについては以前のブログエントリを参照してください:
CentOS に StrongLoop をインストールする


ところで、LoopBack を使って公開された API にはパラメータで挙動を指定できるものもあります。例えばモデルの一覧を取得する GET リクエストでは一覧を絞り込むためのクエリーを指定したり、取得結果の数やオフセットを指定することも可能です:
2016020601


そのための方法を紹介します。例えば items というテーブルに対して LoopBack で CRUD の API を作成したと仮定します。items の一覧を取得するには以下の様な URL に対する HTTP リクエストを GET で実行することになります(XX.XX.XX.XX は LoopBack が動いているサーバー):
http://XX.XX.XX.XX/api/items/

さて、一覧の検索条件(例えば id < 100)を指定する場合、SQL ではこのように指定することになります:
> select * from items where id < 100;

この条件を指定して上記の HTTP GET リクエストを実行するには、つまり id < 100 のものだけの一覧を API で取得するには、以下の様なパラメータを指定して実行します:
http://XX.XX.XX.XX/api/items?filter[where][id][lt]=100

filter という名前に [where] 句が指定され、更に条件である [id] が [lt] (Less Than) で 100 である、という条件が指定されていることになります。


一方、一覧の検索結果の取得数(例えば10件)を指定する場合、SQL(MySQL) ではこのように指定します:
> select * from items limit 10;

この条件を指定して HTTP GET リクエストを実行するには、以下の様なパラメータを指定して実行します:
http://XX.XX.XX.XX/api/items?filter[limit]=10

filter という名前に [limit] 句が指定され、その値が 10 である、という条件が指定されていることになります。なんとなくコツが分かってきましたか?

ちなみに一覧の検索結果の取得数を 50 件目からの 10 件、とするには SQL(MySQL) ではこのように指定します:
> select * from items limit 50, 10;

limit 句を指定し、オフセットが 0 以外の場合は limit 数の前にオフセット数を指定します。これを API のパラメータで指定するには以下のようにします:
http://XX.XX.XX.XX/api/items?filter[limit]=10&filter[offset]=50

クエリーや結果取得の条件は同時に指定することができます。例えば「 id < 100 のものをオフセット 50 で10 件取得」するのであれば、API ではこのように指定します:
http://XX.XX.XX.XX/api/items?filter[where][id][lt]=100&filter[limit]=10&filter[offset]=50

filter の使い方、なんとなくコツがわかってきましたか? 使っているデータベースの種類は何で、そのデータベースでは SQL ではどういった指定になるか、をイメージできるとわかりやすいです。


このパラメータに関する API ドキュメントはこちら:
https://docs.strongloop.com/display/public/LB/Where+filter
 

サーバーサイド JavaScript サーバーである StrongLoop をローカル環境に作成(&実行)する手順を以前に紹介しました:
CentOS に StrongLoop をインストールする
上記記事では、データストア先として標準のメモリ DB を使う前提で手順を紹介していますが、実際には外部のリレーショナル DB を使いたいケースも多いと思っています。というわけで、データストアに MySQL を使う場合の手順を紹介します。

まずは上記リンク先を参照して、StrongLoop のインストールと、StrongLoop アプリケーションの作成(上記リンク先ページだと slc loopback コマンドで myapp アプリを作るところ)までを実行しておいてください。以下、アプリケーションの名前は myapp として、myapp ディレクトリが出来ている前提で以下を紹介します。違うアプリケーション名で作成した場合は適宜読み替えてください:



また、このアプリケーションのデータストア先となる MySQL データベース、および接続情報は以下のようになっているものと仮定します:
属性
MySQL サーバー名(HOSTNAME)
MySQL ポート番号3306
データベース名(DBNAME)
ユーザー名(USERNAME)
パスワード(PASSWORD)


ではこのアプリケーションのデータストア先が上記の MySQL データベースになるようカスタマイズを開始します。まずは LoopBack MySQL コネクタをインストールします:
# cd myapp
# npm install --save loopback-connector-mysql

LoopBack MySQL コネクタのインストールができたら、LoopBack としてのデータソースを作成します。ここでは "mydb" という名前でデータソースを作成しています。データソース名の指定ではデフォルトの mydb のまま、コネクタタイプの指定ではカーソルを MySQL に合わせて選択します:
# slc loopback:datasource mydb
  :
  :
? Enter the data-source name: mydb
? Select the connector for mydb: MySQL (supported by StrongLoop)

今の手順でデータソースファイル(server/datasource.json)が生成されています。このファイルを編集して、目的のリモート MySQL データベースに接続するよう上記の接続情報を指定します(青字部分を追加します):
# vi server/datasource.json


{
  "db": {
    "name": "db",
    "connector": "memory"
  },
  "mydb": {
    "host": "(HOSTNAME)",
    "port": 3306,
    "database": "(DBNAME)",
    "user": "(USERNAME)",
    "password": "(PASSWORD)",
    "name": "mydb",
    "connector": "mysql"
  }
}

ここまでくれば後は前回と同様です。API で CRUD を行うモデルとして、以下の様な item モデルを定義しましょう:
列名列型必須条件
namestringYES
codestringYES
pricenumber 


モデルの名称は item、データソースは先程定義した(MySQL 上の)mydb、PersistedModel で REST API の公開対象とします。また Common model を指定します:
# slc loopback:model item
  :
  :
? Enter the model name: item
? Select the data-source to attach item to: mydb (mysql)
? Select model's base class PersistedModel
? Expose item via the REST API? Yes
? Custom plural form (used to build REST URL):
? Common model or server only? common
Let's add some item properties now.

続けて3つのフィールドの名前、型、必須条件をそれぞれ指定していきます。最後に名前指定の所でそのまま Enter を押すとモデルの定義も終了です:
  :
  :

Enter an empty property name when done.
? Property name: name
(!) generator#invoke() is deprecated. Use generator#composeWith() - see http://yeoman.io/authoring/composability.html
   invoke   loopback:property
? Property type: string
? Required? Yes

Let's add another item property.
Enter an empty property name when done.
? Property name: code
(!) generator#invoke() is deprecated. Use generator#composeWith() - see http://yeoman.io/authoring/composability.html
   invoke   loopback:property
? Property type: string
? Required? Yes

Let's add another item property.
Enter an empty property name when done.
? Property name: price
(!) generator#invoke() is deprecated. Use generator#composeWith() - see http://yeoman.io/authoring/composability.html
   invoke   loopback:property
? Property type: number
? Required? No

Let's add another item property.
Enter an empty property name when done.
? Property name: (ここでそのまま Enter で終了)


最後にこのモデルを DB スキーマとして登録しましょう。MySQL にコマンドラインでログインし、以下のような create table コマンドを実行して item テーブルを作ります:
> create table item( id int primary key auto_increment, name text, code text, price integer );

これで動く状態ができました。実際に動かしてみましょう:
# node .

で、ウェブブラウザを起動して、この開発環境の 3000 番ポートの /explorer パスにアクセスしてみます:
2015121802


作成した myapp アプリケーションの中で、定義した item モデルの CRUD API が公開されています!


実際にデータを作成してみましょう。item モデルを展開後の /items の POST メソッドを開き、data フィールドに以下の内容を入力して "Try it out!" ボタンをクリックしてみます:
2015121803

{
  "name": "コーラ",
  "code": "AA001",
  "price": 1000
}

実行結果が以下のようになっていれば POST コマンドは成功して、テーブルに1レコードが追加されたことになります:
2015121901


ちなみにこの段階で /items の GET コマンドを実行すると、今作成したデータが返されるはずです。
2015121902


念のため、MySQL サーバーにログインして item テーブルの中身を確認すると、このコマンドで POST したレコードが作られているはずです。リモートの MySQL と連携する StrongLoop 環境が作れたことになります:
> select * from item;
+----+-----------+-------+-------+
| id | name      | code  | price |
+----+-----------+-------+-------+
|  2 | コーラ    | AA001 |  1000 |
+----+-----------+-------+-------+
1 row in set (0.18 sec)

なお、この状態を cf コマンドで IBM Bluemix 上の Node.js ランタイムにプッシュすると、そっくりそのまま Bluemix 環境で動かすことも可能です:
2015121903


以上、StrongLoop と MySQL との連携方法の説明、および Bluemix 環境への移行方法の紹介でした。


このページのトップヘ