Kiểm soát đồng thời trong lập trình

Nội dung bài viết

Video học lập trình mỗi ngày

Có một bạn gửi câu hỏi về bằng một hình ảnh chắc lấy ở nhóm nào đó, và tôi không bất ngờ lắm với tình huống trên. Với một 1 triệu url thì nếu sử dụng for loop như bình thường là không thể. Và tôi đã gặp và có giải pháp nay chia sẻ cho các bạn.

Cách kiểm soát đồng thời trong javascript với 1 triệu url

Có một bạn gửi câu hỏi về bằng một hình ảnh, và tôi không bất ngờ lắm với tình huống trên. Với một 1 triệu url thì nếu sử dụng for loop như bình thường là không thể. Và tôi đã gặp và có giải pháp nay chia sẻ cho các bạn.


Kiểm soát đồng thời là gì?

Trước tiên bạn cũng phải làm quen những khái niệm này, để có gì mà đi hỏi hoặc trả lời. Bạn không cần hiểu theo học thuật, bạn theo dõi tips javascript là biết rồi, chúng ta toàn là giải thích theo thực tế. Thực tế là có một bài viết tương tự như vậy đó là cách xử lý 100.000 data trên Client như thế nào? Nó có liên quan đây và ở đây tôi kể thêm một tình huống nữa đó là những bạn đi máy bay đều biết câu chuyện thế này.

Khi sếp hàng vào checkin thì một dãy dài cứ cho là 100 người. Đương nhiên qua tải họ phải mở nhiều port checkin. Và có một người kiểm soát lượng người đó. Họ sẽ cho phép khi nào bạn được vào làm thủ tục khi họ thấy port đó đang còn chỗ trống. Khi bạn tiến vào làm thủ tục, thì người tiếp theo sẽ xếp hàng sau bạn. Khi bạn xong, đi ra, thì người tiếp theo sẽ đến làm thủ tục. Chính lý do đó, phân phối đúng lúc đúng chỗ làm giảm tắc nghẽn hoặc quá tải.

Ví dụ đó chính là Kiểm soát đồng thời, giúp cho hệ thống chạy ổn định hơn nếu bạn muốn nhanh không có cách nào khác bạn phải sử dụng nhiều cách khác nhau, như thêm máy chủ, triển khai Microservice. Mô hình Microservice tôi đã nói ở bài trước, bạn nên đọc nó để vững vàng tay lái hơn.

Để hình dung rõ hơn tôi sẽ vẽ mô hình làm thủ tục lên máy bay. async pool


Các bài viết liên quan

Các thực hành liên quan:

Hiểu domcontentloaded qua một ví dụ với 100.000 dữ liệu

Message Queue đã giúp tôi thoát khỏi cảnh đuổi việc thế nào?

RabbitMQ - Cách thiết lập không mất tin khi triển khai nodejs rabbitmq

Mô hình microservice thực tế

Giải quyết kiểm soát đồng thời

Bạn nào đã tìm hiểu về những tính năng về ES6 đến 2021 thì cũng có thể đoán được chúng ta có thể làm được điều này.

Đầu tiên ý tưởng là thực hiện viết một function asyncPool dưới đây:

await asyncPool(2, [1000, 5000, 3000, 2000], timeout);

Với timeout là nhiệm vụ thực hiện xong mỗi data.

const timeout = i => new Promise(resolve => setTimeout(() => resolve(i), i));

Chúng ta có thể sử dụng thư viện async-pool mà tôi sẽ đề cập dưới bài viết nếu bạn muốn xài thư viện. Cụ thể là như thế này:

function asyncPool(poolLimit, array, iteratorFn){ ... }

Hàm này nhận 3 tham số:

  • poolLimit: Cho biết mỗi lần xử lý bao nhiêu item.
  • array: Đại diện cho một mảng, ví dụ 1 triệu item. [1,2..., 1000000]
  • iteratorFn(Loại hàm): Đại diện cho một hàm lặp, được sử dụng để xử lý từng mục tác vụ. Hàm này trả về một đối tượng Promise hoặc một hàm không đồng bộ.

Đối với ví dụ làm thủ tục máy bay như trên, được sử dụng với asyncPool, quá trình thực hiện tương ứng như sau:

const timeout = i => new Promise(resolve => setTimeout(() => resolve(i), i));
await asyncPool(2, [1000, 5000, 3000, 2000], timeout);

// "Nhan thang nay de thuc thi:::"
// 1000
// "Nhan thang nay de thuc thi:::"
// 4000
// "::::Giai quyet xong thang nay:::::"
// 1000
// "Nhan thang nay de thuc thi:::"
// 3000
// "::::Giai quyet xong thang nay:::::"
// 4000
// "Nhan thang nay de thuc thi:::"
// 2000
// "::::Giai quyet xong thang nay:::::"
// 3000
// "::::Giai quyet xong thang nay:::::"
// 2000
// "results::"
// [1000, 4000, 3000, 2000]

Giải thích: Mới vào ta truyền poolLimit: 2 nghĩa là mỗi lần chạy sẽ là 2 items. Đó là [1000, 4000] Sau khi ::::Giai quyet xong thang nay:::::1000 thì add thằng 3000 vào thực thi... Tiếp tục cứ như thế. Mã code sẽ là thế này.

function asyncPool(poolLimit, array, iteratorFn) {
  let i = 0;
  const ret = []; // Lưu trữ tất cả các tác vụ không đồng bộ
  const executing = []; // Lưu trữ các tác vụ không đồng bộ đang được thực thi
  const enqueue = function () {
    if (i === array.length) {
      return Promise.resolve();
    }
    const item = array[i++]; // Nhận một mục nhiệm vụ mới
    console.log("Nhan thang nay de thuc thi:::", item)
    const p = Promise.resolve().then(() => iteratorFn(item, array));
    ret.push(p);

    let r = Promise.resolve();

    if (poolLimit <= array.length) {
      // Khi nhiệm vụ đã hoàn thành, hãy xóa nhiệm vụ đã hoàn thành khỏi mảng nhiệm vụ đang được thực thi
      const e = p.then(() => executing.splice(executing.indexOf(e), 1));
      executing.push(e);
      if (executing.length >= poolLimit) {
        r = Promise.race(executing); 
      }
    }

    // Sau khi tác vụ nhanh hơn trong danh sách tác vụ được thực thi, tác vụ cần làm mới sẽ nhận được từ mảng
    return r.then(() => enqueue());
  };
  return enqueue().then(() => Promise.all(ret));
}

const timeout = i => new Promise(resolve => setTimeout(() => {
  console.log("::::Giai quyet xong thang nay:::::", i)
  resolve(i)
}, i));

async function asyncCall() {
   await asyncPool(2, [1000, 4000, 3000, 2000], timeout).then(results => {
    console.log('results::', results)
   })
}

asyncCall();

Tóm lại

Thật ra nó không khó, chỉ do bạn chưa gặp tình huống này thôi. Và có một thư viện đã làm điều này đó là async-pool bạn có thể tham khảo tại đó. Đơn giản dễ sử dụng. Ngoài ra, nếu bạn tinh thông chuyện này thì việc tải tệp lớn lên server cũng không là vấn đề lớn đúng không. Hãy suy nghĩ làm thử đi. Còn bây giờ thì xin chào!!

[ANN] Happy coding!!!

Có thể bạn đã bị missing