Статьи

Advanced ZK: асинхронные обновления пользовательского интерфейса и фоновая обработка — часть 1

Асинхронные обновления пользовательского интерфейса очень полезны, потому что они обычно улучшают скорость отклика, удобство использования и общее ощущение пользовательских интерфейсов. Я сосредоточусь здесь на ZK- фреймворке, но в целом те же принципы применимы и к настольным интерфейсам (Swing, SWT).

Длительная обработка

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

Обновления в реальном времени

Иногда вы не знаете заранее точное время, когда что-то в пользовательском интерфейсе должно быть обновлено. Например, у вас может быть визуальный измеритель, который показывает количество пользователей в приложении. Когда новый пользователь входит в приложение, пользовательские интерфейсы текущих пользователей должны обновляться как можно скорее, чтобы отражать количество новых пользователей. Вы можете использовать механизм, основанный на таймере, чтобы постоянно проверять, изменилось ли количество пользователей, но если слишком много одновременных пользователей, непрерывная проверка вызовет очень большую нагрузку, даже если в пользовательском интерфейсе ничего не обновляется.

Базовые концепты

Давайте сначала разберем заголовок этого поста: «Асинхронные обновления пользовательского интерфейса и фоновая обработка»

Фоновая обработка

В случае использования длительной обработки наиболее очевидным способом уменьшения блокировки пользовательского интерфейса является перемещение дорогостоящей обработки из потоков пользовательского интерфейса в некоторые фоновые потоки. Очень важно уметь понимать, какой поток будет запускать код в разных частях вашего приложения. Например, в приложениях ZK большая часть кода выполняется потоками сервлетов, которые в основном являются эквивалентами мира сервлетов потокам пользовательского интерфейса. Чтобы выполнить код в некотором фоновом потоке, нам понадобится пул потоков. Самый простой способ — использовать java.util.concurrent.ExecutorService, который был представлен в JDK5. Мы можем передать объекты Runnable в ExecutorService, поэтому мы в основном просим ExecutorService запустить определенный блок кода в некотором фоновом потоке.

Крайне важно понимать, что фреймворки, использующие ThreadLocals, будут иметь проблемы с этим подходом, потому что ThreadLocals, установленные в потоке сервлета, не будут видны в фоновом потоке. Примером является Spring Security, который по умолчанию использует ThreadLocal для хранения контекста безопасности (= идентификатор пользователя + другие вещи).

Асинхронные обновления пользовательского интерфейса

Что означает асинхронное обновление пользовательского интерфейса в этом контексте? По сути, идея заключается в том, что, как только мы получим некоторую информацию, которую мы хотели бы представить в пользовательском интерфейсе, мы будем информировать пользовательский интерфейс о новых данных (= асинхронных) вместо непосредственного обновления пользовательского интерфейса в фоновом потоке (= синхронном). Мы не можем знать заранее, когда будет доступна новая информация, поэтому мы не можем запрашивать информацию со стороны клиента (если мы не используем опрос, который стоит дорого).

Пуш сервера в ЗК

С ZK у нас в основном два разных подхода, которые мы можем использовать для обновления пользовательского интерфейса, как только фоновый поток получает новую информацию. Название «push server» происходит от того факта, что на сервере есть некоторые новые данные, которые должны быть переданы клиенту вместо типичного рабочего процесса (клиент запрашивает информацию у сервера). Во-первых, вы можете выполнять синхронные обновления, получая эксклюзивный доступ к рабочему столу с помощью Executions.activate / deactivate. Лично я не одобряю это, потому что, как только у вас будет эксклюзивный доступ, потокам пользовательского интерфейса придется ждать, пока вы деактивируете рабочий стол. Вот почему я не буду освещать этот метод в этом посте.

С другой стороны, асинхронные обновления выполняются с использованием Executions.schedule, который соответствует модели Event / EventListener обычной обработки событий. Идея состоит в том, что мы можем передать обычные объекты ZK Event в EventListeners, и клиентская сторона будет проинформирована об этих событиях. После этого ZK выполняет обычный AJAX-запрос с использованием Javascript, и события будут обрабатываться EventListeners. Это означает, что если мы используем асинхронные обновления, вся фактическая обработка событий будет выполняться потоками сервлетов, и все ThreadLocals будут доступны как обычно. Это делает модель программирования очень простой, потому что вам нужно иметь только обычные методы прослушивания событий без сложного параллельного программирования.

Вот небольшой пример:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
public class TestComposer extends GenericForwardComposer {
  private Textbox search;
 
  public void onClick$startButton() {
    if (desktop.isServerPushEnabled()) {
      desktop.enableServerPush(true);
    }
 
    final String searchString = search.getValue();
    final EventListener el = this; // All GenericForwardComposers are also EventListeners
 
    // Don't do this in a real-world application. Use thread pools instead.
    Thread backgroundThread = new Thread() {
       public void run() {
         // In this part of code the ThreadLocals ARE NOT available
         // You must NOT touch any ZK related things (e.g. components, desktops)
         // If you need some information from ZK, you need to get them before this code
         // For example here I've read searchString from a textbox, so I can use the searchString variable without problems
         String result = ... // Retrieve the result from somewhere
         Executions.schedule(desktop, el, new Event('onNewData', null, result));
       }
    };
 
    backgroundThread.start();
  }
  public void onNewData(Event event) {
    // In this part of code the ThreadLocals ARE available
    String result = (String) event.getData();
    // Do something with result. You can touch any ZK stuff freely, just like when a normal event is posted.
  }
}

В следующей части я покажу вам, как использовать JDK5 ExecutorServices для запуска задач без создания потоков вручную. Если вы действительно хотите понять продвижение ZK-сервера, вам также следует прочитать соответствующую документацию ZK .

Приятного кодирования и не забудьте поделиться!

Ссылка: Advanced ZK: Асинхронные обновления пользовательского интерфейса и фоновая обработка — часть 1 от нашего партнера по JCG Joonas Javanainen в техническом блоге Jawsy Solutions .