Преимущества разных способов делиться Ajax запросами - PullRequest
0 голосов
/ 23 марта 2020

У меня есть intr anet веб-система, которая отображает информацию в режиме реального времени. Существуют процессы на стороне сервера, которые регистрируют различные обновления в базе данных. Различные веб-страницы затем используют Ajax, чтобы запрашивать что-то новое каждые несколько секунд. Это все стандартные вещи (например, RSS ...) и до сих пор работает.

Однако ... Если у пользователя есть несколько страниц на дисплее, это вызывает несколько Ajax запросов - каждый фактически запрашивает одни и те же данные. Это не повредит для нескольких пользователей, но не будет хорошо масштабироваться. Поэтому я хочу иметь один запрос на пользователя. Есть много вариантов:

  1. остаться с Ajax процессом на странице и принять дублирование
  2. использовать отправленные сервером события на каждой странице (т. Е. Заставить браузер беспокоиться)
  3. имеет первую страницу, загружающую обновления в локальное хранилище, а другие страницы перехватывают события "хранилища"
  4. перемещают Ajax в веб-работника, который отправляет сообщения на страницы
  5. Я пропустил какой-либо вариант?

Не могли бы вы подумать об относительной выгоде / боли различных вариантов?

1 Ответ

0 голосов
/ 24 марта 2020

Это звучит как работа для SharedWorker .

Все ваши страницы подпишутся на него и будут ждать передачи ресурсов:

Ниже приведен код из демо live fiddle (исходные кадры StackSnippet не допускаются для запуска SharedWorkers.)

const script_content = document.getElementById('worker-script').textContent;
const script_url = URL.createObjectURL( new Blob( [ script_content ] ) );

// This should be the only one per page
const worker1 = new SharedWorker( script_url );
worker1.port.onmessage = (e) => console.log('worker1 received data', e.data);
// This would be an other page
const worker2 = new SharedWorker( script_url );
worker2.port.onmessage = (e) => console.log('worker2 received data', e.data);
<script id="worker-script" >
    const ports = []; // we'll store all the connected ports here
    onconnect = e => { / everytime a process connects
      ports.push( e.ports[ 0 ] );
    };
    setInterval( () => {
        const rand = Math.random(); // fetched data
        // transmit it to all connected processes
        ports.forEach( port => port.postMessage( rand ) );  
      }, 1000
    );
</script>


Самая большая проблема с SharedWorker - это поддержка браузера. Вы не сможете использовать его в Safari.

Но теперь у нас есть BroadcastChannel API , который позволяет взаимодействовать между различными процессами в одном домене и который может быть довольно многофайловым легко благодаря событию Storage.

Таким образом, вы можете попытаться настроить систему, которая будет работать так же, как SharedWorker, но более сложным способом.

Когда браузер подключается к одной из ваших страниц, он пытается присоединиться к BroadcastChannel или создает его.

Сначала он отправляет сообщение через BroadcastChannel, спрашивающее, есть ли кто-то подключенный.

  • Если никто не подключен, то этот процесс будет отвечать за выборки.

    • Каждый раз, когда он получает ответ от сервера, он отправляет его всем другим процессам через BroadcastChannel.
    • Когда процесс собирается быть убитым (например, в onbeforeunload), он сообщает подключенному процессу, чтобы выбрать один в качестве нового сборщика.
  • В противном случае

    • [опционально] Он выдвигает себя в список наследников
    • Он ждет, пока сборщик данных не передаст данные через BroadcastChannel.

Вот подтверждение концепции jsfiddle , поскольку, хотя StackSnippet странным образом разрешено запускать BroadcastChannels, их нулевое происхождение не позволит им поделиться ни с одним экземпляром ... Откройте эту ссылку на нескольких вкладках и посмотрим, как они все говорят друг другу.

Вот код скрипки в любом случае:

const my_id = Math.random();
let I_am_the_master = false;
let connections = ["me"];
const master_timeout = ImDaMasta();

const channel = new BroadcastChannel('demo');

channel.onmessage = e => {
  console.log(e.data);
  switch (e.data.type) {
    case "newData":
      onnewdatareceived(e.data.data);
      break;
    case "disconnect":
      ondisconnect(e);
      break;
    case "connect":
      onconnect(e);
      break;
    case my_id:
      oninitresponse(e);
  }
};

channel.postMessage({
  type: 'connect',
  _id: my_id
});


onbeforeunload = e => {
  channel.postMessage({
    type: 'disconnect',
    index: connections.indexOf('me')
  });
}

// set ourselves as the master process
function ImDaMasta() {
  I_am_the_master = true;
  return setInterval(() => {
    const rand = Math.random();
    channel.postMessage({
      type: "newData",
      data: rand
    });
    log("I'm the master and did post", rand);
  }, 1000);
}
// when we receive new data from the master
function onnewdatareceived(data) {
  log("I'm a slave and received", data);
}
// when an other process disconnected
function ondisconnect(e) {
  connections.slice(e.data.index, 1);
  if (connections[0] === "me") {
    ImDaMasta();
  }
}
// when an other process connects
function onconnect(e) {
  connections.push('other');
  if (I_am_the_master) {
    channel.postMessage({
      type: e.data._id,
      connections
    });
  }
}
// when the master process handled our connection
function oninitresponse(e) {
  I_am_the_master = false;
  clearTimeout(master_timeout);
  connections = e.data.connections;
  connections[connections.length - 1] = "me";
}


// helper
function log(...args) {
  _log.textContent = args.map(o => JSON.stringify(o)).join(' - ');
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...