Node.js: ซอฟแวร์ทำงานร่วมกันแบบไร้รอยต่อด้วย RabbitMQ

ความต้องการซอฟแวร์

– จาก ซอฟแวร์ CRM พื้นๆ ของบล็อค Node.js: ติดต่อกับฐานข้อมูล MongoDB… Action! ที่มีการเก็บข้อมูลลูกค้าได้แล้วนั้น อยากให้มีส่วนนำเสนอที่จะ แสดงข้อมูลใหม่ทุกครั้งที่มีการเปลี่ยนแปลงด้วย แม้ว่าจะอยู่ในขณะที่เปิดหน้าแสดงข้อมูลนี้อยู่ก็แสดงข้อมูลที่สดใหม่ให้ด้วยอยู่เสมอ

ติดตั้งและ start MongoDB Server

– อ่านบทความ Node.js: ติดต่อกับฐานข้อมูล MongoDB… Action! เพื่อติดตั้ง และทำความเข้าใจซอฟแวร์ เริ่มต้นก่อน เพราะบทความนี้ผมจะต่อเติมจากของเดิมครับ

– ซอฟแวร์ที่ผมจะสร้างขึ้นมา เพื่อทำงานตามความต้องการนั้น มีรูปแบบมาจากสถาปัตยกรรม CQRS จากแนวคิดนี้ผมวางแบบ ซอฟแวร์ application server ต่างๆ ออกมาได้ตามภาพข้างล่างนี้

 software-architecture-5

– จากภาพจะเห็นได้ว่า ผม start MongoDB ให้เป็น replica แบบ master/slave ด้วย  เอาละเรามา start MongoDB แบบ master/slave  ตามภาพกันก่อน

– start master node สำหรับ master node นี้ผมกำหนดให้เปิด port 27117 เพื่อไม่ให้ไปชนกับ port defualt ที่จะเปิดเป็น slave node บนเครื่องเดียวกัน ก่อนอื่นให้สร้าง folder C:\CRM\master-db  แล้วเปิด Windows PowerShell ขึ้นมา พิมพ์คำสั่งตามข้างล่างนี้ลงไป แล้วกด enter

mongod –master –port 27117 –dbpath C:\CRM\master-db

– start slave node ก็สร้าง folder C:\CRM\slave-db เปิด Windows PowerShell อีกอันขึ้นมา พิมพ์คำสั่ง ตามนี้ลงไป แล้วกด enter

mongod –slave –source localhost:27117 –dbpath C:\CRM\slave-db

เมื่อ start MongoDB master/slave node ได้แล้วจะแสดงหน้าจอคล้ายๆภาพของผมแบบนี้

 start-master-slave-1

ติดตั้งและ start RabbitMQ Server

– download และติดตั้ง Erlang ก่อน เพราะว่า RabbitMQ run อยู่บน Erlang runtime

– download และติดตั้ง RabbitMQ

– ติดตั้ง rabbitmqadmin plugin ไว้ใช้จัดการ RabbitMQ ผ่านหน้าจอที่เป็น Web ได้ง่ายขึ้น ไปที่ start พิมพ์ว่า “rabbitmq” เลือก Rabbit Command Prompt ตามภาพนี้

install-rabbitmq-1

– เมื่อขึ้นหน้าจอ console จอดำๆขึ้นมาแล้ว ให้พิมพ์คำสั่ง ตามนี้ลงไป แล้วกด enter ครับ

rabbitmq-plugins enable rabbitmq_management

หลังจากที่ติดตั้ง plugin rabbitmqadmin เสร็จแล้ว เราต้องทำการ restart rabbitmq server ด้วย พิมพ์คำสั่ง ให้มัน stop ก่อน

rabbitmq-service stop

ต่อจากนั้นให้พิมพ์คำสั่ง start มันซะเลย ด้วยคำสั่งนี้

rabbitmq-service start

คูภาพประกอบด้านล่าง

install-rabbitmq-2

ทดลองเปิดดู rabbitmqadmin กันดีกว่า เปิด browser IE หรือ Chrome ขึ้นมาแล้วเข้าไปที่

http://localhost:15672

จะพบหน้า login ให้ใส่ guest เป็น username/password ลองเข้าไปเล่นได้เลย ตามภาพข้างล่าง

install-rabbitmq-done

ถึงตอนนี้เราก็มี RabbitMQ Message Broker ซึ่งมันจะเป็นสื่อกลาง ที่ใช้โปรโตคอล AMQP สำหรับการติดต่อสื่อสาร หรือใช้เป็นสื่อกลาง(mediator)เพื่อ integration กันระหว่าง ซอฟแวร์ application ทั้งหมดในระบบของเราแล้ว

OK ซอฟแวร์ที่เป็น infrastructure ของผมก็พร้อมแล้ว สรุปว่าผมได้ start ซอฟแวร์ MongoDB 2 server อันได้แก่ master node ซึ่งเป็น server เพื่อให้ซอฟแวร์ CRM CMD(Command) Processor เข้ามาจัดการเก็บ แก้ไขข้อมูล และ slave node เปิดเพื่อให้ซอฟแวร์ CRM UX(User Experience) Processor เข้ามาอ่าน หรือคิวลี่ข้อมูลได้ และเร็วขึ้นด้วย เพราะว่าผมได้แยก node งานสืบค้นข้อมูลกับ node งานจัดการข้อมูลไว้แล้ว

กับ start ซอฟแวร์ RabbitMQ server ไว้เพื่อเป็นสื่อกลางสื่อสารข้อมูล หรือ integration กันของซอฟแวร์ ทั้ง 2 เข้าด้วยกัน ลำดับต่อไปเราก็มาลงมือทำซอฟแวร์ CRM CMD Processor และ  CRM UX Processor กันเลย

ติดตั้งพร้อมแล้ว ก็มาประดิษฐ์ซอฟแวร์ กันเถอะ

– ไปที่ folder C:\CRM ที่ได้สร้างไว้แล้วตอน start MondoDB

– เพิ่ม file package.json ไว้ใน folder CRM  เปิดด้วย notepad แล้วพิมพ์คำสั่งนี้ลงไป

{

“name”: “CRM-MongoDB-RabbitMQ”,
“description”: “Node.js: ซอฟแวร์ทำงานร่วมกันได้ไรขีดจำกัดด้วย RabbitMQ”,
“version”: “0.0.0”,
“private”: true,
“dependencies”: {

“mongoose”: “*”,
“amqp”: “*”

}

}

– ติดตั้ง node module ที่ต้องการด้วย npm เปิด Windows PowerShell ขึ้นมา แล้ว cd ไปที่ drive C:\CRM ที่เราสร้าง file package.json ไว้ แล้วพิมพ์คำสั่ง

npm install

คำสั่งข้างต้นมันจะทำการ download และ install module mongoose และ amqp  ตามต้องการที่ได้ config ไว้แล้วที่ fle package.json ถ้าแสดงผลตามภาพข้างล่างนี้เหมือนผม เป็นอันว่าเราทำงานนี้สำเร็จ

npm-install-package-1

– ผมจะชอบออกแบบ Domain Model ก่อนที่จะลงมือสร้างซอฟแวร์ ผมก็จะสร้าง CRM Domain ก่อน จากความต้องการ ข้อมูลลูกค้า เพิ่ม file CRM.js ลงไปใน folder CRM อีกอันหนึ่ง แล้วเขียน javascript ตามข้างล่างนี้ลงไป

var mongoose = require(‘mongoose’);

var customerSchema = mongoose.Schema({

firstName: { type: String, required: true, trim: true, index: true },
lastName: { type: String },
shortName: { type: String },
company: String,
tag: String,
address: {

houseNo: String,
soi: String,
subDistric: String,
distric: String,
province: String,
postcode: Number,
country: String

},
website: String,
jobPosition: String,
phone: String,
mobile: String,
fax: String,
email: String,
title: String,
birthDate: Date,
createdDate: { type: Date, default: Date.now },
modifiedDate: { type: Date, default: Date.now }

});

customerSchema.methods.getFullName = function () {

return this.firstName + ‘ ‘ + this.lastName;

}

Customer = mongoose.model(‘Customer’, customerSchema);

– เพิ่ม file crm-cmd-processor.js โดยมันจะรับคำสั่งจากช่องสัญญาณ command ได้แก่ create, update และ delete จาก CRM UX Processor ที่จะรับคำสั่งมาจากผู้ใช้ และอีกหน้าที่รับผิดชอบของมันก็คือ จะจำลองเหตุการณ์โดยการสุ่มเอาข้อมูล customer มาจากฐานข้อมูล crm แล้วดำเนินการแก้ไข field modifiedDate ให้เป็นปัจจุบันเท่านั้นก่อน ให้สมือนแค่ว่ามีการแก้ไขข้อมูลครับ หลังจากที่ update เสร็จก็ให้มันกระจายสัญญาณด้วยคำสั่ง publish ไปที่ exchange crm โดยจะ route คำสั่งเป็น refresh เพื่อให้ ซอฟแวร์ CRM UX Processor ซึ่งเป็นส่วนแสดงผล และติดต่อสือสารกับผู้ใช้โดยตรงนั้นได้ เห็นข้อมูลที่เปลี่ยนแปลงไปแล้วแบบทันทีทันใด หรือในลักษณะใกล้เคียง real-time ได้มากที่สุด เอาละเขียน code ตามข้างล่างนี้ลงไปที่ file crm-cmd-processor.js ครับ

require(‘./CRM.js’);

var mongoose = require(‘mongoose’);
var connection = require(‘amqp’).createConnection();

mongoose.connect(‘mongodb://localhost:27117/crm’, function (err) {

if (err) {

console.log(‘เราไม่สามารถ connect ไปยัง mongo ได้’);

}

});

connection.on(‘ready’, function () {

console.log(‘Connected to ‘ + connection.serverProperties.product);
var e = connection.exchange(‘crm’, {durable: true});
var q = connection.queue(‘command’, { autoDelete: false });

q.on(‘queueDeclareOk’, function (args) {

console.log(‘Queue Command Processor OK’);
q.bind(e, ‘create’);
q.bind(e, ‘update’);
q.bind(e, ‘delete’);

q.subscribe({ ack: true }, function (message, headers, deliveryInfo) {

console.log(‘Received Command create firstName: ‘ + message.firstName);
if (deliveryInfo.routingKey === “create”) {

var newCust = new Customer({

firstName: message.firstName

});

newCust.save(function (err, newCust) {

if (!err) {

console.log(‘Save OK’);
console.log(‘new ‘ + newCust.firstName);
e.publish(‘refresh’, newCust);

}

});// end save

}// end if
q.shift();

});// end q subscribe

});// end q on

setInterval(function () {

Customer.find({}, function (err, custs) {

if (!err) {

if (custs.length > 0) {

var index = Math.floor(Math.random() * custs.length);
var cust = custs[index];

cust.modifiedDate = new Date();
cust.save();
console.log(‘updated ‘ + cust.firstName + ” ” + cust.modifiedDate);
e.publish(‘refresh’, cust);

} else {

console.log(‘wait for update customer…’);

}

}

});// end Customer find

}, 1000);

});//end connection on

– start crm-cmd-processor.js กันครับ เปิด Windows PowerShell ขึ้นมาแล้ว cd ไปที่ folder C:\CRM พิมพ์

node crm-cmd-processor.js

หลังจากกด enter แล้วจะแสดงหน้าจอแบบข้างล่างนี้

run-crm-cmd-processor-1

ซอฟแวร์ CRM CMD Processor จะ simulate ทุกๆ 1 วินาที เพื่อเป็นการจำลองการทำงานโดยการ update ข้อมูล customer modifiedDate ให้เหมือนว่ามีการ update ข้อมูลไปก่อน ลำดับต่อไปผมก็จะสร้างซอฟแวร์ที่เป็นส่วนนำเสนอ หรือแสดงผล เพื่อประสานงานกับผู้ใช้

– เพิ่ม file crm-ux-processor.js โดยผมจะให้มันทำงานค้นข้อมูลจาก MongoDB slave node เพื่อแสดงผล เมื่อมีการเปลี่ยนแปลงข้อมูล Customer ที่จะถูกส่งเหตุการณ์ refresh ผ่านสื่อกลาง RabbitMQ มาจาก CRM CMD Processor ที่ได้ start ไว้แล้วนั้น และ CRM UX Processor นี้ผมก็ให้มันรับ argument ชื่อผู้ใช้ เพื่อใช้สร้าง customer ขึ้นมาใหม่โดยอัตโนมัติไปก่อน โดยมันจะส่ง คำสั่ง create เพื่อสร้างข้อมูล customer ใหม่นี้แล้ว pubslish ไปยัง exchange crm โดยกำหนด route key เป็น create ซึ่งมันจะถูกส่งคำสั่งนี้ไปยัง queue cammand ที่ crm-cmd-processor ได้สร้าง และผูกไว้อยู่บน RabbitMQ เพื่อรอรับคำสั่งไว้แล้วนั่นเองครับ เขียน javascript ตามข้างล่างนี้ลงไป

if (!process.argv[2]) throw new Error(‘please defind username…’);

var userName = process.argv[2];

require(‘./CRM.js’);
var mongoose = require(‘mongoose’);
var connection = require(‘amqp’).createConnection();

mongoose.connect(‘mongodb://localhost/crm’, function (err) {

if (err)  console.log(‘เราไม่สามารถ connect ไปยัง mongo ได้’);

});

connection.on(‘ready’, function () {

console.log(‘Connected to ‘ + connection.serverProperties.product);
var e = connection.exchange(‘crm’, { noDeclare: true });
var q = connection.queue(‘display-‘ + userName, { exclusive: true });

q.on(‘queueDeclareOk’, function (args) {

console.log(‘Hello ‘ + userName);
q.bind(e, ‘refresh’);

q.subscribe({ ack: true }, function (message, headers, deliveryInfo) {

console.log(‘Received Refresh Command firstName: ‘ + message.firstName);
Customer.findOne({ firstName: message.firstName }, function (err, cust) {

if (!err) {

if (cust) {

console.log(“\tmodifiedDate: ” + cust.modifiedDate);

}

}

});//end Customer findOne
q.shift();

});// end q subscribe

});// end q on

Customer.findOne({ firstName: userName }, function (err, cust) {

if (!err) {

if (!cust) {

var newCust = new Customer({

firstName: userName

});

e.publish(‘create’, newCust);

}

}

});//end Customer findOne

});// end connection on

จาก code javascript crm-ux-processor.js ข้างบน เรามา start มันกัน เปิด Windows PowerShell ขึ้นมาใช้คำสั่ง cd เพื่อ move shell ของเราไปที่ folder c:\CRM ครับ แล้วพิมพ์คำสั่งข้างล่างนี้ลงไป เพื่อ start ซอฟแวร์ CRM UX Processor

node crm-ux-processor.js Sam

เมื่อกด enter แล้วจะแสดงหน้าจอคล้ายๆของผมแบบนี้

run-crm-ux-processor-sam

จากคำสั่งตามหน้าจอที่แสดงอยู่ด้านบนนั้น ผมพิมพ์ username Sam เป็น argument แรกให้กับซอฟแวร์ CRM UX Processor คุณจะเห็นว่ามันได้รับคำสั่งให้ refresh ข้อมูลกลับมาทุกๆ 1 วินาทีตามที่ผมได้ simulate ไว้แล้วที่ CRM CMD Processor เอาละผมจะ เพิ่ม ลูกค้า เข้าไปอีก 2 คน เปิด Window PowerShell ใหม่ขึ้นมาอีก 2 หน้าครับแล้วให้ cd ไปที่ folder c:\CRM พิมพ์คำสั่ง เพื่อ start crm-ux-processor.js โดยพิมพ์ชื่ออะไรก็ได้ลงไป แล้วหน้าจอของคุณ จะขึ้นเหมือนผมคล้ายๆ แบบนี้หรือเปล่า

run-crm-ux-processor-dean&booby

สังเกตุจากหน้าจอคู่ที่ผมได้ start ไปนั้น คุณจะเห็นว่า ซอฟแวร์ CRM CMD Processor ได้ทำการสุ่ม Customer Sam, Dean และ Boobby จากฐานข้อมูล CRM ขึ้นมาดำเนินการแก้ไข เสร็จแล้วให้ publish คำสั่ง refresh ซึ่งคำสั่งนี้ ได้ถูกผูกไว้กับช่องสัญญาณ หรือ queue โดย CRM UX Processor ที่ได้ start ขึ้นมาแล้วก็คือ Sam, Dean และ Boobby เพื่อให้ refresh ข้อมูลแล้วแสดงผลที่สดใหม่อยู่เสมอ แม้ว่าจะเปิดหน้าจอไว้ตามที่เราต้องการนั่นเอง

ผมก็ขอจบบล็อค  Node.js: ซอฟแวร์ทำงานร่วมกันแบบไร้รอยต่อด้วย RabbitMQ ไว้แต่เพียงเท่านี้ ก็ยาวพอสมควรนะครับ ในครั้งต่อไป ผมก็จะมาทำหน้าจอที่เป็น Web จริงๆกันด้วย Node.js เพื่อใช้เป็นส่วนสื่อประสานคำสั่งการทำงานกับผู้ใช้ได้ง่ายขึ้น สำหรับบล็อกนี้ก็เพื่อแสดงลักษณะซอฟแวร์การทำงานง่ายๆ เพื่อให้มันทำงานให้เวอคร์ตามความต้องการพื้นฐานให้ได้ก่อน แล้วเราจะมาเติมแต่ง พัฒนาซอฟแวร์นี้ให้มีลักษณะที่เจ๋งยิ่งขึ้น มีประโยชน์ยิ่งขึ้นต่อไปกันครับ

สำหรับ SourceCode ของบล็อคนี้ จะอยู่ที่ folder CRM-2 ครับ

ขอบคุณครับ

#:P

Advertisements

#amqp, #cqrs, #event-driven, #message-broker