Оглавление | Назад | Вперёд | Индекс

Глава 6
Служба Session Management Service

В этой главе рассматриваются объекты службы Session Management Service, доступные в серверном JavaScript и предназначенные для обеспечения совместного использования данных несколькими клиентскими запросами к приложению, несколькими пользователями одного приложения или даже несколькими приложениями на сервере.

Session Management Service это набор возможностей для управления созданием и уничтожением различных предопределённых объектов в ходе сессии сервера. Эти возможности предоставлены через объекты request, client, project и server.

Помимо этого Вы можете конструировать экземпляры Lock для управления доступом к совместно используемой информации. Lock -экземпляры дают возможность точно управлять использованием информации, устанавливая исключительный доступ к специфицированным объектам.

В главе имеются следующие разделы:

Предопределённые Объекты. Обзор.


Предопределённые объекты request, client, project и server содержат данные, существующие в течение различных периодов времени и доступные различным клиентам и приложениям. Имеется единственный объект server, используемый всеми запущенными на сервере приложениями. Имеется отдельный объект project для каждого запущенного приложения. Имеется один объект client для каждого браузера (клиент), выполняющего доступ к данному отдельному приложению. Наконец, имеется отдельный объект request для каждого клиентского запроса с определённого клиента к определённому приложению. На Рисунке 6.1 показаны возможности относительной доступности различных объектов.

Рисунок 6.1   Относительная доступность объектов, обслуживающих сессию

Машина выполнения JavaScript на сервере конструирует объекты обслуживания сессии в разные моменты времени. Эти объекты применяются для хранения различных данных. Вы можете определить специфические для приложения свойства для любого из этих объектов.

Рисунок 6.2 может помочь представить, как все эти объекты соответствуют URL страницы Вашего приложения.

Рисунок 6.2   Предопределённые объекты в URL

На этом рисунке Joe запрашивает URL http://www.royalairways.com/videoapp/category.html, соответствующий странице приложения videoapp. Когда машина выполнения получает запрос, она использует уже существующий объект server, соответствующий www.royalairways.com и уже существующий объект project, соответствующий приложению videoapp. Машина создаёт объект client, соответствующий комбинации Joe и приложения videoapp. Если Joe уже получал доступ к другим страницам этого приложения, новый объект client использует любые сохранённые свойства. Наконец, машина создаёт новый объект request для конкретного запроса на страницу category.html.

Объект request


Объект request содержит данные, специфичные для текущего клиентского запроса. Они имеет самое короткое время существования из всех объектов. JavaScript конструирует новый объект request для каждого получаемого клиентского запроса; например, объект создаётся, когда

Машина выполнения JavaScript на сервере уничтожает объект request по окончании ответа на запрос (обычно предоставляя запрошенную страницу). Следовательно, типичный период существования объекта request - менее одной секунды.

ПРИМЕЧАНИЕ:

Вы не можете использовать объект request в начальной странице приложения. Эта страница запускается, когда приложение стартует на сервере. В этой точке нет объекта клиентского запроса, и поэтому нет доступного объекта request. О начальных страницах см. раздел "Установка Нового Приложения".

Резюме по объекту request см. а разделе "Предопределённые Объекты. Обзор.".

Свойства


В таблице перечислены предопределённые свойства объекта request. Некоторые из них соответствуют переменным окружения CGI. Вы можете получить доступ к этим и другим переменным окружения CGI через использование функции ssjs_getCGIVariable, описанной в разделе "Доступ к Переменным CGI".

Таблица 6.1  Свойства объекта request
Свойство Описание Пример значения
agent

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

Mozilla/1.1N (Windows; I; 32bit)
auth_type

Тип авторизации, если запрос защищён авторизацией какого-либо типа. Netscape web-серверы поддерживают базовую авторизацию доступа по HTTP. Соответствует переменной окружения CGI AUTH_TYPE.

basic
auth_user

Имя локального HTTP-пользователя web-браузера, если авторизация доступа HTTP была активирована для данного URL. Заметьте, что это не способ определения имени пользователя, получающего доступ к Вашей программе. Соответствует переменной окружения CGI REMOTE_USER.

vpg
ip

IP-адрес клиента. Может использоваться для авторизации или отслеживания доступа.

198.95.251.30
method

HTTP-метод, ассоциированный с запросом. Приложение может использовать его для определения соответствующего ответа на запрос. Соответствует переменной окружения CGI REQUEST_METHOD.

GET 1
protocol

Уровень протокола HTTP, поддерживаемый клиентским программным обеспечением. Соответствует переменной окружения CGI SERVER_PROTOCOL.

HTTP/1.0
query

Информация из запрашивающей HTML-страницы; это информация в URL, идущая после знака "?". Соответствует переменной окружения CGI QUERY_STRING.

button1=on&button2=off
imageX

Горизонтальная позиция курсора, когда пользователь щёлкает на карте изображений/image map. Описано в разделе "Работа с Картами Изображений".

45
imageY

Вертикальная позиция курсора, когда пользователь щёлкает на карте изображений. Описано в разделе "Работа с Картами Изображений".

132
uri

Частичный URL запроса: с вырезанными протоколом, именем хоста и, возможно имеющимся, номером порта.

videoapp/add.html

1 Для HTTP 1.0 method имеет одно из значений: GET, POST или HEAD. Если Вы объявляете переменные верхнего уровня в серверном JavaScript, они имеют тот же период существования, что и свойства объекта request. Например, такое объявление существует только на протяжении текущего запроса:

var number = 42;

Помимо предопределённых свойств, Вы можете иметь в клиентском коде информацию, которая будет становиться свойствами объекта request. Это делается через использование элементов формы и кодирование свойств в URL запроса, как описано в разделе "Отправка Значений с Клиента на Сервер".

Хотя Вы можете создавать дополнительные свойства объекта request непосредственно операторами серверного JavaScript, производительность будет выше, если использовать переменные JavaScript. Создаваемые Вами свойства объекта request могут быть любого допустимого в JavaScript типа, включая ссылки на другие объекты JavaScript.

Помните, что период существования объекта request и, следовательно, его свойств, это период действия запроса. Если Вы сохраняете ссылку на другой объект в объекте request, то объект, на который ссылаются, уничтожается вместе с объектом request, если только объект, на который ссылаются, не имеет на себя другой действующей ссылки, прямой или косвенной, с объекта server или project.

Работа с Картами Изображений/Image Maps


Атрибут ISMAP тэга IMG указывает на серверную карту изображений. Если пользователь щёлкает на карте, горизонтальная и вертикальная позиции курсора высылаются на сервер. Свойства imageX и imageY возвращают эти координаты. Рассмотрим такой HTML:

<A HREF="mapchoice.html">
<IMG SRC="images\map.gif" HEIGHT=599 WIDTH=424 BORDER=0
   ISMAP ALT="SANTA CRUZ COUNTY">
</A>

Страница mapchoice.html получает свойства request.imageX и request.imageY на основе позиции курсора в момент щелчка мышью.

Объект client


Несколько браузеров-клиентов могут иметь одновременный доступ к приложению JavaScript. Объект client предоставляет метод для работы отдельно с каждым клиентом. Он также имеет технологию для отслеживания работы каждого браузера-клиента с приложением при наличии нескольких запросов.

Машина выполнения JavaScript на сервере конструирует объект client для каждой пары клиент/приложение. Браузер-клиент, соединённый с одним приложением, имеет другой объект client из того же самого браузера-клиента, соединённого с другим приложением. Машина выполнения конструирует новый объект client каждый раз, когда пользователь выполняет доступ к приложению; могут быть сотни и тысячи объектов client, активных одновременно.

ПРИМЕЧАНИЕ:

Вы не можете использовать объект client на начальной странице Вашего приложения. Эта страница начинает работу при старте приложения на сервере. В этот момент клиентского запроса нет, и поэтому нет также и доступного объекта client. Дополнительно см. раздел "Установка Нового Приложения".

Машина выполнения конструирует и уничтожает объект client для каждого клиентского запроса. В то же время, при обработке запроса машина выполнения сохраняет имена и значения свойств объекта client. Таким способом машина выполнения может конструировать новый объект client из сохранённых данных, если тот же самый пользователь вновь использует приложение, сделав следующий запрос. Таким образом, концептуально Вы можете представлять объект client как объект, действующий в течение сессии работы клиента с приложением.

JavaScript не сохраняет объекты client, не имеющие значений свойств. Поэтому, если приложению не нужны объекты client и оно не присваивает свойствам объекта client никаких значений, оно не выполняет никакой лишней работы.

У Вас имеются несколько различных опций: как и где машине выполнения сохранять свойства объекта client. Эти опции обсуждаются в разделе "Технология Обслуживания Объекта client".

Резюме по объекту client см. в разделе "Предопределённые Объекты. Обзор".

Свойства


В объекте client отсутствуют значения предопределённых свойств, поскольку он предназначен для хранения специфических для приложения данных. Операторы JavaScript могут присваивать специфичные для приложения свойства и значения объекту client. Хорошим примером свойства объекта client является ID-номер потребителя. Когда пользователь в первый раз вызывает приложение, оно обязано присвоить customer ID, как в следующем примере:

client.custID = getNextCustID();

Здесь определяемая в приложении функция getNextCustID используется для вычисления customer ID. Машина выполнения затем присваивает этот ID свойству custID объекта client.

После установки customer ID может оказаться неудобным требовать от пользователя ввода ID на каждой странице приложения. Однако без использования объекта client нет иной возможности ассоциировать корректный customer ID с последующими запросами клиента.

Из-за использования такой техники для обслуживания свойств объекта client при наличии нескольких клиентских запросов имеется одно важное ограничение для значений свойств объекта client. Машина выполнения JavaScript не сервере конвертирует значения всех свойств объекта client в строки.

Не присваивайте объект в качестве значения свойства объекта client. Если Вы это сделаете, машина выполнения конвертирует этот объект в строку; после этого Вы не сможете больше работать с этим объектом. Если значение клиентского свойства представляет другой тип данных, например, number, Вы обязаны конвертировать строковое значение перед тем как его использовать. Например, Вы можете создать целочисленное свойство объекта client:

client.totalNumber = 17;

Затем Вы можете использовать parseInt для инкремента значения totalNumber:

client.totalNumber = parseInt(client.totalNumber) + 1;

Аналогично Вы можете создать Булево свойство объекта client:

client.bool = true;

А затем проверить его:

if (client.bool == "true")
   write("It's true!");
else
   write("It's false!");

Заметьте, что условное выражения сравнивает client.bool со строкой "true". Можно использовать эту технику для обработки Булева выражения. Например, для отрицания Булева свойства используйте такой код:

client.bool = (client.bool == "true") ? false : true;

Хотя Вы можете работать непосредственно со свойствами объекта client, Вы выполняете таким образом лишнюю работу. Если Вы повторно используете значение свойства объекта client, предусмотрите использование переменных верхнего уровня JavaScript. Перед использованием свойства объекта client, присвойте его переменной. Когда Вы закончите работу с этой переменной, присвойте результат обратно соответствующему свойству объекта client. Эта техника несколько повысит производительность среды.

Как отмечено ранее, Вы не можете сохранять ссылки на другие объекты в объекте client. Вы можете, однако, сохранять ссылки на объекты в объектах project или server. Если у вас имеется свойство, ассоциированное с клиентом, принимающее значения объекта, создайте массив, индексированный по клиентским ID, и храните ссылку на массив в объекте project или server. Вы можете использовать этот массив для хранения значений объекта, ассоциированного с клиентом. Рассмотрим следующий код:

if client.id == null
   client.id = ssjs_generateClientID();
project.clientDates[client.id] = new Date();

Здесь использована функция ssjs_generateClientID, описанная далее, для создания уникального ID для данного объекта client. Она использует этот ID как индекс массива clientDates в объекте project и сохраняет новый объект Date в этом массиве, ассоциированном с текущим объектом client.

Уникальная Ссылка на Объект client


Для некоторых приложений может понадобиться сохранять информацию, специфическую для пары клиент/приложение, в объектах project или server. Два обычных примера - сохранение соединения с БД между клиентскими запросами (описано в Главе 8, "Соединение с Базой Данных") или хранение специального объекта, имеющего такой же период существования, что и предопределённый объект client, и содержащего значения объектов (описано в разделе "Создание Специального Объекта client").

В этих случаях Вам необходим способ уникально обратиться к паре клиент/приложение. JavaScript имеет для этого две функции, ssjs_getClientID и ssjs_generateClientID. Они не принимают аргументов; обе возвращают уникальную строку, которую вы можете использовать для идентификации пары.

При каждом вызове ssjs_generateClientID машина выполнения возвращает новый идентификатор. Исходя из этого, если Вы используете эту функцию и Вам нужен идентификатор, существующий дольше, чем отдельный клиентский запрос, Вам нужно сохранить этот идентификатор, возможно, как свойство объекта client. Пример использования этой функции см. в разделе "Совместное Использование Массива Пула Соединений".

Если Вы используете эту функцию и сохраняете ID в объекте client, нужно проявлять осторожность, чтобы хакер не смог получить доступ к этому ID и, как следствие, к закрытой информации.

Альтернативой может стать использование функции ssjs_getClientID. Если Вы применяете одну из этих серверных технологий для объекта client, машина JavaScript генерирует и использует идентификатор для доступа к информации определённой пары клиент/приложение. (О работе с объектом client см. раздел "Технология Работы с Объектом client").

При использовании этих технологий ssjs_getClientID возвращает идентификатор, используемый машиной выполнения. Каждый раз при вызове этой функции из определённой пары клиент/приложение Вы будете получать тот же самый идентификатор. Соответственно, Вам нет необходимости сохранять идентификатор, возвращаемый функцией ssjs_getClientID. Однако, если Вы используете другую технику, эта функция возвращает "undefined"; тогда необходимо использовать функцию ssjs_generateClientID.

Если Вам нужен идентификатор и Вы используете серверную технологию, возможно, понадобится использовать функцию ssjs_getClientID. Тогда Вам не нужно будет самостоятельно сохранять идентификатор и отслеживать его использование; машина выполнения сделает это за Вас. При использовании клиентской техники, однако, Вы не сможете применять функцию ssjs_getClientID; тогда Вы обязаны использовать функцию ssjs_generateClientID.

Создание Специального Объекта client


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

В этом разделе приведён пример создания такого объекта. Можете включить этот код как файл JavaScript в Ваше приложение. Затем в начале страницы, на которой нужно использовать этот объект, введите следующий оператор:

var customClient = getCustomClient()

(Разумеется, можно использовать другое имя переменной.) Если это первая страница, запрашивающая данный объект, метод getCustomClient создаёт новый объект. На других страницах он будет возвращать уже существующий объект.

Этот код сохраняет массив всех специальных объектов client, определённых в приложении как значения свойства customClients предопределённого объекта project. Он сохраняет индекс в этом массиве и строковое значение свойства customClientID предопределённого объекта client. Кроме того, этот код использует блокировку/lock, хранимую в свойстве customClientLock объекта project, чтобы гарантировать надёжность доступа к этому массиву. О блокировании см. раздел "Безопасное Совместное Использование Объектов с Блокировкой".

Переменная timeout в функции getCustomClient жёстко кодирует период окончания действия этого объекта. Если Вам нужен другой период окончания действия, специфицируйте другое значение для этой переменной. Независимо от используемого периода действия, Вы должны вызывать метод expiration предопределённого объекта client для установки его срока окончания действия в то же значение, какое специфицировано Вами для специального объекта. О работе этого метода см. раздел "Период Существования Объекта client".

Для удаления всех закончивших работу специальных объектов приложения вызовите следующую функцию:

expireCustomClients()

Это всё, что нужно сделать! Если Вы используете этот код, предопределённые объекты client и project имеют следующие дополнительные свойства, которые Вы не должны изменять:

Вы можете специализировать класс путём изменения его методов onInit и onDestroy. Как показано здесь, эти методы - это просто основа. Вы можете добавить код для изменения действий при создании и уничтожении объекта.

Вот этот код:

// Эта функция создаёт новый специальный объект client или запрашивает существующий. function getCustomClient()
{
   // ==========> Измените жёстко кодированный период ожидания <==========
   // Примечание: Не забудьте установить окончание обслуживания client-статуса
   // в то же самое значение, что и использованное ниже в вызове
   // client.expiration. Это даст возможность индексу отключать все предопределённые
   // объекты client в то же время, которое содержится в объекте project.
   var timeout = 600;
   var customClient = null;
   var deathRow = null;
   var newObjectWasCreated = false;
   var customClientLock = getCustomClientLock();
   customClientLock.lock();
   var customClientID = client.customClientID;
   if ( customClientID == null ) {
      customClient = new CustomClient(timeout);
      newObjectWasCreated = true;
   }
   else {
      var customClients = getCustomClients();
       customClient = customClients[customClientID];
       if ( customClient == null ) {
         customClient = new CustomClient(timeout);
         newObjectWasCreated = true;
      }
      else {
         var now = (new Date()).getTime();
         if ( customClient.expiration <= now ) {
            delete customClients[customClientID];
            deathRow = customClient;
             customClient = new CustomClient(timeout);
             newObjectWasCreated = true;
         }
         else {
             customClient.expiration = (new Date()).getTime() +
               timeout*1000;
         }
      }
   }
   if ( newObjectWasCreated )
       customClient.onInit();
   customClientLock.unlock();
   if ( deathRow != null )
       deathRow.onDestroy();
   return customClient;
}
// Функция для удаления старых специальных объектов client.
function expireCustomClients()
{
   var customClients = getCustomClients();
   var now = (new Date()).getTime();
   for ( var i in customClients ) {
      var clientObj = customClients[i];
      if ( clientObj.expiration <= now ) {
         var customClientLock = getCustomClientLock();
         customClientLock.lock();
         if ( clientObj.expiration <= now ) {
            delete customClients[i];
         }
         else {
            clientObj = null;
         }
         customClientLock.unlock()
         if ( clientObj != null )
            clientObj.onDestroy();
      }   }   }
// Не вызывайте эту функцию явно.
// Она используется методами getCustomClient и expireCustomClients.
function getCustomClientLock()
{
    if ( project.customClientLock == null ) {
      project.lock()
      if ( project.customClientLock == null )
         project.customClientLock = new Lock()
      project.unlock()
   }
   return project.customClientLock
}
// Не вызывайте эту функцию явно.
// Она используется методами getCustomClient и expireCustomClients.
function getCustomClients()
{
    if ( project.customClients == null ) {
      project.lock()
      if ( project.customClients == null )
         project.customClients = new Object()
      project.unlock()
   }
   return project.customClients
}
// Конструктор класса CustomClient. Не вызывайте его явно.
// Используйте вместо него функцию getCustomClient.
function CustomClient(seconds)
{
   var customClients = getCustomClients();
   var customClientID = ssjs_generateClientID();
   this.onInit = CustomClientMethod_onInit;
   this.onDestroy = CustomClientMethod_onDestroy;
   this.expiration = (new Date()).getTime() + seconds*1000;>
   client.customClientID = customClientID;    customClients[customClientID] = this;
}
// Если нужно специализировать, переопределите следующие две функции.
function CustomClientMethod_onInit()
{
   // ==========> Добавьте код инициализации Вашего объекта <==========
   // Этот метод вызывается при блокировке.
}
function CustomClientMethod_onDestroy()
{
   // ==========> Добавьте код очистки Вашего объекта <==========
   // Этот метод не вызывается из блокировки.
}

Объект project


Объект project содержит глобальные данные приложения и предоставляет метод для совместного использования информации клиентами, выполняющими доступ к приложению. JavaScript конструирует новый объект project, когда приложение стартует при использовании Application Manager. Каждый клиент, получающий доступ к приложению, использует один и тот же объект project. Резюме по объекту project см. в разделе "Предопределённые Объекты. Обзор.".

В отличие от предыдущих релизов, в этом релизе машина выполнения JavaScript не создаёт и не уничтожает объект project для каждого запроса. Если Вы остановили работу приложения, объект project этого приложения уничтожается. Новый объект project создаётся для приложения, когда оно стартует снова. Типичный период существования объекта project - дни или недели.

JavaScript конструирует набор объектов project для каждого Netscape HTTP-процесса, запущенного на сервере. JavaScript конструирует объект project для каждого приложения, запущенного на каждом отдельном сервере. Например, если один сервер запущен на порте 80, а другой - на порте 142 на той же самой машине, JavaScript конструирует отдельный набор объектов project для каждого процесса.

Свойства


У объекта project нет предопределённых свойств, поскольку он предназначен для содержания специфических для приложений данных, доступных для многих клиентов. Вы можете создавать свойства любого верного типа JavaScript, включая ссылки на другие JavaScript-объекты. Если Вы храните ссылку на другой объект в объекте project, машина выполнения не уничтожает объект, на который ссылаются, по окончании клиентского запроса, в котором объект создаётся. Объект доступен и в течение последующих запросов.

Хороший пример свойства объекта project - следующий доступный ID потребителя. Приложение может использовать это свойство для отслеживания последующих присваиваемых IDs. Любому клиенту, получающему доступ к приложению и не имеющему ID потребителя, присваивается этот ID, и его значение будет увеличиваться для каждого первоначального доступа.

Помните, что объект project существует только в течение периода работы приложения на сервере. Когда приложение останавливается, объект project уничтожается вместе со всеми значениями его свойств. Поэтому, если у Вас имеются данные приложения, которые нужно сохранять постоянно, их необходимо сохранять в БД (см. Часть 3, "LiveWire Database Service") или в файле на сервере (см. "Служба Файловой Системы").

Совместное Использование Объекта project


Для каждого приложения имеется только один объект project. Таким образом, код, исполняемый в любом запросе к данному приложению, может получить доступ к тому же объекту project. Поскольку сервер является многопоточным, могут иметься несколько активных запросов в данный момент времени, либо от одного и того же клиента, либо от разных клиентов.

Для поддержания целостности данных Вы обязаны гарантировать исключительный доступ к свойству объекта project при изменении значения свойства. Неявной блокировки, как это было в предыдущих релизах, больше нет; Вы обязаны запрашивать исключительный доступ. Легче всего сделать это через использование методов lock и unlock объекта project. См. раздел "Безопасное Совместное Использование Объектов с Помощью Блокировки.".

Объект server


Объект server содержит глобальные данные для всего сервера в целом и предоставляет метод для обеспечения совместного использования информации разными приложениями, работающими на сервере. Объект server также автоматически инициализируется информацией о сервере. Резюме по объекту server см. в разделе "Предопределённые Объекты. Обзор.".

Машина выполнения JavaScript конструирует новый объект server, когда сервер стартует, и уничтожает объект server, когда сервер останавливается. Каждое приложение, запущенное на сервере, использует один и тот же объект server.

JavaScript конструирует объект server для каждого Netscape HTTPD-процесса (на сервере), запущенного на машине. Например, может иметься серверный процесс на порте 80 и другой - на порте 8080. Это совершенно отдельные серверные процессы, и JavaScript конструирует объект server для каждого из них.

Свойства


В следующей таблице описаны свойства объекта server.

Таблица 6.2  Свойства объекта server
Свойство Описание Пример
hostname

Полное имя хоста сервера, включая номер порта

www.netscape.com:85
host

Имя сервера, субдомена и домена

www.netscape.com
protocol

Используемый протокол соединения

http:
port

Используемый номер порта сервера; по умолчанию 80 для HTTP

85
jsVersion

Версия сервера и платформа

3.0 WindowsNT

Например, Вы можете использовать свойство jsVersion для обусловливания возможностей на базе серверной платформы (или версии), на которой приложение работает, как показано здесь:

if (server.jsVersion == "3.0 WindowsNT")
   write ("Application is running on a Windows NT server.");

Помимо этих автоматически инициализируемых свойств, Вы можете создавать свойства для хранения данных, совместно используемых многими приложениями. Свойства могут иметь любой допустимый в JavaScript тип, включая ссылки на другие JavaScript-объекты. Если Вы сохраняете ссылку на другой объект в объекте server, машина выполнения не разрушает объект, на который ссылаются, по окончании запроса, в ходе которого он (объект server) был создан. Объект остаётся доступным для последующих запросов.

Как и случае с объектом project, объект server имеет ограниченный период существования. Когда web-сервер останавливается, объект server разрушается вместе со всеми значениями свойств. Поэтому, если у Вас имеются данные приложения, которые нужно сохранять постоянно, их необходимо сохранять в БД (см. Часть 3, "LiveWire Database Service") или в файле на сервере (см. "Служба Файловой Системы").

Совместное Использование Объекта server


Для всего сервера имеется один объект server. Таким образом, код, выполняемый в любом запросе, в любом приложении, может иметь доступ к одному и тому же объекту server. Поскольку сервер является многопоточным, могут иметься несколько запросов в данный момент времени. Для поддержания целостности данных Вы обязаны гарантировать исключительный доступ к объекту server при внесении изменений.

Также Вы обязаны гарантировать, что имеется исключительный доступ к свойству объекта server, когда изменяется значение этого свойства. Неявной блокировки, как это было в предыдущих релизах, теперь нет; Вы обязаны запрашивать исключительный доступ. Легче всего сделать это через использование методов lock и unlock объекта server. См. раздел "Безопасное Совместное Использование Объектов с Помощью Блокировки".

Технология Обслуживания Объекта client


Объект client ассоциирован как с определённым приложением, так и с определённым клиентом. Как было сказано в разделе "Объект client", машина выполнения создаёт новый объект client всякий раз при поступлении нового запроса от клиента к серверу. Однако целью является сохранение свойств объекта client от одного запроса до следующего. Для этого машине выполнения нужно сохранить свойства объекта client между запросами.

Есть два основных подхода при работе со свойствами объекта client: можно работать с ними на стороне клиента или на сервере. Эти два вида клиентской техники либо сохраняют имена свойств и их значения непосредственно в куках на клиенте, либо в URLs на генерируемой HTML-странице. Все три вида серверной техники сохраняют имена свойств и их значения в структуре данных в памяти сервера, но различаются по используемой для индексирования структуры этих данных схеме.

Вид техники выбирается, когда Вы используете JavaScript Application Manager для инсталяции или модификации приложения, как указано в разделе "Установка Нового Приложения". Это даёт Вам (или менеджеру сайта) возможность изменять технику обслуживания без перекомпилирования приложения. Однако поведение Вашего приложения может меняться в зависимости от действующей техники обслуживания объекта client, как описано в следующих разделах. Обязательно объясните Вашему менеджеру сайта, от какого вида техники зависит работа Вашего приложения. Иначе менеджер может изменить эти установки и нарушить работу Вашего приложения.

Поскольку некоторые виды этой техники сохраняют информацию в структуре данных на сервере или в куки-файле на клиенте, машина выполнения JavaScript дополнительно должна определять, когда избавиться от этих свойств. В разделе "Период Существования Объекта client" рассматривается, как машина выполнения определяет это, и описываются методы, которые можно использовать для изменения этого поведения.

Сравнение Видов Техники Обслуживания Объекта client


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

В таблице выполнено общее сравнение клиентской и серверной техники.

Таблица 6.3  Сравнение клиентской и серверной техники обслуживания
Серверная Клиентская

1.

Не ограничивается количество хранимых свойств или занимаемое ими пространство.

Ограничения на свойства.

2.

Занимает дополнительную память сервера в промежутке между клиентскими запросами.

Не использует дополнительную память сервера в промежутке между клиентскими запросами.

Эти различия относительны. Отсутствие ограничения на количество и размер свойств может быть и недостатком, и преимуществом. Вообще нужно ограничивать размер данных приложения, доступных через Internet, чтобы не перегрузить память Вашего сервера. Иначе лучше использовать клиентскую технику. Однако, если у вас приложение для Intranet (внутренней сети), где нужно хранить большой объём данных, можно допустить это на сервере, так как количество клиентов ограничено.

3.

Свойства хранятся в памяти сервера и теряются при рестарте сервера или приложения.

Свойства не хранятся в памяти сервера и не теряются при рестарте сервера.

Если свойства являются настройками пользователя, Вам может понадобиться сохранить их между рестартами сервера; если они относятся к отдельной сессии, может понадобиться, чтобы они были стёрты.

4.

Не увеличивает или незначительно увеличивает сетевой трафик.

Увеличивает сетевой трафик.

Клиентская техника пересылает имя и значение каждого свойства клиенту один или более раз. Это значительно увеличивает сетевой трафик.

Поскольку все серверные виды техники хранят имена и значения свойств на сервере, максимум, что они пересылают клиенту, это сгенерированное имя клиента, используемое при идентификации доступа к структуре данных сервера.

На Рисунке 6.3 и на Рисунке 6.4 видно, какая информация хранится при использовании каждого вида техники, где она хранится и передаётся ли по сети. На Рисунке 6.3 дана информация для клиентской техники.

Рисунок 6.3   Клиентская техника


На Рисунке 6.4 дана информация для серверной техники.

Рисунок 6.4   Серверная техника

Имеются некоторые общие для видов (серверной и клиентской) техники вопросы. Для обоих типов техники, использующих куки, браузер обязан поддерживать протокол Netscape cookie protocol. В обоих случаях, когда браузер на клиентской машине закрывается, информация сохраняется в cookie-файле на клиентской машине. В других случаях ограничений нет.

Техника серверных кук создаёт единственную куку для идентификации соответствующего объекта client. В противоположность этому, техника клиентских кук создаёт отдельную куку для каждого свойства объекта client. На технику клиентских кук, следовательно, скорее повлияет ограничение в 20 кук на приложение.

В технике клиентских кук свойства объекта client высылаются клиенту, когда высылается первая часть HTML-страницы. Если Вы изменяете позднее значения свойств объекта client при выполнении действий на странице, эти изменения не отсылаются клиенту и теряются. Это ограничение не действует для другой техники.

Для обеих техник, использующих кодирование в URL, если Ваше приложение конструирует URL на этапе выполнения или использует функцию redirect, необходимо либо вручную присоединять свойства объекта client, которые должны быть сохранены, либо использовать addClient, чтобы машина выполнения присоединила эти свойства. Хотя присоединение свойств не является обязательным для других техник, Вам может понадобиться сделать это, чтобы изменение техники не нарушило работу Вашего приложения.

Кроме того, при использовании техник кодирования URL, как только браузер перейдёт на страницу за пределами приложения или даже отправит форму приложению с использованием метода GET, все свойства объекта client будут утеряны. Свойства не теряются в такой ситуации для других видов техники. Ваш выбор техники частично определяется тем, должны ли существовать свойства объекта client в такой ситуации.

Ваш выбор используемой техники опирается на требования Вашего приложения. Техника клиентских кук не использует дополнительной памяти сервера (как при серверной технике) и высылает информацию только один раз для страницы (в противоположность клиентской технике кодирования URL). Эти факты могут сделать использование техники клиентских кук предпочтительным для больших Internet-приложений. Однако в некоторых случаях другая техника может оказаться более подходящей. Например, серверный IP-адрес работает быстрее, не увеличивая сетевого трафика. Можно использовать это для приложений Вашей Intranet, для которых скорость работы является критичной.

Клиентская Техника


Есть два вида клиентской техники:

Сравнение этих видов техники см. в разделе "Сравнение Видов Техники Обслуживания Объекта сlient".

Когда приложение использует клиентские виды техники, машина выполнения кодирует свойства объекта client в ответ на клиентский запрос в шапке/header ответа (для клиентской куки) или в URLs в теле ответа (для клиентского кодирования URL).

Поскольку реальные имена и значения свойств пересылаются между клиентом и сервером, рестарт сервера не вызывает потери клиентской информации. Однако отправка этой информации вызывает увеличение сетевого трафика.

Использование Клиентской Куки/Cookie


В технике клиентских кук машина выполнения JavaScript на сервере использует протокол Netscape cookie protocol для передачи клиенту свойств объекта client и их значений. Она создаёт по одной куке для каждого свойства объекта client. Свойства высылаются клиенту один раз в шапке/header ответа генерируемой HTML-страницы. Netscape cookie protocol описан в книге Клиентский JavaScript. Руководство .

Для исключения конфликтов с другими куками, которые Вы можете создать в Вашем приложении, машина выполнения создаёт имя куки, добавляя NETSCAPE_LIVEWIRE. перед началом имени свойства объекта client. Например, если client имеет свойство custID, машина выполнения создаёт куку под названием NETSCAPE_LIVEWIRE.custID. Когда информация куки высылается клиенту, машина выполнения делает всё необходимое кодирование специальных символов в значении свойства, как описано в книге Клиентский JavaScript. Руководство .

Иногда Вашему приложению может понадобиться взаимодействие операторов JavaScript на сервере и на стороне клиента. Поскольку это вид техники высылает клиенту свойства объекта client как куки, Вы можете использовать это как способ облегчить это взаимодействие. См. дополнительно "Взаимодействие Между Сервером и Клиентом".

При использовании этой техники машина выполнения сохраняет свойства объекта client, когда она в первый раз очищает внутренний буфер, содержащий сгенерированную HTML-страницу. Исходя из этого, для того чтобы предотвратить потерю любой информации, Вы должны как можно раньше присвоить значения всем свойствам объекта client в скриптах на каждой странице. В особенности Вы должны гарантировать, что свойства объекта client будут высылаться перед тем как (1) машина выполнения сгенерирует 64KB содержимого HTML-страницы (она автоматически очищает буфер вывода в этой точке), (2) Вы вызовете функцию flush для очистки буфера вывода или (3) Вы вызовете функцию redirect для изменения клиентских запросов. Дополнительно см. разделы "Очистка Буфера Вывода" и "Процессинг Времени Выполнения на Сервере".

По умолчанию, когда Вы используете технику клиентских кук, машина выполнения не устанавливает явно время окончания срока действия кук. В этом случае куки заканчивают работать, когда пользователь закрывает браузер. (Это поведение по умолчанию для всех кук.) Как указано в разделе "Период Существования Объекта client", Вы можете использовать метод expiration объекта client для изменения срока окончания действия. Если Вы используете client.expiration, машина выполнения устанавливает соответствующий срок окончания работы куки в cookie-файле.

При использовании техники клиентских кук метод client.destroy уничтожает все значения свойств объекта client, но не влияет на то, что хранится в cookie-файле на клиентской машине. Не используйте для удаления кук из cookie-файла или памяти браузера метод client.destroy; вместо него используйте client.expiration с аргументом 0 секунд.

В целом Netscape-куки имеют нижеследующие ограничения. Эти ограничения применяются тогда, когда Вы используете куки для хранения свойств объекта client:

Использование Клиентского Кодирования URL


При использовании техники клиентского кодирования URL машина выполнения на сервере пересылает клиенту свойства и значения объекта client, присоединяя их к каждому URL в генерируемой HTML-странице. Соответственно свойства и их значения пересылаются столько раз, сколько имеется гиперссылок на генерируемой HTML-странице, что приводит к значительному увеличению сетевого трафика.

Размер строки URL ограничен 4KB. Следовательно, когда Вы используете клиентское кодирование URL, общий размер имён свойств и их значений не может превышать 4KB. Любая информация свыше лимита 4KB будет усекаться.

Если Вы генерируете URLs динамически или используете функцию redirect, Вы можете добавлять свойства объекта client или другие свойства к URL. Когда Вы вызываете redirect или генерируете URL, компилятор не присоединяет автоматически свойства объекта client. Если присоединение необходимо, используйте функцию addClient. См. раздел "Присоединение Свойств Объекта client к URL Вручную".

В технике клиентского кодирования URL значения свойств добавляются к URL по мере обработки этих URL. Нужно следить, чтобы Ваши URL имели одинаковые свойства и значения. Например, рассмотрим код:

<SERVER>
...
client.numwrites = 2;
write (addClient(
   "<A HREF='page2.html'>Some link</A>"));
client.numwrites = 3;
write (addClient(
   "<A HREF='page3.html'>Another link</A>"));
...
</SERVER>

Когда машина выполнения обрабатывает первый оператор write, она использует 2 как значение свойства numwrites, а при обработке второго оператора write она использует в качестве значения 3.

Итак, если Вы используете метод client. destroy в середине страницы, только ссылки, шедшие на странице до вызова этого метода получат значения, присоединённые к URL. Те же, которые идут после вызова этого метода, не имеют присоединённых значений. Следовательно, значения свойств объекта client передаются на некоторые страницы, но не на все. Это может быть нежелательно.

Если страница имеет ссылку на URL за пределами Вашего приложения, Вам не понадобится присоединять клиентский статус. Тогда не используйте статическую строку в качестве значения HREF. Вместо этого вычисляйте значение. Это предотвратит автоматическое присоединение машиной выполнения клиентского статуса к URL. Например, у вас имеется ссылка:

<A HREF="mailto:me@royalairways.com">

Машина выполнения присоединяет свойства объекта client. Чтобы этого не происходило, используйте очень похожую ссылку:

<A HREF=`"mailto:me@royalairways.com"`>

При этой технике объект client не перестаёт действовать, поскольку существует только в URL-строке, находящейся на клиенте. Следовательно, метод client.expiration не производит никаких действий.

При клиентском кодировании URL Вы теряете все свойства объекта client, когда отправляете форму, используя метод GET, и когда выполняете доступ к другому приложению. Ещё раз - Вам может быть нужно или не нужно терять эти свойства, в зависимости от потребностей Вашего приложения.

В отличие от техники клиентских кук, клиентское кодирование URL не требует ни поддержки web-браузером протокола Netscape cookie, ни записи информации на клиентской машине.

Серверная Техника


Есть три вида серверной техники:

Сравнение разных видов техники см. в разделе "Сравнение Видов Техники Обслуживания Объекта сlient".

При любом виде техники машина выполнения на сервере сохраняет свойства объекта client и их значения в структуре данных в памяти сервера. Единая структура данных, сохраняемая в период между клиентскими запросами, используется для всех приложений, работающих на сервере. Виды техники различаются только в индексе, используемом для доступа к информации в этой структуре данных, гарантируя, что каждая пара клиент/приложение получает соответствующие свойства и значения для объекта client.

Ни одна из этих техник не записывает информацию на жёсткий диск сервера. Только техника серверных кук позволяет записывать информацию на диск клиентской машины при окончании работы браузера.

Поскольку эти виды техники сохраняют информацию объектов client в памяти сервера в промежутке между клиентскими запросами, нет или почти нет увеличения сетевого трафика. Имена и значения свойств никогда не пересылаются клиенту. Кроме того нет ограничения на количество свойств объекта client и на размер свойства.

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

Использование IP-Адреса


Техника с использованием IP-адреса индексирует структуру данных на основе IP-адресов приложения и клиента. Эта простая техника является также и самой быстрой, поскольку вообще не требует отправки информации клиенту. Так как индекс базируется на IP-адресах приложения и клиента, эта техника создаёт отдельный индекс для каждой пары приложение/клиент, работающей на сервере.

Эта техника хорошо работает, когда все клиенты имеют фиксированные IP-адреса. Она работает ненадёжно, если клиент не имеет гарантированно фиксированного IP-адреса, например, если клиент использует протокол Dynamic Host Configuration Protocol (DHCP) или провайдера Internet, который динамически размещает IP-адреса. Эта техника также не работает у клиентов, использующих прокси-сервер, поскольку все пользователи прокси сообщают один и тот же IP-адрес. Поэтому данная техника используется в основном только для приложений Intranet.

Использование Серверных Кук


Техника серверных кук использует длинное уникальное имя, генерируемое машиной выполнения для индексации структуры данных на сервере. Машина выполнения использует протокол Netscape cookie для хранения генерируемого имени как куки/cookie на клиенте. Она не сохраняет имена и значения свойств как куки. Поэтому данная техника создаёт одну куку, в то время как клиентская техника кук создаёт отдельную куку для каждого свойства объекта client.

Сгенерированное имя отсылается клиенту только один раз в шапке/header HTML-страницы. Вы можете получить доступ к этому имени через функцию ssjs_getClientID, описанную в разделе "Уникальное Обращение к Объекту client". Эта техника использует тот же самый cookie-файл, что и техника клиентских кук; эти виды техники отличаются тем, что информация сохраняется в cookie-файле. Протокол Netscape cookie protocol описан в книге Клиентский JavaScript. Руководство .

Итак, поскольку клиенту отсылается только генерируемое имя, а не реальные имена и значения свойств, не имеет значения, где на Вашей странице изменяются свойства объекта client. Это контрастирует с техникой клиентских кук.

По умолчанию машина выполнения устанавливает период действия серверной структуры данных в 10 минут и не устанавливает срок действия кук, отправляемых клиенту. Как указано в разделе "Период Существования Объекта client", Вы можете использовать метод expiratio объекта client для изменения срока действия и для установки периода действия куки.

При использовании серверной куки метод client.destroy уничтожает все значения свойств объекта client.

В общем, Netscape-куки имеют ограничения, перечисленные в разделе "Использование Клиентских Кук". Если Вы используете серверные куки, эти ограничения вряд ли будут достигнуты, так как создаётся только одна кука (содержащая индекс).

Это быстрая техника, не имеющая встроенных ограничений на количество и размер свойств и их значений. Вы больше ограничены тем, сколько пространства будете использовать на Вашем сервере для хранения этой информации.

Использование Серверной Кодировки URL


Техника серверного кодирования URL использует длинное уникальное имя, генерируемое машиной выполнения для индексации структуры данных на сервере. В этом случае, вместо того чтобы сделать это генерируемое имя клиентской кукой, сервер присоединяет имя к каждому URL на генерируемой HTML-странице. Следовательно, имя высылается столько раз, сколько имеется ссылок на генерируемой HTML-странице. (Имена и значения свойств не присоединяются к URLs, только генерируемое имя.) Ещё раз: Вы можете получить доступ к этому генерируемому имени с помощью функции ssjs_getClientID, описанной в разделе "Уникальное Обращение к Объекту client".

Если Вы генерируете URLs динамически или используете функцию redirect, Вы можете добавлять свойства к URL. Поэтому, когда Вы вызываете redirect или генерируете URL, компилятор не присоединяет индекс автоматически. Если Вы хотите оставить индекс для свойств объекта client, используйте функцию addClient. См. также "Присоединение Свойств Объекта client к URL Вручную".

Если Ваша страница имеет ссылку на URL вне Вашего приложения, Вам может и не понадобиться присоединение клиентского индекса. Тогда не используйте статическую строку как значение атрибута HREF. Вместо этого вычисляйте это значение. Это предотвратит автоматическое присоединение машиной выполнения клиентского индекса к URL. Например, у Вас имеется ссылка:

<A HREF="mailto:me@royalairways.com">

В это случае машина выполнения присоединит индекс объекта client. Чтобы этого не происходило, используйте очень похожую ссылку:

<A HREF=`"mailto:me@royalairways.com"`>

При серверном кодировании URL вы теряете идентификатор объекта client (и, соответственно, свойства и их значения) при отправке формы с методом GET. Вы можете терять или не терять эти свойств, в зависимости от потребностей Вашего приложения.

Период Существования Объекта client


После того как клиент получил доступ к приложению, не гарантируется, будет он далее запрашивать продолжение обработки или продолжит выполнение до логического конца. В связи с этим объект client не имеет встроенного механизма окончания строка действия. Этот механизм позволяет JavaScript периодически "зачищать" старые объекты client, которые больше не нужны. Каждый раз, при получении сервером запроса на страницу приложения, JavaScript восстанавливает период существования объекта client.

Вызов Окончания Действия Свойств Объекта client


По умолчанию поведение механизма срока действия значений варьируется и зависит от вида используемой техники работы с объектом client, как видно из таблицы.

Таблица 6.4  Срок действия по умолчанию свойств объекта client на основе вида используемой техники
Для данного вида техники... Свойства Объекта client...

клиентская кука

Перестают действовать, когда браузер закрывается.

клиентское кодирование URL

Никогда не перестают действовать.

серверная кука

Удаляются из структуры данных на сервере через 10 минут. Кука на клиенте перестаёт действовать при выходе из браузера. Свойства объекта client более не доступны, как только структура данных удаляется или браузер закрывается.

серверное кодирование URL

Удаляются из структуры данных на сервере через 10 минут.

серверный IP-адрес

Удаляются из структуры данных на сервере через 10 минут.

Приложение может управлять периодом ожидания JavaScript перед зачисткой свойств объекта client. Для изменения величины этого периода используйте метод expiration, как в следующем примере:

client.expiration(30);

В ответ на это вызов машина выполнения зачищает свойства объекта client по прошествии 30 секунд. Для серверной техники этот вызов заставит сервер удалить свойства объекта из структур данных через 30 секунд. Для этих двух видов техники такой вызов устанавливает окончание срока действия через 30 секунд.

Если объект client перестаёт действовать, когда имеется активный клиентский запрос с использованием этого объекта, машина выполнения ждёт окончания этого запроса, прежде чем уничтожить объект client.

Вы обязаны вызывать expiration на каждой странице, срок окончания действия которой хотите специфицировать. Страницы, не специфицирующие срок действия, используют поведение по умолчанию.

Уничтожение Объекта client


Приложение может явно уничтожать объект client методом destroy:

client.destroy();

Когда приложение вызывает destroy, JavaScript удаляет все свойства из объекта client.

Если Вы используете технику клиентских кук для работы с объектом client, метод destroy уничтожает все значения свойств объекта client, но не влияет на то, что хранится в клиентском cookie-файле. Чтобы удалить и значения свойств из этого cookie-файла, не используйте метод destroy; вместо него используйте expiration с аргументом 0 секунд.

Если Вы используете технику клиентского кодирования URL для работы с объектом client, метод destroy удаляет все свойства объекта client. Ссылки на странице до вызова destroy оставляют свойства объекта client в своих URL, а ссылки, расположенные после вызова метода, не имеют свойств. Поскольку маловероятно, что Вам понадобится, чтобы  только некоторые URL страницы содержали свойства объекта client, Вы, вероятно, должны будете вызывать destroy либо вверху, либо внизу страницы, когда используете работу с клиентскими URL. См. также "Использование Клиентского Кодирования URL".

Присоединение Свойств Объекта client к URL Вручную


При использовании кодирования URL на клиенте или на сервере для работы с объектом client машина выполнения обычно должна сохранять соответствующую информацию (имена и значения свойств объекта client или индекс серверной структуры данных) во всех URL, высылаемых клиенту, вне зависимости от того, являются ли эти URL как статический HTML или были сгенерированы операторами серверного JavaScript.

Машина выполнения автоматически присоединяет соответствующую информацию к гиперссылкам HTML, находящимся вне тэгов SERVER. Так, например, предположим, что Ваша HTML-страница содержит следующие операторы:

<HTML>
For more information, contact
<A HREF="http://royalairways.com/contact_info.html">
Royal Airways</a>
...
</HTML>

Если приложение использует кодирование URL для объекта client, машина выполнения автоматически присоединит client -информацию в конец URL. Вы не должны ничего делать специально для поддержки этого поведения.

Однако ваше приложение может использовать функцию write для динамической генерации оператора HTML, содержащего URL. Вы можете также использовать функцию redirect для старта нового запроса. Когда Вы используете операторы серверного JavaScript для добавления URL к генерируемой HTML-странице, машина выполнения предполагает, что Вы специфицировали полный URL для отправки в нужном Вам виде. Она не присоединяет автоматически клиентскую информацию даже при использовании кодирования URL для работы с объектом client. Если Вам нужно присоединить клиентскую информацию, Вы обязаны сделать это сами.

Вы используете функцию addClient для добавления вручную соответствующей client -информации. Эта функция принимает URL и возвращает новый URL с присоединённой информацией. Например, предположим, что контактный URL варьируется в зависимости от значения свойства client.contact. Вместо вышеприведённого HTML Вы можете ввести следующее:

<HTML>
For more information, contact
<server>
if (client.contact == "VIP") {
   write ("<A HREF='http://royalairways.com/vip_contact_info.html'>");
   write ("Royal Airways VIP Contact</a>");
}
else {
   write ("<A HREF='http://royalairways.com/contact_info.html'>");
   write ("Royal Airways</a>");
}
</server>
...
</HTML>

Теперь машина выполнения не присоединяет свойства объекта client к URL. Если Вы используете один из видов техники кодирования URL для работы с объектом client, может возникнуть проблема. Тогда, если Вы хотите отправить свойства объекта client с этим URL, используйте такой код:

<HTML>
For more information, contact
<server>
if (client.contact == "VIP") {
   write (addClient(
      "<A HREF='http://royalairways.com/vip_contact_info.html'>"));
   write ("Royal Airways VIP Contact</a>");
}
else {
   write (addClient(
       "<A HREF='http://royalairways.com/contact_info.html'>"));
   write ("Royal Airways</a>");
}
</server>
...
</HTML>

Также всякий раз, когда Вы применяете функцию redirect для перенаправления клиентского запроса, Вы должны использовать addClient для присоединения информации, как здесь:

redirect(addClient("mypage.html"));

В противоположность этому, если Ваша страница имеет ссылку на URL вне Вашего приложения, Вам может не понадобиться присоединение клиентской информации. Тогда не используйте статическую строку в значении атрибута HREF. Вместо этого вычисляйте значение. Это предотвратит автоматическое присоединение машиной выполнения клиентского индекса или свойств к URL. Например, у вас имеется ссылка:

<A HREF="mailto:me@royalairways.com">

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

<A HREF=`"mailto:me@royalairways.com"`>

Хотя приложение первоначально инсталировано для использования техники без кодирования URL для работы с client, оно может быть позднее модифицировано для использования техники кодирования URL. Следовательно, если Ваше приложение генерирует динамические URL или использует redirect, Вам всегда нужно будет использовать addClient.

Безопасное Совместное Использование Объектов с Блокировкой/Locking


Рабочая среда для версии 3.x или 4.x Netscape-сервера является многопоточной; то есть она обрабатывает более одного запроса в единицу времени. Поскольку эти запросы могут требовать выполнения JavaScript, то более чем один поток выполнения JavaScript может быть активным в одно и то же время.

Если несколько потоков одновременно пытаются изменить свойство одного и того же объекта JavaScript, они могут привести этот объект в несоответствующее состояние. Участок кода, в котором необходимо выполнять один, и только один, поток выполнения в единицу времени, называется критическим разделом/сritical section.

Один объект server используется совместно всеми клиентами и всеми приложениями, работающими на сервере. Один объект project используется всеми клиентами, получающими доступ к одному приложению на сервере. Кроме того, Ваше приложение может создавать другие объекты, которые оно предоставляет в совместное пользование клиентским запросам, или оно даже может совместно с другими приложениями использовать объекты. Для поддержания целостности данных в этих совместно используемых объектах Вы обязаны получить исключительный доступ к объекту, прежде чем изменять любое его свойство.

Важно!

В отличие от предыдущих релизов, неявная блокировка объектов project и server теперь отсутствует.

Чтобы лучше понять, что происходит, рассмотрим следующий пример. Предположим, Вы создаёте совместно используемый объект project.orders для отслеживания заказов пользователей. Вы обновляете project.orders.count каждый раз при получении нового заказа, используя следующий код:

var x = project.orders.count;
x = x + 1;
project.orders.count = x;

Предположим, что project.orders.count первоначально установлено в 1 и что поступили два новых заказа в двух разных потоках. Произойдёт следующее:

  1. Первый поток сохраняет project.orders.count в переменной x.
  2. Прежде чем продолжить, второй поток запускается и сохраняет то же самое значение в своей копии переменной x.
  3. С этого момента оба потока имеют значение 1 в x.
  4. Второй поток завершает своё выполнение и устанавливает project.orders.count в 2.
  5. Первый поток продолжает выполнение, не зная, что значение project.orders.count изменилось, и также устанавливает 2 в х.

Итак, конечное значение project.orders.count будет 2, хотя корректным должно быть 3.

Чтобы избежать проблем такого рода, Вам нужно получить исключительный доступ к свойствам совместно используемых объектов перед тем как записывать в них. Для этих целей Вы можете конструировать Ваши собственные экземпляры класса Lock, работающие с любым совместно используемым объектом. Кроме того, объекты server и project имеют методы lock и unlock, которые Вы можете использовать для ограничения доступа к этим объектам.

Использование Экземпляров Класса Lock


Представьте lock (замок/блокировку) как именованный флаг, который Вы обязаны устанавливать перед входом в критичный раздел. Если Вы запрашиваете именованный флаг и кто-то уже имеет его, Вы будете ждать, пока этот второй не освободит флаг. В процессе ожидания Вы не сможете изменять то, что не должны изменять. После получения Вами флага кто-либо ещё будет ожидать и не сможет ничего изменить, пока Вы не освободите флаг. Если возникнет ошибка или таймаут закончится до того, как Вы получите флаг, Вы можете снова вернуться в режим ожидания, либо делать что-нибудь другое, как, например, дать Вашим пользователям знать, что приложение очень занято, чтобы выполнить данную операцию сейчас. Вы не должны вмешиваться в процесс ожидания (изменяя совместно используемую информацию)! Рисунок 6.5 иллюстрирует этот процесс.

Рисунок 6.5   Thread (поток) 2 ожидает, пока thread 1 имеет lock (замок)

В терминах программирования замок/lock представлен экземпляром класса Lock. Вы можете использовать экземпляр класса Lock для получения исключительного доступа к любому совместно используемому объекту. Обычно Вы создаёте экземпляры Lock на начальной странице Вашего приложения (по причинам, которые будет изложены позднее).

На других страницах, перед критичным для совместно используемого объекта разделом (например, перед разделом, который запрашивает и изменяет значение свойства), Вы вызываете метод lock экземпляра Lock. Если этот метод возвращает true, Вы получаете замок и можете продолжать. В конце критичного раздела Вы вызываете метод unlock Lock -экземпляра.

Когда клиентский запрос в одиночном потоке выполнения вызывает метод lock, любой другой запрос, вызывающий метод lock для того же Lock -экземпляра, ожидает, пока первый поток не вызовет метод unlock, пока не закончится таймаут или пока не возникнет ошибка. Это верно независимо от того, находится второй запрос в другом потоке для того же клиента или в потоке для другого клиента.

Если все потоки вызывают метод lock перед попыткой изменения совместно используемого объекта, то лишь один поток в единицу времени может войти в критичный раздел.

Важно!

Использование замков находится всецело под управлением разработчика и требует кооперации. Машина выполнения не заставляет Вас ни вызывать lock, ни учитывать блокировку, полученную кем-либо другим. Если Вы не спрашиваете, Вы можете изменять всё что захотите. Поэтому очень важно выработать привычку всегда вызывать lock и unlock при входе и выходе из критичного раздела кода и проверять return-значение метода lock, чтобы гарантировать, что блокировка получена. Можно представлять это в терминах флага: если Вы не запрашиваете флаг, вы не будете находиться в режиме ожидания. Если Вы не находитесь в режиме ожидания, Вы можете изменять то, что изменять нельзя.

Вы можете создать столько замков, сколько Вам необходимо. Один и тот же замок может использоваться для управления доступом к нескольким объектам, либо каждый объект (или даже каждое свойство) может иметь собственный замок.

Замок/lock сам по себе является просто объектом JavaScript; Вы можете сохранить ссылку на него в любом другом объекте JavaScript. Таким образом, например, обычной практикой является конструирование экземпляра Lock и сохранение его в объекте project.

ПРИМЕЧАНИЕ:

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

Следующий код показывает, как отследить заказы потребителей в совместно используемом объекте project.orders, рассмотренном ранее, и как обновлять project.orders.count каждый раз при получении нового заказа. Включите в начальную страницу приложения такой код:

// Создать новый Lock и сохранить в project.
project.ordersLock = new Lock();
if (! project.ordersLock.isValid()) {
   // Невозможно создать Lock. Перенаправить на страницу обработки ошибок.
   redirect ("sysfailure.html");
}

Этот код создаёт экземпляр класса Lock и проверяет (вызовом метода isValid), не возвращено ли что-нибудь неправильное при его создании. Очень редко Ваш экземпляр Lock конструируется неправильно. Это случается только тогда, когда машина выполнения запущена вне системных ресурсов при создании объекта.

Вы обычно создаёте экземпляры Lock на начальной странице, поэтому Вам не нужно получать замок перед созданием экземпляров Lock. Начальная страница запускается только один раз - при старте приложения на сервере. Поэтому Вам гарантируется, что создаётся только один экземпляр каждого замка.

Если, однако, Ваше приложение создаёт замок на какой-либо иной странице, множественные запросы могут вызывать эту страницу в это время. Один запрос может проверять наличие замка и не обнаружить его, в то время как другой запрос создаёт замок, а третий запрос создаёт второй замок. Тем временем первый запрос вызывает метод lock своего объекта. Затем второй запрос вызывает метод lock своего объекта. Оба запроса теперь "думают", что они имеют безопасный доступ к критичному разделу кода и продолжают свою работу, нарушая работу другого.

После получения верного замка Ваше приложение может продолжать работу. На странице, требующей доступа к критичному разделу, можете ввести такой код:

// Начало критичного раздела -- получить замок.
if ( project.ordersLock.lock() ) {
   var x = project.orders.count;
   x = x + 1;
   project.orders.count = x;
   // Конец критичного раздела -- освободить замок.
   project.ordersLock.unlock();
}
else
   redirect("combacklater.html");

Этот код запрашивает замок. Если замок получен (то есть, если метод lock возвратил true), выполняется вход в критичный раздел, вносятся изменения и, наконец, замок освобождается. Если метод lock возвращает false, то данный код не получает замка. В этом случае приложение перенаправляет на страницу, которая сообщает, что приложение в данный момент не может выполнить запрос.

Специальные Замки/Locks для Объектов project и server


Каждый из объектов project и server имеет методы lock и unlock. Вы можете использовать эти методы для получения исключительного доступа к свойствам этих объектов.

В этих методах ничего нового нет. Вам также необходима кооперация с другими участками кода. Вы можете представлять эти методы как имеющие флаги: один флаг с именем "project", а другой - флаг с именем "server." Если другой раздел кода не вызывает project.lock, первый может изменять любые свойства объекта project.

В отличие от метода lock класса Lock, Вы не можете специфицировать таймаут для метода lock объектов project и server. То есть, когда Вы вызываете project.lock, система ожидает бесконечно долго освобождения замка. Если Вы хотите ожидать только в течение определённого периода, используйте экземпляр класса Lock.

В примере использованы методы lock и unlock для получения исключительного доступа к объекту project для модификации свойства ID потребителя:

project.lock()
project.next_id = 1 + project.next_id;
client.id = project.next_id;
project.unlock();

Исключение Мёртвой Блокировки/Deadlock


Вы используете замки для защиты критичных участков кода. На практике это означает, что один запрос ожидает, пока другой выполняет критичный код. Вы обязаны соблюдать осторожность при использовании замков для защиты критичных разделов. Если один запрос ожидает освобождения замка, полученного другим запросом, а этот второй запрос ожидает освобождения замка, полученного первым запросом, ни один из запросов не сможет продолжить работу. Эта ситуация называется deadlock/тупик/мертвая блокировка.

Рассмотрим предыдущий пример обработки заказов потребителей. Предположим, что приложение разрешает два действия. В одном - пользователь вводит нового потребителя; в другом - пользователь вводит новый заказ. Как часть создания нового потребителя приложение также создаёт новый заказ потребителя. Это действие выполняется на одной странице приложения, давая примерно такой код:

// Создать нового потребителя (customer).
if ( project.customersLock.lock() ) {
   var id = project.customers.ID;
   id = id + 1;
   project.customers.ID = id;
   // Стартовать новый заказ (order) для этого нового потребителя.
   if ( project.ordersLock.lock() ) {
      var c = project.orders.count;
      c = c + 1;
      project.orders.count = c;
      project.ordersLock.unlock();
   }
   project.customersLock.unlock();
}

Во втором типе действия пользователь вводит новый заказ потребителя. Как часть процесса ввода нового заказа: если потребитель ещё не является зарегистрированным потребителем, приложение создаёт нового потребителя. Это действие выполняется на другой странице приложения, где может быть примерно такой код:

// Стартовать новый заказ.
if ( project.ordersLock.lock() ) {
   var c = project.orders.count;
   c = c + 1;
   project.orders.count = c;
   if (...код определения неизвестного потребителя...) {       // Создать нового потребителя.
      // Этот внутренний замок может вызвать проблемы!
      if ( project.customersLock.lock() ) {
          var id = project.customers.ID;
         id = id + 1;
         project.customers.ID = id;
          project.customersLock.unlock();
      }
   }
   project.ordersLock.unlock();
}

Заметьте, что каждый из этих фрагментов кода пытается получить второй замок, уже получив один. Это может вызвать проблемы. Предположим, что один поток начинает создание нового потребителя; он получает замок customersLock. В это же самое время другой поток начинает создание нового заказа; он получает замок ordersLock. Теперь первый поток запрашивает замок ordersLock. Поскольку второй поток уже получил этот замок, первый поток должен ждать. Предположим, однако, что второй поток теперь запрашивает замок customersLock. Первый поток уже имеет этот замок, поэтому второй поток должен ждать. Теперь потоки ждут друг друга. Поскольку никто их них не специфицировал таймаут, оба они будут ждать бесконечно.

В данном случае проблему можно легко устранить. Поскольку значения ID потребителя и номер заказа не зависят один от другого, нет никакого смысла вкладывать замки друг в друга. Вы можете избежать возможных тупиков, переписав оба фрагмента кода. Перепишите первый фрагмент так:

// Создать нового потребителя.
if ( project.customersLock.lock() ) {
   var id = project.customers.ID;
   id = id + 1;
   project.customers.ID = id;
   project.customersLock.unlock();
}
// Стартовать новый заказ для этого нового потребителя.
if ( project.ordersLock.lock() ) {
   var c = project.orders.count;
   c = c + 1;
   project.orders.count = c;
   project.ordersLock.unlock();
}

Второй фрагмент будет примерно таким:

// Стартовать новый заказ.
if ( project.ordersLock.lock() ) {
   var c = project.orders.count;
   c = c + 1;
   project.orders.count = c;
   project.ordersLock.unlock();
}
if (...код для определения неизвестного потребителя...) {    // Создать нового потребителя.
   if ( project.customersLock.lock() ) {
      var id = project.customers.ID;
      id = id + 1;
      project.customers.ID = id;
      project.customersLock.unlock();
   }
}

Хотя это и надуманная ситуация, тупики это совершенно реальная проблема, и они могут произойти во многих случаях. Для этого даже не понадобится более одного замка или более одного запроса. Рассмотрим код, в котором две функции запрашивают один и тот же замок:

function fn1 () {
   if ( project.lock() ) {
      // ... какие-то действия ...
      project.unlock();
   }
}
function fn2 () {
   if ( project.lock() ) {
      // ... какие-то другие действия ...
      project.unlock();
   }
}

Сам по себе этот код не содержит проблем. Позднее слегка измените его, чтобы fn1 вызывала fn2, уже имея замок, как показано далее:

function fn1 () {
   if ( project.lock() ) {
      // ... какие-то действия ...
      fn2();
      project.unlock();
   }
}

Вот вы и получили тупик/deadlock. Это, конечно, немного смешно, когда единственный запрос ожидает от самого себя освобождения флага!

Оглавление | Назад | Вперёд | Индекс

Дата последнего обновления: 29 сентября 1999 г.

© Copyright ╘ 1999 Sun Microsystems, Inc. Некоторая часть Copyright ╘ 1999 Netscape Communications Corp. Все Права Зарезервированы.