Bài 3: Stream và Buffer trong NodeJS - bạn đã nắm được những khái niệm này?

Xin chào các bạn, hôm nay sẽ là một bài mới trong series nodejs của mình. Và hôm nay chúng ta sẽ đi tìm hiểu về Stream và Buffer nhé 😄

Binary data

Ngày bé khi xem phim ma trận bạn có nhớ sẽ có những đoạn xuất hiện các dãy số 0, 1 chạy loằng ngoằng không? Vâng, đó chính là binary data (hay còn gọi là nhị phân). Máy tính lưu trữ và biểu diễn dữ liệu trong các tệp nhị phân. Nhị phân là một tập hợp các số 0 và 1 . Ví dụ: Ta sẽ đưa chuỗi viblo sang kiểu nhị phận thì sẽ có được là: 01110110 01101001 01100010 01101100 01101111

Sau đây là hình ảnh của bộ ký tự ASCII

Như trong bảng mã thì bạn có thể thấy chữ v sẽ tương ứng với 1110110, nhưng chúng ta lại có thêm số 0, vì ASCII chính xác là mã 7-bit (nghĩa là dùng kiểu bit biểu diễn với 7 số nhị phân), nhưng nhiều máy tính dùng nhóm 8-bit nên đã có bit thứ 8 được dùng bit chẵn-lẽ (parity) để kiểm tra lỗi trên các đường thông tin hoặc kiểm tra các chức năng đặc hiệu theo tiết bị (có những thiết bị sẽ thiết lập bit thứ 8 là 1)

Stream

Stream có thể hiểu là 1 chuỗi những mảnh dữ liệu được lắp ghép lại với nhau để tạo ra dòng chảy dữ liệu và chúng được tách ra vận chuyển với một đoạn dữ liệu gọi là chunk. Chunk là 1 đoạn dữ liệu được truyền qua 1 stream, diữ liệu được cắt ra thành những mảng chunks và chuyển đi. Ví dụ, ta có 1 file có kích thước 128MB sẽ được tách ra thành 4 mảnh 32MB và chuyển đi

Đây chính là cách mà NodeJS dùng để xử lý lượng lớn dữ liệu. Giả sử bạn có 1 lượng dữ liệu vô cùng lớn phải xử lý, nhưng bạn sẽ không cần thiết phải đợi tất cả các dữ liệu được đọc mà có thể xử lý từng phần riêng biệt

Chúng ta sẽ có 4 loại stream:

  • Readable stream: Tạo ra một stream để đọc
  • Writeable stream: Tạo ra một stream để ghi
  • Duplex stream: Tạo ra stream vừa để đọc và ghi trong cùng một thời điểm
  • Transform stream: Giống như duplex nhwung dữ liệu có thể được thay đổi trong quá trình đọc và ghi

Giờ bạn hãy tạo 1 file README.txt khoảng 64kb hoăc hơn nhé. Sau đó tạo file app.js

const fs = require('fs');

const readable = fs.createReadStream(__dirname + '/README.txt', {
    encoding: 'utf8',
    highWaterMark: 32 * 1024
});

const writeable = fs.createWriteStream(__dirname + '/READMECOPY.txt');

readable.on('data', (chunk) => {
    console.log(chunk.length);
    writeable.write(chunk);
});

Ở đây nghĩa là chúng ta đang đọc từng 32kb một chứ không đọc từng lúc cả file. Giờ hãy chạy node app.js và bạn sẽ thấy một file READMECOPY.txt được tạo ra, và hãy nhìn phần log xem, file của mình đã được chia nhỏ ra và đọc từng phần

Buffer

Chúng ta đã hiểu được stream là một chuỗi dữ liệu đi từ điểm này tới điểm khác. Nhưng chính xác thì nó di chuyển như thế nào? Giờ ví dụ bạn đang xem phim, thì cả một bộ phim đó sẽ là dữ liệu. Tưởng tượng sau một ngày làm việc căng thẳng, PM dí deadline, teammate mỉa mai vì không có ny, bạn về nhà và muốn xem một phim nào đó để giải trí , nhưng bạn sẽ phải đợi toàn bộ dữ liệu load ra thì mới có có thể xem được

Nhưng hãy yên tâm, bạn sẽ không phải trải qua cảm giác như vậy, vì đã có buffer. Buffer là một nơi chứa dữ liệu tạm thời đang được di chuyển từ điểm này tới điểm khác với kích thược xác định và giới hạn. Thế quá trình mà load phim kia sẽ được xử lý như thế nào? Dữ liệu của bộ phim không được load toàn bộ mà sẽ chỉ được load từng phần thông qua stream

Nếu tốc độ dữ liệu đến nhanh hơn tốc độ xử lý, thì dữ liệu sẽ phải được đợi ở đâu đó để hoàn thành được quá trình xử lý. Mặt khác, nếu quá trình xử lý nhanh hơn, nghĩa là sẽ có một lượng dữ liệu đã được đưa tới và lượng còn lại chưa tới, thì những dự liệu tới trước lại phải đợi ở đâu đó trước khi được gửi đi để xử lý. Và đâu đó chính là buffer. Đó sẽ là một vị trí vật lý nhỏ trong máy của bạn, thương sẽ là RAM, nơi dữ liệu thạm thời sẽ được gom lại, chờ đợi và cuối cùng được gửi đi và xử lý trong stream

Hiểu nôm na thì stream và buffer như một nhà xe. Xe sẽ đợi đủ khách hoặc tới một thời gian cụ thể thì mới đi. Khách có thể tới vào thời điểm khác nhau, tốc độ khác nhau. Cái này thì nhà xe và khách đều không thể kiểm soát được. Với những khách đến sớm thì sẽ phải ngồi đợi tới khi xe khởi hành. Còn với những khách đến muộn sẽ phải đợi chuyến sau.

Trong bất kỳ trường hợp nào, luôn luôn có vùng chờ. Và đó là Buffer. NodeJS không thể kiểm soát được tốc độ và thời gian dữ liệu được trả tới, tốc độ của stream. Nó chỉ có thế quyết định thời gian dữ liệu được gửi đi. Nếu chưa tới thời gian thì NodeJS sẽ đưa dữ liệu vào buffer, cho tới lúc chúng được gửi đi để xử lý

Đến đây các bạn đã hiểu hơn về buffer rồi đúng không. Ủa nhưng thế liên quan gì đến binary data, thằng tác giả bị dở hơi à? Đương nhiên là cái gì cũng phải có lí do của nó chứ. Giờ chúng ta sẽ xem cách làm việc với buffer

Trong NodeJS, chúng ta hoàn toàn có thể tạo ra một buffer riêng cho chúng ta

const buffer = new Buffer.from('Xin chàooo', 'utf8');

console.log(buffer)

Nếu string của bạn chứa kí tự thì phải sử dụng bạng mã utf8 nhé, nếu không thì không truyền vào cũng được

và kết quả thu được sẽ là một object chứa những dữ liệu nhị phân

<Buffer 58 69 6e 20 63 68 c3 a0 6f 6f 6f>

Để đọc được thì bạn có thể lội lên đoạn đầu, tìm vào bảng mã và dịch. Nhưng cũng có thể toString() cho nhanh nhé 😄, hoặc bản có thể toJSON(). Bạn cũng có thể ghi đè lên dữ liệu đã có

const buffer = new Buffer.from('Xin chàooo', 'utf8');
buffer.write('Hello world');
console.log(buffer.toString());

Giờ kết quả đã được thay đổi hoàn toàn. Và chúng ta còn có thể làm được vô vàn điều khác với buffer, bạn có thể tìm thêm trong docs của nodejs nha

Bài viết hôm nay của mình tới đây thôi, mong nó sẽ giúp ích được phần nào cho các bạn. Cảm ơn các bạn đã đọc 😄


All Rights Reserved