Серверный JavaScript 1.4. Руководство по использованию

       

Запрашивание Свободного Соединения


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

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

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

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

  • Bucket: Определяет тип объекта (в данном примере - с названием bucket) для содержания соединения и штампа времени.

  • MarkBucket: Помечает объект bucket штампом текущего времени.

  • RetrieveConnections: Проходит по массиву соединений в поисках объекта Connection, к которому не было доступа в течение установленного лимита времени, и использует CleanBucket (описан далее) для запрашивания этого объекта.


  • CleanBucket: Закрывает курсоры (и возможные хранимые процедуры и результирующие наборы), откатывает или подтверждает каждую открытую транзакцию и возвращает соединение в пул.


Ваше приложение может использовать эти функции так:



  1. Когда Вы получаете новое соединений, вызывайте Bucket для создания объекта bucket.




  2. На любой странице, получающей доступ к соединению, вызывайте MarkBucket для обновления штампа времени.


  3. Если приложение делает паузу (таймаут), пытаясь получить соединение их пула, вызывайте RetrieveConnection для поиска незанятых соединений, закройте все открытые курсоры, подтвердите или откатите работающие транзакции и верните незанятые соединения обратно в пул.


  4. Если соединение было возвращено в пул, попытайтесь получить соединение из пула.


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



Создание Bucket


Функция bucket содержит соединение и штамп времени. Этот образец конструктора функции принимает соединение в качестве единственного параметра:

// Конструктор для Bucket
function Bucket(c)
{
   this.connection = c;
   this.lastModified = new Date();
}

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

myBucket.openCursor =
   myBucket.connection.cursor("select * from customer", true);


Пометка Объекта Bucket


Функция MarkBucket принимает объект Bucket в качестве параметра и устанавливает в поле lastModified текущее время.



function MarkBucket(bucket)
{
   bucket.lastModified = new Date();
}

Вызывайте MarkBucket на каждой странице приложения, которая использует соединение, содержащееся в bucket. Это восстанавливает в lastModified значение текущей даты и предотвращает появление незанятых соединений.



Запрашивание Старых Соединений


RetrieveConnections сканирует массив объектов Bucket, ищет buckets соединения, штамп времени которых установлен ранее некоторого определённого времени. Если соединение найдено, функция вызывает CleanBucket (описан далее) для возвращения соединения в пул БД.

// Запрашиваются соединения, не занятые в течение специфицированного количества минут.
function RetrieveConnections(BucketArray, timeout)
{
   var i;
   var count = 0;
   var now;

   now = new Date();

   // Этот цикл выполняется для каждого bucket в массиве.

   for (i in BucketArray) {

 // Вычисляется разница во времени между текущей/now и последней/last
 // модифицированной датой. Эта разница выражается в миллисекундах.
// Если она больше значения timeout, вызывается функция зачистки.       if ((now - i.lastModified)/60000) > timeout) {

CleanBucket(i);// Избавляется от bucket, поскольку он больше не используется.

  delete i;count = count + 1;
      }
   }  return count;
}



Зачистка Bucket


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

Данная функция-образец закрывает открытые курсоры, откатывает открытые транзакции и освобождает соединение.

function CleanBucket(bucket)
{
   bucket.openCursor.close();
   bucket.connection.rollbackTransaction();
   bucket.connection.release();
}

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





Направление Их Всех в Пул


Следующий пример кода использует уже определённые функции для запрашивания соединений, к которым не обращались в течение 10 минут. Сначала создаётся совместно используемый массив соединений и пул БД с 5 соединениями:

if ( project.sharedConns == null ) {
   project.sharedConns = new Object();
   project.sharedConns.pool = new DbPool ("ORACLE", "mydb",
      "user", "password", "", 5, false);
   if ( project.sharedConns.pool.connected() ) {
      project.sharedConns.connections = new Object();
   }else {
      delete project.sharedConns;
   }
}

Теперь используем следующий код для попытки получения соединения. После зачистки пула генерируется клиентский ID, который затем используется как индекс в массиве соединений. Далее пытаемся получить соединение. Если возникает таймаут, вызываем RetrieveConnections для возвращения старого соединения в пул.

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

if ( project.sharedConns != null ) {
   var pool = project.sharedConns.pool;   // Этот код запускается, только если пул уже соединён.
   // Если нет, возможно, Вам нужен код для соединения.
   if ( pool.connected() == true ) {

      // Генерируется клиентский ID.

      client.id = ssjs_generateClientID();

      // Попытка получить соединение.

      var connection = pool.connection("my connection", 30);



      // Если соединение null, тогда ничего не будет доступно       // в течение специфицированного лимита времени. Пытаемся запросить старые соединения.
      if (connection == null) {

         // Запрашиваются соединения, не используемые в течение последних 10 минут.

var count = RetrieveConnections(project.sharedConns.connections, 10);

         // Если count не равен 0, делаем какое-нибудь соединение доступным.

if (count != 0){

connection = pool.connection("my connection", 30);

    // Если connection всё ещё null, отказываемся.

      if (connection == null)

        redirect("nofreeconnections.htm");

      }
         else {

            // Отказываемся.

         redirect("nofreeconnections.htm");

       }}

      // Если Вы не дошли досюда, Вы получили соединение и можете продолжить работу.
      // Поместите это connection в новый bucket, стартуйте транзакцию,
      // получайте курсор, сохраняйте его в bucket и продолжайте работу.
      project.sharedConns.connections[client.id] =         new Bucket(connection);

 connection.beginTransaction();

project.sharedConns.connections[client.id].cursor =

connection.cursor("select * from customer", true);      // Помечаем bucket соединения как использованный.

      MarkBucket(project.sharedConns.connections[client.id]);

   // Операторы Базы Данных.
   ...
}

На следующей странице многостраничной транзакции выполняются операции БД по этому соединению. После последней операции БД по соединению помечается bucket соединения:

var Bucket = project.sharedConns.connections[client.id];

if ( Bucket == null) {
   // Повторное соединение.
}else {

   // Взаимодействие с БД.

...   // Последняя операция БД на странице.

   row = Bucket.cursor.next();
   row.customerid = 666;

 Bucket.openCursor.insertRow("customer");   // Помечается bucket соединения как использованный на данной странице.
   MarkBucket(Bucket);
}


Содержание раздела