Cloud Foundryを使ってみよう[5]
この記事は1年以上前に投稿されました。情報が古い可能性がありますので、ご注意ください。
node.jsとRabbitMQの連携
RabbitMQは、Advanced Message Queuing Protocolを使用する、メッセージ指向のミドルウェアです。
本項では、Using RabbitMQ with Node.jsを参考に、RabbitMQに接続するnode.jsで作成されたアプリケーションを、Cloud Foundry上にデプロイします。
RabbitMQのインストール
RabbitMQサーバをローカルにインストールします。
root@debian:~# aptitude install rabbitmq-server : Setting up erlang-base (1:14.a-dfsg-3) ... Searching for services which depend on erlang and should be started...none found. Setting up erlang-syntax-tools (1:14.a-dfsg-3) ... Setting up erlang-asn1 (1:14.a-dfsg-3) ... Setting up erlang-mnesia (1:14.a-dfsg-3) ... Setting up erlang-runtime-tools (1:14.a-dfsg-3) ... Setting up erlang-crypto (1:14.a-dfsg-3) ... Setting up erlang-public-key (1:14.a-dfsg-3) ... Setting up erlang-ssl (1:14.a-dfsg-3) ... Setting up erlang-inets (1:14.a-dfsg-3) ... Setting up erlang-corba (1:14.a-dfsg-3) ... Setting up erlang-xmerl (1:14.a-dfsg-3) ... Setting up erlang-edoc (1:14.a-dfsg-3) ... Setting up erlang-docbuilder (1:14.a-dfsg-3) ... Setting up erlang-erl-docgen (1:14.a-dfsg-3) ... Setting up erlang-eunit (1:14.a-dfsg-3) ... Setting up erlang-ic (1:14.a-dfsg-3) ... Setting up erlang-inviso (1:14.a-dfsg-3) ... Setting up libltdl7 (2.2.6b-2) ... Setting up erlang-snmp (1:14.a-dfsg-3) ... Setting up erlang-os-mon (1:14.a-dfsg-3) ... Setting up erlang-parsetools (1:14.a-dfsg-3) ... Setting up erlang-percept (1:14.a-dfsg-3) ... Setting up erlang-ssh (1:14.a-dfsg-3) ... Setting up erlang-webtool (1:14.a-dfsg-3) ... Setting up erlang-tools (1:14.a-dfsg-3) ... Setting up libsctp1 (1.0.11+dfsg-1) ... Setting up lksctp-tools (1.0.11+dfsg-1) ... Setting up odbcinst (2.2.14p2-1) ... Setting up odbcinst1debian2 (2.2.14p2-1) ... Setting up unixodbc (2.2.14p2-1) ... Setting up erlang-odbc (1:14.a-dfsg-3) ... Setting up erlang-nox (1:14.a-dfsg-3) ... Setting up rabbitmq-server (2.6.1-1) ... Adding group `rabbitmq' (GID 105) ... Done. Adding system user `rabbitmq' (UID 103) ... Adding new user `rabbitmq' (UID 103) with group `rabbitmq' ... Not creating home directory `/var/lib/rabbitmq'. Starting rabbitmq-server: SUCCESS rabbitmq-server. root@debian:~#
RabbitMQを利用するnode.jsのサンプルアプリケーションの作成
サンプルアプリケーションのディレクトリを作成します。
cf@debian:~$ mkdir rabbitmq-node cf@debian:~$ cd rabbitmq-node cf@debian:~/rabbitmq-node$
node.jsパッケージをインストールするための、依存関係を記述したファイルを作成します。
cf@debian:~/rabbitmq-node$ cat > package.json
{
"name":"node-amqp-demo",
"version":"0.0.1",
"dependencies": {
"amqp":">= 0.1.0",
"sanitizer": "*"
}
}
cf@debian:~/rabbitmq-node$
npmコマンドでインストールを行います。
cf@debian:~/rabbitmq-node$ npm install npm WARN publish-everything amqp@0.1.1 Adding entire directory to tarball. Please add a npm WARN publish-everything amqp@0.1.1 .npmignore or specify a 'files' array in the package.json npm ok cf@debian:~/rabbitmq-node$
サンプルアプリケーションを作成します。
cf@debian:~/rabbitmq-node$ cat > app.js
var http = require('http');
var amqp = require('amqp');
var URL = require('url');
var htmlEscape = require('sanitizer').escape;
function rabbitUrl() {
if (process.env.VCAP_SERVICES) {
conf = JSON.parse(process.env.VCAP_SERVICES);
return conf['rabbitmq-2.4'][0].credentials.url;
}
else {
return "amqp://localhost";
}
}
var port = process.env.VCAP_APP_PORT || 3000;
var messages = [];
function setup() {
var exchange = conn.exchange('cf-demo', {'type': 'fanout', durable: false}, function() {
var queue = conn.queue('', {durable: false, exclusive: true},
function() {
queue.subscribe(function(msg) {
messages.push(htmlEscape(msg.body));
if (messages.length > 10) {
messages.shift();
}
});
queue.bind(exchange.name, '');
});
queue.on('queueBindOk', function() { httpServer(exchange); });
});
}
function httpServer(exchange) {
var serv = http.createServer(function(req, res) {
var url = URL.parse(req.url);
if (req.method == 'GET' && url.pathname == '/env') {
printEnv(res);
}
else if (req.method == 'GET' && url.pathname == '/') {
res.statusCode = 200;
openHtml(res);
writeForm(res);
writeMessages(res);
closeHtml(res);
}
else if (req.method == 'POST' && url.pathname == '/') {
chunks = '';
req.on('data', function(chunk) { chunks += chunk; });
req.on('end', function() {
msg = unescapeFormData(chunks.split('=')[1]);
exchange.publish('', {body: msg});
res.statusCode = 303;
res.setHeader('Location', '/');
res.end();
});
}
else {
res.statusCode = 404;
res.end("This is not the page you were looking for.");
}
});
serv.listen(port);
}
console.log("Starting ... AMQP URL: " + rabbitUrl());
var conn = amqp.createConnection({url: rabbitUrl()});
conn.on('ready', setup);
// ---- helpers
function openHtml(res) {
res.write("<html><head><title>Node.js / RabbitMQ demo</title></head><body>");
}
function closeHtml(res) {
res.end("</body></html>");
}
function writeMessages(res) {
res.write('<h2>Messages</h2>');
res.write('<ol>');
for (i in messages) {
res.write('<li>' + messages[i] + '</li>');
}
res.write('</ol>');
}
function writeForm(res) {
res.write('<form method="post">');
res.write('<input name="data"/><input type="submit"/>');
res.write('</form>');
}
function printEnv(res) {
res.statusCode = 200;
openHtml(res);
for (entry in process.env) {
res.write(entry + "=" + process.env[entry] + "<br/>");
}
closeHtml(res);
}
function unescapeFormData(msg) {
return unescape(msg.replace('+', ' '));
}
cf@debian:~/rabbitmq-node$
サンプルアプリケーションをローカルで実行します。
cf@debian:~/rabbitmq-node$ node app.js Starting ... AMQP URL: amqp://localhost
別シェルからwgetコマンドで接続します。
cf@debian:~$ wget -q -O - http://localhost:3000/ <html><head><title>Node.js / RabbitMQ demo</title></head><body><form method="post"><input name="data"/><input type="submit"/></form><h2>Messages</h2><ol></ol></body></html>cf@debian:~$
次に、データをPOSTしてみます。
cf@debian:~$ wget -q --post-data="data=TEST" -O - http://localhost:3000/ <html><head><title>Node.js / RabbitMQ demo</title></head><body><form method="post"><input name="data"/><input type="submit"/></form><h2>Messages</h2><ol></ol></body></html>cf@debian:~$
POSTしたデータが表示されることを確認します。
cf@debian:~$ wget -q -O - http://localhost:3000/ <html><head><title>Node.js / RabbitMQ demo</title></head><body><form method="post"><input name="data"/><input type="submit"/></form><h2>Messages</h2><ol><li>TEST</li></ol></body></html>cf@debian:~$
以上のように動作が確認できたら、Cloud Foundry上にサンプルアプリケーションをデプロイします。この際、RabbitMQと関連付けを行っておきます。
cf@debian:~/rabbitmq-node$ vmc push Would you like to deploy from the current directory? [Yn]:
y
Application Name:
rabbitmq-node
Application Deployed URL [rabbitmq-node.cloudfoundry.com]:
rabbitmq-node-creationline.cloudfoundry.com
Detected a Node.js Application, is this correct? [Yn]:
y
Memory Reservation (64M, 128M, 256M, 512M, 1G, 2G) [64M]: Creating Application: OK Would you like to bind any services to 'rabbitmq-node'? [yN]:
y
The following system services are available 1: mongodb 2: mysql 3: postgresql 4: rabbitmq 5: redis Please select one you wish to provision:
4
Specify the name of the service [rabbitmq-ec036]: Creating Service: OK Binding Service [rabbitmq-ec036]: OK Uploading Application: Checking for available resources: OK Packing application: OK Uploading (1K): OK Push Status: OK Staging Application: OK Starting Application: .......Error: Application 'rabbitmq-node's state is undetermined, not enough information available. cf@debian:~/rabbitmq-node$
アプリケーションの起動に失敗したので、ログの確認を行います。
cf@debian:~/rabbitmq-node$ vmc crashlogs rabbitmq-node
====> logs/stderr.log <====
node.js:134
throw e; // process.nextTick error, or 'error' event on first tick
^
Error: Cannot find module 'amqp'
at Function._resolveFilename (module.js:326:11)
at Function._load (module.js:271:25)
at require (module.js:355:19)
at Object.<anonymous> (/var/vcap/data/dea/apps/rabbitmq-node-0-f77585e3130e2d0644c1004455eaf707/app/app.js:2:12)
at Module._compile (module.js:411:26)
at Object..js (module.js:417:10)
at Module.load (module.js:343:31)
at Function._load (module.js:302:12)
at Array.<anonymous> (module.js:430:10)
at EventEmitter._tickCallback (node.js:126:26)
cf@debian:~/rabbitmq-node$
node.jsとMongoDBを連携させたときと同じく、RabbitMQ用のnode.jsモジュールをアプリケーションと一緒にデプロイしなければいけないようです。
サンプルアプリケーションのディレクトリに、モジュールを格納するためのnode_modulesディレクトリを作成し、そこに必要なモジュールをコピーします。
cf@debian:~/rabbitmq-node$ mkdir node_modules cf@debian:~/rabbitmq-node$ cp -a ../.node_libraries/.npm/amqp/active/package node_modules/amqp cf@debian:~/rabbitmq-node$ cp -a ../.node_libraries/.npm/sanitizer/active/package node_modules/sanitizer cf@debian:~/rabbitmq-node$
サンプルアプリケーションを更新し、必要なモジュールをCloud Foundry上に転送します。
cf@debian:~/rabbitmq-node$ vmc update rabbitmq-node Uploading Application: Checking for available resources: OK Processing resources: OK Packing application: OK Uploading (1K): OK Push Status: OK Stopping Application: OK Staging Application: OK Starting Application: OK cf@debian:~/rabbitmq-node$
Webブラウザで http://rabbitmq-node-creationline.cloudfoundry.com/ にアクセスします。
[ ] [送信] Messages
以上のようなフォームが表示されれば動作しています。例えば「TEST」とフォームに入力して「送信」ボタンを押すと、
[ ] [送信] Messages 1. TEST
となります。
