icon picker
Node

Nodejs là một flatform để thiết kế phần mềm, đặt biệt là webserver. Chương trình được chạy trên node sẽ được viết bằng JS. Node chạy trên nền của Chrome's Javascript runtime Engine cho phép việc xây dựng ứng dụng web server nhanh, dễ dàng mở rộng. Node.js sử dụng kĩ thuật điều khiển theo sự kiện (event - driver) và nhập xuất bất đồng bộ (non-blocking I/O) giúp nó nhẹ và efficient, hoạt động tốt cho các ứng dụng sử lý dữ liệu data-intensive realtime.
Node bao gồm V8 JS engine của Google và libUV và một số thư viện khác. Nó sử dụng kĩ thuật CommonJS. Ngoài ra nó còn cung cấp môi trường REPL kiểu shell language như python.
Mã nguồn webserver đơn giản
const http = require('http');
const hostname = '127.0.0.1';
const PORT = 3000;

const server = http.createServer((req, res) => {
res.statusCode = 200;
res.setHeader('Content-Type', 'text/plain');
res.end('Hello World');
});

server.listen(port, hostname, () => {
console.log(`server running at http://${hostname}:${PORT}/`);
});
Một ứng dụng web Nodejs có thể xử một lúc nhiều request nhờ kết hợp của event loop và stack call của engine.
Với mỗi connection được xử lí, callback sẽ được gọi, các connection tới sau có thể sẽ chờ trong event loop tới ghi call stack của connection hiện tại xử lí xong sẽ là connection tiếp theo nằm trong queue. Và Event loop sẽ cứ chạy từ dòng code đầu tiên tới cuối cùng., nếu không có connection nào Nodejs sẽ đơn giản là sleep và để event loop chạy.
// Giải thích sau ...

Cách hoạt động của node không giống với các mô hình web server hiên nay (Các mô hình hiện nay sử dụng cơ chế thread base, gồm nhiều thread để xử lí đồng thời các request của client) vì các mô hình đó rất khó sử dụng và không hiểu quả lắm. Trong Nodejs chỉ có 1 thread duy nhất → không lo bị deadlock bởi vì trong Nodejs nó không có lock và rất ít thứ trong node chạy mà lock chương trình. → Dễ dàng mở rộng.
Ngoài webserver HTTP thì Node còn làm được nhiều thứ như là máy chủ TCP chẳng hạn
var net = require('net');

net.createServer(function(stream) {
stream.write('hello\r\n');
stream.on('end', function(){
stream.end('goodbye\r\n');
});

stream.pipe(stream);
}).listen(7000);

Event-Loop, Callback queue, Call-Stack, ... etc ...

image.png
Call-Stack
Là thành phần trong bộ nhớ runtime, chưa thông tin về một subroutine - tập các cấu lệnh giúp chạy một task cụ thể - của chương trình, call stack là kiến trúc trong hầu hết các ngôn ngữ. Vì theo cấu trúc stack → LIFO, các hàm được gọi vào sau sẽ nằm trên cùng của call stack và được sử lí trước, rồi mới xuống dần tới cuối stack. Thường khi chạy một chương trình hàm main() sẽ là hàm nhảy vào stack đầu tiên → Hàm main chạy xong thì cũng là lúc chương trình kết thúc. Nếu trong quá trình chạy hàm main() mà gặp một hàm khác hoặc một opration nào khác thì nó sẽ được push vào call stack và xử lí (nếu là hàm con thì sẽ nhảy vào load hàm con đó, nếu là operation thì xử lí nó). Mỗi một thread có thể chứa một call stack cho thread đó.
Javascipt là một ngôn ngữ chỉ một 1 thread (single thread), đồng nghĩa với việc nó chỉ có một call-stack → Trong 1 thời điểm chỉ làm được một việc.
Blocking
Ta đã thấy call-stack chỉ có thể làm được một việc một lúc 😱😱😱, vậy nếu bị blocking thì sao - môt hàm hay một chứ năng nào đó chạy chậmm (for loop 1 → 1B, request network...), thì sẽ ra sao → Call-Stack sẽ bị đứng → App sẽ bị treo cho tới khi task đó hoàn thành (╯°□°)╯. → Nghĩa là ta không thể làm gì được hết, nếu trên browser thì nó sẽ đứng máy, nếu trên node thì nó cũng đứng app luôn. Vậy làm sao 😭😭😭
Async call-back
Ta sẽ quăng cho nó một hàm call-back tới khi nào xong thì nó sẽ gọi hàm đó và xử lí đúng không. Quan trong là nếu đưa vào bức tranh toàn cảnh ở trên, thì hàm call-back của chúng ta sẽ nằm ở đâu. Ở đây là lúc các thành phần như web API (trong node là c++ api) và event loop và callback queue hoạt động.
Các hàm Async hay Promise or setTimeout là thuộc phần của API do Node hoặc trình duyệt cung cấp, nó chạy trong một thread khác với runtime của chúng ta.
Khi nó chạy xong, nó sẽ đưa hàm call-back vào trong (enqueue) callback queue.
Event-loop sẽ có việc là nó sẽ cứ loop, khi trong runtime call-stack rỗng → nó sẽ đưa thứ đầu tiên (dequeue) nằm trong callback queue vào trong call-stack (push).

ES6 trong nodejs và express

sử dụng babel-node cli npm install -g babel-cli
nhớ là cùng dùng thêm npm install -save-dev babel-eslint
// .eslintrc
{
"parser":"babel-eslint",
"env":{
"node":true,
"es6": true
},
"rules":{
"vars-on-top":2,
"no-undef":2
}
}
file app.js
"use strict";
import express from 'express';
import path from 'path';
import favicon from 'serve-favicon';
import logger from 'morgan';
import cookieParser from 'cookie-parser';
import bodyParser from 'body-parser';

import routes from './routes/index';
import users from './routes/users'

//using let
let app = express();

app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'jade');

app.use(logger('dev'));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
app.use(cookieParser());
app.use(express.static(path.join(__dirname, 'public')));

app.use('/', routes);
app.use('/users', users);

// using arrow syntax
app.use((req, res, next) => {
let err = new Error('Not Found');
err.status = 404;
next(err);
});

if (app.get('env') === 'development') {
app.use((err, req, res, next) => {
res.status(err.status || 500);
res.render('error', {
message: err.message,
error: err
});
});
}

app.use((err, req, res, next) => {
res.status(err.status || 500);
res.render('error', {
message: err.message,
error: {}
});
});


module.exports = app;
//file package.json
{
"name": "es6-express",
"version": "0.0.0",
"description": "ES6/ES2015 Express Example",
"repository": "n/a",
"license": "MIT",
"author": "Steven",
"scripts": {
"lint": "eslint app.js",
"start": "babel-node ./bin/www",
"auto-start": "nodemon --exec \"npm run lint && npm start\" --ignore public/js"
},
"dependencies": {
"body-parser": "~1.13.2",
"cookie-parser": "~1.3.5",
"debug": "~2.2.0",
"express": "~4.13.1",
"jade": "~1.11.0",
"morgan": "~1.6.1",
"serve-favicon": "~2.3.0"
},
"devDependencies": {
"babel": "^5.8.21",
"babel-eslint": "^4.0.10",
"eslint": "^1.1.0",
"nodemon": "^1.4.1"
}
}

Cách cung cấp static file
app.use(express.static(__dirname + '/public'));
*NOTE:
.env là file bash → không đuoc chứa khoảng trắng → MONGO = ABC là lỗi mà phải viết sát vào.

Các tính năng học thêm được

Các package hay ho

Dùng thư viện thêm file rất dễ dàng.
Nếu muốn Id trong mongodb ngắn hơnー > dùng
dns module của node, cho khả năng check trang web
Để dns lookup được thì không nên có http hoặc https ...
Tách url → regex = /^(.*:)\/\/(.*)/;

Cách tạo trường tự tăng trong mongodb.

onst mongoose = require('mongoose');

const counterSchema = mongoose.Schema({
_id: {type: String, required: true},
seq: {type: Number, default: 0}
});

const Counter = mongoose.model('counter', counterSchema);

//Counter({ _id: 'shortLinkShortUrl'}).save();

const shortLinkSchema = mongoose.Schema({
original_url: {type: String},
short_url: {type: Number}
});

shortLinkSchema.pre('save', function(next){
const doc = this;
Counter.findByIdAndUpdate(
{ _id: 'shortLinkShortUrl'},
{ $inc: {seq: 1}},
function(error, counter){
if(error)
return next(error);
doc.short_url = counter.seq;
next();
}
);
});

const ShortLink = mongoose.model('short-link', shortLinkSchema);

module.exports = ShortLink;
Tạo một model chưa các số thứ tự hiện tại của mỗi opject. (nhớ tạo document)
dùng hook pre của schema.

Người ta làm link thu gọn kiểu gì.

Lấy link → lưu vào database cùng với chuỗi short của nó
Khi truy cập vào link short thì redirect tới đường dẫn chính.
Để redirect ra một service , website khác hệ thống → cần phải có thêm ‘http’ và ‘https’
Want to print your doc?
This is not the way.
Try clicking the ⋯ next to your doc name or using a keyboard shortcut (
CtrlP
) instead.