Rumah  >  Artikel  >  hujung hadapan web  >  Perbincangan ringkas tentang cara melaksanakan memori dikongsi dalam model berbilang proses Node.js (penjelasan kod terperinci)

Perbincangan ringkas tentang cara melaksanakan memori dikongsi dalam model berbilang proses Node.js (penjelasan kod terperinci)

青灯夜游
青灯夜游ke hadapan
2021-08-05 10:26:034373semak imbas

Artikel ini akan berbincang dengan anda Node.jsMenggunakan pelbagai kaedah teras - model multi-threading yang disediakan oleh modul worker_threads, kami akan memperkenalkan kaedah melaksanakan memori dikongsi dalam Node.js multi- model proses.

Perbincangan ringkas tentang cara melaksanakan memori dikongsi dalam model berbilang proses Node.js (penjelasan kod terperinci)

Node.js Disebabkan reka bentuk model benang tunggalnya, proses Nod (benang utama) hanya boleh menggunakan satu teras CPU Walau bagaimanapun, mesin hari ini pada asasnya Berbilang teras , ini menyebabkan pembaziran prestasi yang serius. Secara umumnya, jika anda ingin memanfaatkan berbilang teras, anda biasanya mempunyai kaedah berikut:

  • Tulis pemalam C untuk Node mengembangkan kumpulan benang, dan wakilkan CPU- memakan tugas untuknya dalam kod JS Pemprosesan benang lain.

  • Gunakan model berbilang benang yang disediakan oleh modul benang_pekerja (masih percubaan).

  • Gunakan model berbilang proses yang disediakan oleh modul child_process atau cluster Setiap proses ialah proses Node.js yang bebas.

Dari perspektif kemudahan penggunaan, pencerobohan kod dan kestabilan, model berbilang proses biasanya merupakan pilihan pertama. [Pembelajaran yang disyorkan: "tutorial nodejs"]

Masalah dengan model berbilang proses gugusan Node.js

dalam Dalam model berbilang proses yang disediakan oleh modul kluster, setiap proses Nod adalah proses aplikasi yang bebas dan lengkap, dengan ruang ingatannya sendiri yang tidak boleh diakses oleh proses lain. Oleh ituWalaupun semua proses Pekerja mempunyai status dan tingkah laku yang konsisten apabila projek dimulakan, tiada jaminan bahawa status mereka akan kekal konsisten dalam larian berikutnya.

Sebagai contoh, apabila projek dimulakan, terdapat dua proses Pekerja, proses A dan proses B. Kedua-dua proses mengisytiharkan pembolehubah a=1. Tetapi projek itu menerima permintaan, dan proses Master menugaskannya untuk memproses A untuk pemprosesan Permintaan ini menukar nilai a kepada 2. Pada masa ini, a=2 dalam ruang memori proses A, tetapi a=2 dalam. ruang ingatan proses B. Masih 1. Pada masa ini, jika terdapat permintaan untuk membaca nilai a, keputusan dibaca apabila proses Master menghantar permintaan untuk memproses A dan proses B tidak konsisten, yang menyebabkan masalah konsistensi.

Modul kluster tidak menyediakan penyelesaian semasa mereka bentuk, tetapi memerlukan proses Worker tanpa kewarganegaraan, iaitu pengaturcara tidak boleh dibenarkan mengubah suai nilai dalam memori semasa memproses permintaan semasa menulis kod ketekalan semua proses Pekerja. Walau bagaimanapun, dalam amalan, sentiasa terdapat pelbagai situasi yang memerlukan penulisan ke ingatan, seperti merekodkan status log masuk pengguna Dalam amalan banyak perusahaan, biasanya merekodkan data status ini secara luaran, seperti pangkalan data, redis, baris gilir Mesej. sistem fail, dsb. akan membaca dan menulis ruang storan luaran setiap kali permintaan berstatus diproses.

Ini adalah pendekatan yang berkesan, Walau bagaimanapun, ia memerlukan pengenalan ruang storan luaran tambahan, dan pada masa yang sama, ia mesti mengendalikan isu ketekalan di bawah akses serentak oleh pelbagai proses dan mengekalkan hayat kitaran data dengan sendirinya (kerana proses Nod dan data yang diselenggara secara luaran tidak dicipta dan dimusnahkan secara serentak), dan terdapat kesesakan prestasi IO dalam kes akses serentak yang tinggi (jika ia disimpan dalam persekitaran bukan memori seperti sebagai pangkalan data) . Sebenarnya, pada asasnya, kami hanya memerlukan ruang yang boleh dikongsi dan diakses oleh pelbagai proses Kami tidak memerlukan storan yang berterusan masa apabila menggunakannya. Oleh itu, memori kongsi proses silang telah menjadi kaedah yang paling sesuai untuk digunakan dalam senario ini.

Memori kongsi Node.js

Malangnya, Node itu sendiri tidak menyediakan pelaksanaan memori dikongsi, jadi kita boleh lihat repositori npm Pelaksanaan perpustakaan pihak ketiga. Sebahagian daripada perpustakaan ini dilaksanakan melalui pemalam C yang memanjangkan fungsi Node, dan sebahagian lagi dilaksanakan melalui mekanisme IPC yang disediakan oleh Node Malangnya, pelaksanaannya sangat mudah dan tidak menyediakan akses yang saling eksklusif, pemantauan objek dan fungsi lain, yang membuat menggunakan Pengarang mesti berhati-hati mengekalkan memori bersama ini sendiri, jika tidak, ia akan menyebabkan masalah masa.

Saya melihat sekeliling dan tidak menemui apa yang saya mahu. . . Lupakan, saya akan tulis sendiri.

Reka bentuk memori bersama

Pertama sekali, kita mesti menjelaskan jenis memori bersama yang saya perlukan berdasarkan keperluan saya sendiri (untuk menggunakannya dalam projek Ia menyimpan data keadaan yang diakses merentas proses), sambil mengambil kira kepelbagaian, jadi perkara berikut akan dipertimbangkan terlebih dahulu:

  • Menggunakan objek JS sebagai unit asas untuk akses baca dan tulis.

  • membolehkan akses yang saling eksklusif antara proses Apabila satu proses mengakses, proses lain disekat.

  • boleh memantau objek dalam memori dikongsi dan proses pemantauan boleh dimaklumkan apabila objek berubah.

  • Di bawah premis memenuhi syarat di atas, kaedah pelaksanaannya adalah semudah mungkin.

Ia boleh didapati bahawa kita sebenarnya tidak memerlukan memori yang dikongsi di peringkat sistem pengendalian Kita hanya perlu mempunyai beberapa proses Node mengakses objek yang sama kita boleh Dilaksanakan pada mekanisme yang disediakan dengan sendirinya. Anda boleh menggunakan ruang memori proses Induk sebagai ruang memori dikongsi Proses Pekerja mewakilkan permintaan baca dan tulis kepada proses Induk melalui IPC, dan proses Induk membaca dan menulis, dan kemudian mengembalikan hasilnya kepada Pekerja. proses melalui IPC.

Untuk menjadikan penggunaan memori yang dikongsi konsisten dalam proses Induk dan proses Pekerja, kita boleh mengabstrak operasi memori yang dikongsi ke dalam antara muka, dan melaksanakan antara muka ini dalam proses Induk dan proses Pekerja masing-masing. Rajah kelas adalah seperti yang ditunjukkan dalam rajah di bawah Gunakan kelas SharedMemory sebagai antara muka abstrak dan isytiharkan objek dalam fail masukan server.js. Ia dijadikan sebagai objek Manager dalam proses Induk dan sebagai objek Worker dalam proses Pekerja. Objek Manager mengekalkan memori kongsi dan mengendalikan permintaan baca dan tulis ke memori kongsi, manakala objek Worker menghantar permintaan baca dan tulis ke proses Master.

Perbincangan ringkas tentang cara melaksanakan memori dikongsi dalam model berbilang proses Node.js (penjelasan kod terperinci)

Anda boleh menggunakan atribut dalam kelas Manager sebagai objek memori kongsi Cara untuk mengakses objek adalah sama seperti cara untuk mengakses objek JS biasa. dan kemudian lakukan lapisan enkapsulasi , hanya dedahkan operasi asas seperti get, set dan remove untuk mengelakkan pengubahsuaian langsung atribut.

Memandangkan proses Master akan dibuat sebelum semua proses Worker, anda boleh mencipta proses Worker selepas mengisytiharkan ruang memori kongsi dalam proses Master untuk memastikan setiap proses Worker boleh mengakses memori kongsi serta-merta selepas penciptaan. .

Untuk kesederhanaan penggunaan, kami boleh mereka bentuk SharedMemory sebagai singleton, supaya hanya ada satu contoh dalam setiap proses dan boleh digunakan terus selepas imported SharedMemory.

Pelaksanaan kod

Kawalan baca dan tulis serta komunikasi IPC

Mula-mula laksanakan antara muka luaran SharedMemory kelas, bukannya membenarkan Manager dan Worker mewarisi daripada SharedMemory, SharedMemory mengembalikan tika Manager atau Worker apabila dibuat instantiat, dengan itu merealisasikan pemilihan subkelas secara automatik.

Dalam Nod 16, isPrimary menggantikan isMaster Dua kaedah penulisan digunakan di sini untuk keserasian.

// shared-memory.js
class SharedMemory {
  constructor() {
    if (cluster.isMaster || cluster.isPrimary) {
      return new Manager();
    } else {
      return new Worker();
    }
  }
}

Manager bertanggungjawab mengurus ruang memori kongsi Kami terus menambah atribut Manager pada objek __sharedMemory__ Memandangkan ia juga objek JS, ia akan menjadi termasuk dalam pengurusan kutipan sampah JS , jadi kami tidak perlu melakukan operasi seperti pembersihan memori dan pemindahan data, menjadikan pelaksanaannya sangat mudah. Kemudian tentukan operasi standard seperti __sharedMemory__, set dan get dalam remove untuk menyediakan kaedah akses.

Kami menggunakan cluster.on('online', callback) untuk memantau peristiwa penciptaan proses pekerja, dan sejurus selepas penciptaan, gunakan worker.on('message', callback) untuk memantau komunikasi IPC daripada proses pekerja dan menyerahkan mesej komunikasi kepada handle fungsi untuk pemprosesan. Tanggungjawab fungsi

handle adalah untuk membezakan jenis operasi yang ingin dilakukan oleh proses pekerja, dan mengeluarkan parameter operasi dan mewakilkannya kepada set, get yang sepadan, dan fungsi remove (nota bukan __sharedMemory__ set, get, remove) dalam proses dan mengembalikan hasil yang diproses kepada proses pekerja.

// manager.js
const cluster = require('cluster');

class Manager {
  constructor() {
    this.__sharedMemory__ = {
      set(key, value) {
        this.memory[key] = value;
      },
      get(key) {
        return this.memory[key];
      },
      remove(key) {
        delete this.memory[key];
      },
      memory: {},
    };

    // Listen the messages from worker processes.
    cluster.on('online', (worker) => {
      worker.on('message', (data) => {
        this.handle(data, worker);
        return false;
      });
    });
  }

  handle(data, target) {
    const args = data.value ? [data.key, data.value] : [data.key];
    this[data.method](...args).then((value) => {
      const msg = {
        id: data.id, // workerId
        uuid: data.uuid, // communicationID
        value,
      };
      target.send(msg);
    });
  }

  set(key, value) {
    return new Promise((resolve) => {
      this.__sharedMemory__.set(key, value);
      resolve('OK');
    });
  }

  get(key) {
    return new Promise((resolve) => {
      resolve(this.__sharedMemory__.get(key));
    });
  }

  remove(key) {
    return new Promise((resolve) => {
      this.__sharedMemory__.remove(key);
      resolve('OK');
    });
  }
}

WorkerGunakan process.on untuk memantau mesej pulangan daripada proses Master sejak objek dicipta (lagipun, anda tidak boleh menunggu mesej dihantar sebelum memantaunya, kemudian ia akan terlambat). Mengenai peranan objek __getCallbacks__, kami akan membincangkannya kemudian. Pada ketika ini objek Worker dicipta. Selepas

, apabila projek berjalan di suatu tempat, jika anda ingin mengakses memori yang dikongsi, ia akan memanggil fungsi Worker, set dan get remove, yang seterusnya akan memanggil fungsi handle Hantar mesej kepada proses induk melalui process.send, dan rekod operasi yang akan dilakukan apabila hasil yang dikembalikan diperoleh dalam __getCallbacks__. Apabila keputusan kembali, ia akan dipantau oleh fungsi sebelumnya dalam process.on, dan fungsi panggil balik yang sepadan akan dikeluarkan daripada __getCallbacks__ dan dilaksanakan.

因为访问共享内存的过程中会经过IPC,所以必定是异步操作,所以需要记录回调函数,不能实现成同步的方式,不然会阻塞原本的任务。

// worker.js
const cluster = require('cluster');
const { v4: uuid4 } = require('uuid');

class Worker {
  constructor() {
    this.__getCallbacks__ = {};

    process.on('message', (data) => {
      const callback = this.__getCallbacks__[data.uuid];
      if (callback && typeof callback === 'function') {
        callback(data.value);
      }
      delete this.__getCallbacks__[data.uuid];
    });
  }

  set(key, value) {
    return new Promise((resolve) => {
      this.handle('set', key, value, () => {
        resolve();
      });
    });
  }

  get(key) {
    return new Promise((resolve) => {
      this.handle('get', key, null, (value) => {
        resolve(value);
      });
    });
  }

  remove(key) {
    return new Promise((resolve) => {
      this.handle('remove', key, null, () => {
        resolve();
      });
    });
  }

  handle(method, key, value, callback) {
    const uuid = uuid4(); // 每次通信的uuid
    process.send({
      id: cluster.worker.id,
      method,
      uuid,
      key,
      value,
    });
    this.__getCallbacks__[uuid] = callback;
  }
}

一次共享内存访问的完整流程是:调用Workerset/get/remove函数 -> 调用Workerhandle函数,向master进程通信并将回调函数记录在__getCallbacks__ -> master进程监听到来自worker进程的消息 -> 调用Managerhandle函数 -> 调用Managerset/get/remove函数 -> 调用__sharedMemory__set/get/remove函数 -> 操作完成返回Managerset/get/remove函数 -> 操作完成返回handle函数 -> 向worker进程发送通信消息 -> worker进程监听到来自master进程的消息 -> 从__getCallbacks__中取出回调函数并执行。

互斥访问

到目前为止,我们已经实现了读写共享内存,但还没有结束,目前的共享内存是存在严重安全问题的。因为这个共享内存是可以所有进程同时访问的,然而我们并没有考虑并发访问时的时序问题。我们来看下面这个例子:

时间 进程A 进程B 共享内存中变量x的值
t0

0
t1 读取x(x=0)
0
t2 x1=x+1(x1=1) 读取x(x=0) 0
t3 将x1的值写回x x2=x+1(x2=1) 1
t4
将x2的值写回x 1

进程A和进程B的目的都是将x的值加1,理想情况下最后x的值应该是2,可是最后的结果却是1。这是因为进程B在t3时刻给x的值加1的时候,使用的是t2时刻读取出来的x的值,但此时从全局角度来看,这个值已经过期了,因为t3时刻x最新的值已经被进程A写为了1,可是进程B无法知道进程外部的变化,所以导致了t4时刻最后写回的值又覆盖掉了进程A写回的值,等于是进程A的行为被覆盖掉了。

在多线程、多进程和分布式中并发情况下的数据一致性问题是老大难问题了,这里不再展开讨论。

为了解决上述问题,我们必须实现进程间互斥访问某个对象,来避免同时操作一个对象,从而使进程可以进行原子操作,所谓原子操作就是不可被打断的一小段连续操作,为此需要引入锁的概念。由于读写均以对象为基本单位,因此锁的粒度设置为对象级别。在某一个进程(的某一任务)获取了某个对象的锁之后,其它要获取锁的进程(的任务)会被阻塞,直到锁被归还。而要进行写操作,则必须要先获取对象的锁。这样在获取到锁直到锁被释放的这段时间里,该对象在共享内存中的值不会被其它进程修改,从而导致错误。

Manager__sharedMemory__中加入locks属性,用来记录哪个对象的锁被拿走了,lockRequestQueues属性用来记录被阻塞的任务(正在等待锁的任务)。并增加getLock函数和releaseLock函数,用来申请和归还锁,以及handleLockRequest函数,用来使被阻塞的任务获得锁。在申请锁时,会先将回调函数记录到lockRequestQueues队尾(因为此时该对象的锁可能已被拿走),然后再调用handleLockRequest检查当前锁是否被拿走,若锁还在,则让队首的任务获得锁。归还锁时,先将__sharedMemory__.locks中对应的记录删掉,然后再调用handleLockRequest让队首的任务获得锁。

// manager.js
const { v4: uuid4 } = require('uuid');

class Manager {
  constructor() {
    this.__sharedMemory__ = {
      ...
      locks: {},
      lockRequestQueues: {},
    };
  }

  getLock(key) {
    return new Promise((resolve) => {
      this.__sharedMemory__.lockRequestQueues[key] =
        this.__sharedMemory__.lockRequestQueues[key] ?? [];
      this.__sharedMemory__.lockRequestQueues[key].push(resolve);
      this.handleLockRequest(key);
    });
  }

  releaseLock(key, lockId) {
    return new Promise((resolve) => {
      if (lockId === this.__sharedMemory__.locks[key]) {
        delete this.__sharedMemory__.locks[key];
        this.handleLockRequest(key);
      }
      resolve('OK');
    });
  }

  handleLockRequest(key) {
    return new Promise((resolve) => {
      if (
        !this.__sharedMemory__.locks[key] &&
        this.__sharedMemory__.lockRequestQueues[key]?.length > 0
      ) {
        const callback = this.__sharedMemory__.lockRequestQueues[key].shift();
        const lockId = uuid4();
        this.__sharedMemory__.locks[key] = lockId;
        callback(lockId);
      }
      resolve();
    });
  }
  ...
}

Worker中,则是增加getLockreleaseLock两个函数,行为与getset类似,都是调用handle函数。

// worker.js
class Worker {
  getLock(key) {
    return new Promise((resolve) => {
      this.handle('getLock', key, null, (value) => {
        resolve(value);
      });
    });
  }

  releaseLock(key, lockId) {
    return new Promise((resolve) => {
      this.handle('releaseLock', key, lockId, (value) => {
        resolve(value);
      });
    });
  }
  ...
}

监听对象

有时候我们需要监听某个对象值的变化,在单进程Node应用中这很容易做到,只需要重写对象的set属性就可以了,然而在多进程共享内存中,对象和监听者都不在一个进程中,这只能依赖Manager的实现。这里,我们选择了经典的观察者模式来实现监听共享内存中的对象。

Perbincangan ringkas tentang cara melaksanakan memori dikongsi dalam model berbilang proses Node.js (penjelasan kod terperinci)

为此,我们先在__sharedMemory__中加入listeners属性,用来记录在对象值发生变化时监听者注册的回调函数。然后增加listen函数,其将监听回调函数记录到__sharedMemory__.listeners中,这个监听回调函数会将变化的值发送给对应的worker进程。最后,在setremove函数返回前调用notifyListener,将所有记录在__sharedMemory__.listeners中监听该对象的所有函数取出并调用。

// manager.js
class Manager {
  constructor() {
    this.__sharedMemory__ = {
      ...
      listeners: {},
    };
  }

  handle(data, target) {
    if (data.method === 'listen') {
      this.listen(data.key, (value) => {
        const msg = {
          isNotified: true,
          id: data.id,
          uuid: data.uuid,
          value,
        };
        target.send(msg);
      });
    } else {
      ...
    }
  }

  notifyListener(key) {
    const listeners = this.__sharedMemory__.listeners[key];
    if (listeners?.length > 0) {
      Promise.all(
        listeners.map(
          (callback) =>
            new Promise((resolve) => {
              callback(this.__sharedMemory__.get(key));
              resolve();
            })
        )
      );
    }
  }

  set(key, value) {
    return new Promise((resolve) => {
      this.__sharedMemory__.set(key, value);
      this.notifyListener(key);
      resolve('OK');
    });
  }

  remove(key) {
    return new Promise((resolve) => {
      this.__sharedMemory__.remove(key);
      this.notifyListener(key);
      resolve('OK');
    });
  }

  listen(key, callback) {
    if (typeof callback === 'function') {
      this.__sharedMemory__.listeners[key] =
        this.__sharedMemory__.listeners[key] ?? [];
      this.__sharedMemory__.listeners[key].push(callback);
    } else {
      throw new Error('a listener must have a callback.');
    }
  }
  ...
}

Worker中由于监听操作与其它操作不一样,它是一次注册监听回调函数之后对象的值每次变化都会被通知,因此需要在增加一个__getListenerCallbacks__属性用来记录监听操作的回调函数,与__getCallbacks__不同,它里面的函数在收到master的回信之后不会删除。

// worker.js
class Worker {
  constructor() {
    ...
    this.__getListenerCallbacks__ = {};

    process.on('message', (data) => {
      if (data.isNotified) {
        const callback = this.__getListenerCallbacks__[data.uuid];
        if (callback && typeof callback === 'function') {
          callback(data.value);
        }
      } else {
        ...
      }
    });
  }

  handle(method, key, value, callback) {
    ...
    if (method === 'listen') {
      this.__getListenerCallbacks__[uuid] = callback;
    } else {
      this.__getCallbacks__[uuid] = callback;
    }
  }

  listen(key, callback) {
    if (typeof callback === 'function') {
      this.handle('listen', key, null, callback);
    } else {
      throw new Error('a listener must have a callback.');
    }
  }
  ...
}

LRU缓存

有时候我们需要用用内存作为缓存,但多进程中各进程的内存空间独立,不能共享,因此也需要用到共享内存。但是如果用共享内存中的一个对象作为缓存的话,由于每次IPC都需要传输整个缓存对象,会导致缓存对象不能太大(否则序列化和反序列化耗时太长),而且由于写缓存对象的操作需要加锁,进一步影响了性能,而原本我们使用缓存就是为了加快访问速度。其实在使用缓存的时候通常不会做复杂操作,大多数时候也不需要保障一致性,因此我们可以在Manager再增加一个共享内存__sharedLRUMemory__,其为一个lru-cache实例,并增加getLRUsetLRUremoveLRU函数,与setgetremove函数类似。

// manager.js
const LRU = require('lru-cache');

class Manager {
  constructor() {
    ...
    this.defaultLRUOptions = { max: 10000, maxAge: 1000 * 60 * 5 };
    this.__sharedLRUMemory__ = new LRU(this.defaultLRUOptions);
  }

  getLRU(key) {
    return new Promise((resolve) => {
      resolve(this.__sharedLRUMemory__.get(key));
    });
  }

  setLRU(key, value) {
    return new Promise((resolve) => {
      this.__sharedLRUMemory__.set(key, value);
      resolve('OK');
    });
  }

  removeLRU(key) {
    return new Promise((resolve) => {
      this.__sharedLRUMemory__.del(key);
      resolve('OK');
    });
  }
  ...
}

Worker中也增加getLRUsetLRUremoveLRU函数。

// worker.js
class Worker {
  getLRU(key) {
    return new Promise((resolve) => {
      this.handle('getLRU', key, null, (value) => {
        resolve(value);
      });
    });
  }

  setLRU(key, value) {
    return new Promise((resolve) => {
      this.handle('setLRU', key, value, () => {
        resolve();
      });
    });
  }

  removeLRU(key) {
    return new Promise((resolve) => {
      this.handle('removeLRU', key, null, () => {
        resolve();
      });
    });
  }
  ...
}

共享内存的使用方式

目前共享内存的实现已发到npm仓库(文档和源代码在Github仓库欢迎pull request和报bug),可以直接通过npm安装:

npm i cluster-shared-memory

下面的示例包含了基本使用方法:

const cluster = require('cluster');
// 引入模块时会根据当前进程 master 进程还是 worker 进程自动创建对应的 SharedMemory 对象
require('cluster-shared-memory');

if (cluster.isMaster) {
  // 在 master 进程中 fork 子进程
  for (let i = 0; i < 2; i++) {
    cluster.fork();
  }
} else {
  const sharedMemoryController = require(&#39;./src/shared-memory&#39;);
  const obj = {
    name: &#39;Tom&#39;,
    age: 10,
  };
  
  // 写对象
  await sharedMemoryController.set(&#39;myObj&#39;, obj);
  
  // 读对象
  const myObj = await sharedMemoryController.get(&#39;myObj&#39;);
  
  // 互斥访问对象,首先获得对象的锁
  const lockId = await sharedMemoryController.getLock(&#39;myObj&#39;);
  const newObj = await sharedMemoryController.get(&#39;myObj&#39;);
  newObj.age = newObj.age + 1;
  await sharedMemoryController.set(&#39;myObj&#39;, newObj);
  // 操作完之后释放锁
  await sharedMemoryController.releaseLock(&#39;requestTimes&#39;, lockId);
  
  // 或者使用 mutex 函数自动获取和释放锁
  await sharedMemoryController.mutex(&#39;myObj&#39;, async () => {
    const newObjM = await sharedMemoryController.get(&#39;myObj&#39;);
    newObjM.age = newObjM.age + 1;
    await sharedMemoryController.set(&#39;myObj&#39;, newObjM);
  });
  
  // 监听对象
  sharedMemoryController.listen(&#39;myObj&#39;, (value) => {
    console.log(`myObj: ${value}`);
  });
  
  //写LRU缓存
  await sharedMemoryController.setLRU(&#39;cacheItem&#39;, {user: &#39;Tom&#39;});
  
  // 读对象
  const cacheItem = await sharedMemoryController.getLRU(&#39;cacheItem&#39;);
}

缺点

这种实现目前尚有几个缺点:

  • 不能使用PM2的自动创建worker进程的功能。

由于PM2会使用自己的cluster模块的master进程的实现,而我们的共享内存模块需要在master进程维护一个内存空间,则不能使用PM2的实现,因此不能使用PM2的自动创建worker进程的功能。

  • 传输的对象必须可序列化,且不能太大。

  • 如果使用者在获取锁之后忘记释放,会导致其它进程一直被阻塞,这要求程序员有良好的代码习惯。

原文地址:https://juejin.cn/post/6992091006220894215

作者:FinalZJY

更多编程相关知识,请访问:编程视频!!

Atas ialah kandungan terperinci Perbincangan ringkas tentang cara melaksanakan memori dikongsi dalam model berbilang proses Node.js (penjelasan kod terperinci). Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!

Kenyataan:
Artikel ini dikembalikan pada:掘金--FinalZJY. Jika ada pelanggaran, sila hubungi admin@php.cn Padam