【實作筆記】利用Node.js及Socket.io製作簡易聊天室﹙下﹚
【實作筆記】利用Node.js及Socket.io製作簡易聊天室﹙下﹚
終於來到這個教學的最後一篇了,經過了前兩篇的教學文章,我們的聊天室現在有一個可以暫存聊天記錄的功能。不過這個雞肋的功能大家細心留意大概都會有個疑惑:「說好的紀錄呢?怎麼重啟伺服器後都是空白的?」。
相信現在大家對於Node.js和以及Socket.io的了解也不會陌生,接下來我就要和各位見見我們這篇教學的主角 — 資料庫。
現代網站多數都擁有一個儲存眾多資料的地方,我們稱之為資料庫。通常為一個伺服器程式,提供一個或多個連接介面給程式開發者與資料庫伺服器互動,例如資料新增、查詢、修改、刪除等,而這四項操作又稱之為CRUD,Create、Read、Update、Delete,是資料的常用操作。
今天我們將要採用的資料庫系統是MongoDB,我們將透過MongoDB Atlas 這個免費服務來帶大家幫我們的聊天室服務加上資料庫的儲存功能。
P.S. 之前網上有很多教學也是以mlab作為資料庫,但自從mlab合併到MongoDB Atlas之後,多多少少設定也有不一樣,所以就藉著這篇教學來教教各位。
所以現在,我們但要把資料存放在記憶體中,還要把它們放在資料庫中,一起來動手看看吧!
上回提要
步驟1: 申請帳號
使用MongoDB Atlas之前我們要先申請一個帳號,填寫基本資料抑或利用Google帳號申請便可。
步驟2: 建立組織和專案
申請好帳號並登入MongoDB Atlas服務之後,我們就要來建立一個組織和所屬的專案啦。
步驟3: 部署你的集群
設定完組織名稱跟專案名稱之後,理應可以看到Cluster﹙集群﹚
的設定。就如上圖一樣,之後你便可以選擇所需要的服務。
步驟4: 設定集群的基本資料
當在集群頁面按下Create
鍵之後,大家可以看到之後會有幾個選項,當中包括雲端伺服器供應商以及地區。你可以選擇AWS
、GCP
或Azure
,這邊就依個人喜好選擇就行了﹙我建議大家選AWS作為伺服器供應商而且地區選取美國地區,因為稍後我們會把網站擺放在Heroku上,而基於Heorku的設定,這樣資料傳輸的速度會比較快﹚。
不過第二項的Cluster Tiers
,大家將看到可用於共享集群選項的集群層。你可以查看各層之間的記憶體,存儲,虛擬處理器和基本價格的比較,以幫助您選擇合適的層。然而只有第一個M0 Sandbox
才是免費的,所以請選取預設的M0沙盒層。
之後也沒有什麼重要設定,集群名稱就隨便地跟它預設的好了。選好之後點右下方的Create Cluster
進入下一步。
步驟5: 建立數據庫使用者
Atlas要求客戶端作為MongoDB數據庫用戶進行身份驗證才能訪問群集,因此讓我們為您的群集創建一個快速的實例。
如下面的gif中看到的那樣,創建數據庫用戶非常簡單。首先,點選Database Access
的分頁標籤(位於左側導航欄中的Security
底下)。單擊Create new Database User
將出現提示,你可以在其中選擇該用戶的身份驗證方法和數據庫用戶特權。
選擇“Password”身份驗證方法,然後為該用戶提供用戶名和密碼。為了方便起見,你甚至可以直接在Atlas中自動生成安全密碼,我強烈建議各位往後可以這樣做。
但是在這個實作中,其實我也是可以隨隨便便的建立一個可以存取和寫入的使用者便可。
步驟6: 授予授權的IP地址訪問權限
設置群集的最後一步是選擇允許訪問哪些IP地址。要快速啟動並運行,請將您的群集設置為允許從任何位置進行訪問﹙設定為0.0.0.0/0﹚。
大概在MongoDB Atlas的設定基本上就完成了,而Database﹙數據庫﹚
和Collection﹙集﹚
可以不用設定,因為我們一會兒會利用程式自動生成。
終於要開始回到我們的正題啦!首先我們要先在專案資料夾內打開終端機安裝套件:
npm install --save mongoose
在這個教學中,我們選擇使用 Mongoose ODM 這個套件,而不使用 MongoDB 的原生套件的原因是因為我覺得有點難用,不過原生的套件比較單純且也比較貼近 MongoDB 的原生用法,所以算是各有優劣啦。
建立連線
裝好套件之後,在我們先前的聊天室專案資料夾中開一個新檔案叫做db-connector.js
,然後輸入以下程式:
const mongoose = require('mongoose');
mongoose.connect('這邊改成MongoDB Atlas資料庫位址');
const db = mongoose.connection;
db.on('error', console.error.bind(console, 'connection error:'));
db.once('open', function() {
console.log('connected!');
});
而MongoDB Atlas資料庫位址就在Cluster
頁面中點撃Connect
按鈕,然後選擇Collect your application
,接著複製位址到上方程式內。 你看到的位址格式應該是這樣:
mongodb+srv://<數據庫使用者名稱>:<數據庫使用者密碼>@cluster0.<隨機生成專屬碼>.mongodb.net/<數據庫名稱>?retryWrites=true&w=majority
在修改的時候,記得要去掉<>
的符號,而數據庫名稱你在這個實作中可以隨便取,另外Collection
之後將會以messages
作為名稱。
然後試著執行看看,應該會看到一個connected!的訊息,如果沒有的話,檢查一下<數據庫使用者名稱>
和<數據庫使用者密碼>
有沒有輸入錯誤。
一切就緒沒問題的話,我們將這個程式修改成:
const mongoose = require('mongoose');
mongoose.connect('這邊改成MongoDB Atlas資料庫位址');
module.exports = mongoose.connection;
這樣這個連線資訊才能方便地在其他程式間使用。
資料模型設計
Mogoonse的Data Model資料模型特性可以讓你在資料庫的設計上更為嚴謹些。因為在原始的 MongoDB設計中,資料的型別可以是任意值,而這往往會造成不預期的資料內容。雖然原生的 MongoDB也有提供資料模型可以做型別驗證,但Mongoose提供的方法更簡單好用。
雖然這個在我們單純的聊天室中用途不大,不過透過這樣的設計,我們可以比較好掌握我們到底存了什麼樣的資料到資料庫中,未來除錯也會方便許多。雖然這會失去MongoDB自由儲存內容的特性就是了。那麼在建立資料模型前,我們得先定義資料表﹙詳細可參考:Schema﹚。
在聊天室專案資料夾中建立一個schema.js
的檔案,並把以下內容輸入:
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
module.exports = new Schema({
name: { // 欄位名稱
type: String, // 欄位資料型別
required: true, // 必須要有值
},
msg: {
type: String,
required: true,
},
time: {
type: Date,
required: true
}
});
我們在這個檔案中定義了資料儲存的基本規範,我們要求了三個欄位,並且指定他們的資料型別﹙type﹚
,然後都設定成必要輸入的內容,不可缺少。
設定可以參考官方的文件:http://mongoosejs.com/docs/validation.html
存入與取出資料
定義完聊天室的資料模型後,我們要來寫程式將聊天資料從伺服器記憶體存到資料庫中。
打開records.js
,加入這些程式碼:
const {EventEmitter} = require("events");
// 加入下面這些
const mongoose = require('./db-connector');
const schema = require('./schema');
const Message = mongoose.model('Message', schema);
然後修改push
method:
// 將聊天資料轉成資料模型
const m = new Message(msg);
// 存至資料庫
m.save();
this.emit("new_message", msg);
if (data.length > MAX) {
data.splice(0, 1);
}
再來是get
method:
get (callback) {
// 取出所有資料
Message.find((err, msgs) => {
callback(msgs);
});
}
最後修改index.js
:
// socket.emit("chatRecord", records.get()); // 砍掉這行
// 改成下面這個
records.get((msgs) => {
socket.emit("chatRecord", msgs);
});
好了!這樣你就得到了一個聊天資料是存到資料庫中的聊天室了!
趕快啟動伺服器試試看,輸入幾個聊天資料後,關閉伺服器再重啟,看看先前輸入的聊天資料會不會再一次出現吧~
刪除多餘資料
等等,我們當初在設計聊天記錄的功能時,有加入最大上限﹙MAX﹚的設計,那麼資料庫也應該要把這問題考慮進去。
讓我們回到records.js
的push
method:
// 刪除這段
if (data.length > MAX) {
data.splice(0, 1);
}
// 加入這段
// 取得資料庫中有多少筆紀錄
Message.count().then((count) => {
if (count >= MAX) {
Message.find().sort({'time': 1}).limit(1).then((res) => { // 找到最舊的那個訊息
Message.findByIdAndRemove(res[0]._id); // 然後移除
});
}
});
好了,現在你不需要擔心資料庫會塞太多歷史垃圾而變得累贅了!
Heroku設定
請不要馬上離開,現在我還有重點的心得!
當各位興高采烈以為已經弄好之後,就一直在locahost
玩耍,但卻忘了上載到網上給大家一起用才是當中的重點,而這件事情卻連原作者也沒有提及到。當你申請完帳號而且將專案推送上GitHub
之後,其實建立Heroku
的專案相當簡單,所以那些教學大家可以自己查找,我也不在這裡長篇大論。
然而,要提醒大家的是我們之前在index.js
所設立的port
本應是80
,但是在要放到Heroku
的時候請各位必須把它改回3000
,不然在成功deploy之後網站會出現getting "Application Error"
的警告。這是因為它的設定是預設在3000的埠。
另外,在推送到GitHub
後大家可能會收到警告電郵指專案不安全,皆因大家擺放了含有帳號密碼的MongoDB Atlas資料庫連結在db-connector.js
。解決方法也十分簡單,首先把連結的項目改為以下代碼:
const uri = process.env.MONGODB_URI;
然後按照下圖在Heroku
的專案設定上加入MONGODB_URI
以及連結作為關鍵碼,這樣就可以避免別人在你的程式碼上找到你的數據庫資料了。
結語
這一篇也算是系列文的最後一篇了。這三篇文章從什麼都沒有到作出一個擁有記錄功能的聊天室,雖然功能還是很單純但它卻因為簡單而變得輕巧。
希望這幾篇文章能給予你任何對於 Node.js、Socket.io、MongoDB 學習上的一點幫助。
Online Demo:https://simplechat-service.herokuapp.com/
GitHub Source:https://github.com/alvinau0427/SimpleChat
P.S. 此教學原作者為single9,我也是加插一些小細節跟心得,所以先在此感謝他網上的教學