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
となります。