Структура страниц веб-сервера 


Мы поможем в написании ваших работ!



ЗНАЕТЕ ЛИ ВЫ?

Структура страниц веб-сервера



После запуска DssHost, откройте веб-браузер и введите в адресную строку http://localhost:50000. На экране должно отобразиться что-то похожее на рис. 4. Заметьте, что в левой части окна размещено меню.

 

Рис. 4. Запущенный веб-сервер

 

Панель управления (Control Panel)

После выбора пункт Control Panel отображаются сервисы, которые можно запустить на данном DSS узле. Прежде чем будет отображена информация может пройти немного времени, так как выполняется обновление кэша каталога сервисов. Сервисы из списка можно запустить вручную. Можно выполнить поиск сервиса, заполнив строку поиска. На рис. 5 показаны результаты поиска сервиса по строке «sim».

 

Рис. 5. Поиск сервиса

 

В выпадающем списке рядом с каждым сервисом указан список манифестов, относящихся к сервису, которые были найдены DSS узлом. Для запуска экземпляра сервиса можно выбрать один из этих манифестов или оставить выпадающий список установленным в <<Start without manifest>> и нажать на кнопку Create. При наличии веб-камеры, убедитесь, что она подключена к компьютеру и нажмите на кнопку Create рядом с сервисом Webcam (не выбирайте манифест).

 

Service Directory

После запуска сервиса Webcam, этим сервисом можно управлять, выбрав пункт меню Service Directory (рис. 6).

 

Рис. 6. Каталог сервисов

 

Каждый DSS узел предоставляет информацию о запущенных на нём сервисах. На рис. 6 в списке находятся два сервиса Webcam (информация о втором сервисе на рис. 6 уместилась не полностью), так как сервис реализует общее соглашение Webcam и своё собственное.

Выберите сервис Webcam в списке Service Instance Directory для просмотра информации о работе сервиса. В адресной строке будет отображён URL http://localhost:50000/webcam/c7354581-c28f-4b4c-b419-d5b7ea06aab9. Эти числа меняются при каждом запуске сервиса – они делают URI сервиса уникальными и позволяют уникально идентифицировать каждый экземпляр сервиса. Доступ к сервису Webcam можно получить лишь путем добавления его имени в конец URL адреса узла DSS.

Вывод сервиса WebCam представляет собой Web форму. С её помощью выполняется форматирование вывода состояния сервиса (для получения состояния сервиса и отображения его в веб-браузере используется запрос HttpGet). В форме можно устанавливать параметры камеры, включая интервал обновления (Refresh Interval), формат вывода (Display Format), разрешение изображения (Capture Format), указать режим обновления данных, получаемых с камеры (ручной или автоматический).

Рекомендуется запускать DSS узел из под учётной записи «Администратор». Это позволит избежать проблем с запуском DssHost из-за настроек брэндмауэра или настроек безопасности на компьютере.

На странице Security Manager отображается содержимое файла store\SecuritySettings.xml, который допускает редактирование. Если файл не найден, на DSS узле будут использоваться настройки безопасности по умолчанию. Имя файла, в котором записаны настройки безопасности, устанавливается в конфигурационном файле DssHost: bin\dsshost.exe.config. Ключ Security определяет расположение файла с настройками безопасности:

<!--comment the line below to disable security-->

<add key=*security* value="..\store\securitysettings.xml"/>

 

Просмотр состояния сервиса

Запустите пример из каталога Microsoft Robotics Developer Studio→Visual Simulation Environment→Lego NXT Tribot Simulation (меню Пуск). Откройте DSS узел в веб-браузере и выберите сервис Generic Contact Sensors для просмотра его состояния. Вывод на экран должен быть похож на рис. 7. Состояние выводится в виде XML кода. Значение параметра Pressed бампера установлено в false, т. е. в настоящий момент времени робот не взаимодействует ни с одним объектом симулятора.

 

Рис. 7. Состояние сервиса

Создание нового сервиса

Использование Visual Studio

Создайте новый сервис (см. рис. 8), выполнив следующие шаги:

1) откройте Visual Studio и выберите File®New®Project;

2) выберите тип проекта Robotics и шаблон DSS Service (2.2);

3) убедитесь, что сервис расположен в папке Projects;

4) укажите для сервиса имя ServiceA.

 

Рис. 8. Создание сервиса

 

После создания проекта в панели Solution Explorer появятся следующие исходные файлы:

1) AssemblyInfo.cs: содержит информацию о результирующей сборке (DLL библиотека);

2) ServiceA.cs: главный исходный файл сервиса (ядро сервиса);

3) ServiceA.manifest.xml: манифест, который используется DSS для загрузки сервиса (манифест – XML файл, описывающий сервисы которые, должны быть запущены при загрузке манифеста на DSS узел);

4) ServiceATypes.cs: содержит набор классов (типов), которые использются сервисом, идентификатор соглашения сервиса, типы сообщений, обрабатываемые главным портом сервиса, состояние сервиса.

Рассмотрим свойства проекта (страница Project Properties):

1) закладка Application: тип приложения – библиотека классов (DLL). Все сервисы MRDS являются динамически подключаемыми библиотеками. Имя сборки записывается в формате ServiceA.Yyyyy.Mmm, где yyyy – текущий год, а mm – текущий месяц. Дата в имени используется для создания различных версий сервисов с одинаковыми именами. Идентификаторы соглашений также содержат год и месяц. Имя сборки и идентификатор соглашения должны соответствовать. Пространством имён по умолчанию является Robotics.ServiceA;

 

Рис. 9. Закладка Application

 

2) закладка Build: все сервисы MRDS находятся в папке bin, поэтому параметр Output Path установлен в...\...\bin. Значение Output Path – относительный путь, это необходимо учитывать при переносе файлов проекта в другую папку, в этом случае указанный относительный путь не будет корректным;

 

Рис. 10. Закладка Build

 

3) закладка Build Events:

"<MRDS path>\bin\dssproxy.exe"

/dll:"S(TargetPath)"/proxyprojectpath:"$(ProjectDir)Proxy

/keyfile:"$(AssemblyOriginatorKeyFile)"$(ProxyDelaySign)

S(CompactFrameworkProxyGen) /binpath:".

/referencepath:"<MRDS path>\bin\" /referencepath: "<MRDS path>\bin\"

Данная команда предназначена для создания proxy DLL сервиса.

 

Рис. 11. Закладка Build Events

 

4) закладка Debug: после запуска отладчика, автоматически запускается программа Dsshost.exe. Dsshost.exe запускает DSS узел, который является средой выполнения сервиса. Рабочим каталогом для DSSHost является корневой каталог MRDS. Команде DSSHost передаются следующие параметры:

/p:50000

/t:50001

/manifest:"samples\config\LEGO.NXT.Tribot.Simulation.manifest.xml"

После компиляции проекта сервиса генерируются три dll библиотеки:

1) сборка реализации сервиса, формируемая на основе файлов исходного кода, включенных в проект сервиса (Service.Y2012.M07.dll);

Сборка DSS Proxy предоставляющая соглашение сервиса для его использования другими сервисами. Сборка proxy включает общедоступные операции и типы состояния, предоставляемые сервисом. Организация взаимодействия сервисов реализуется посредством Proxy dll (Service.Y2012.M07.proxy.dll).

Сборка DSS Transform содержит описание соответствия между типами, определенными в реализации сервиса и реализации proxy. Данная сборка загружается автоматически с помощью среды выполнения DSS и взаимодействует только со сборкой реализации сервиса (Service.Y2012.M07.transform.dll).

Все три сборки копируются в каталог MRDS\bin.

Команда DssNewService

Запустите DSS Command Prompt из меню MRDS и выполните следующие шаги:

1) перейдите в папку Projects;

2) запустите команду DssNewService для создания сервиса:

DssNewService /Service:ServiceB

3) откройте созданный сервис.

Команда DssNewService имеет несколько параметров. Параметры отображаются после запуска DssNewService со следующим параметром:

DssNewService /?

Параметры командной строки команды DssNewService:

1) URI префикс, который используется в идентификаторе соглашения, указывается с помощью. По умолчанию – shemas.tempuri.org;

2) параметры /month и /year определяют идентификатор соглашения и имя сборки (по умолчанию – текущий месяц и год);

3) /namespace – пространство имен сервиса;

4) /clone – используется для копирования существующего сервиса. В результате, создаётся образ сервиса, копирование исходных файлов не выполняется. После построения solution с помощью команды DssNewService проект будет содержать методы без реализации;

5) /alt указывает альтернативный сервис, то есть общее соглашение, которое должен реализовать разрабатываемый сервис в дополнение к собственному набору операций (при использовании /alt нужно добавить параметр /i).

 

Исходные файлы сервиса

В проекте ServiceA откройте References в панели Solution Explorer. При создании сервиса автоматически вставляются ссылки для Ccr.Core, DssBase и DssRuntime. Почти во всех сервисах также требуется ссылка на Robotics.Common.Proxy, которая используется для работы с реальными роботами.

Исходный код сервиса разделен на два файла: основной код (ServiceA.cs) и описание типов (ServiceATypes.cs).

Файл описания типов

В начале файла ServiceATypes.cs находится соглашение для ServiceA:

namespace Robotics.ServiceA

{

/// <summary>

/// класс описания соглашения ServiceA

/// </summary>

public sealed class Contract

{

/// <summary>

/// соглашение сервиса

/// </summary>

public const String Identifier =

"http://www.promrds.com/contracts/2008/01/servicea.html";

}

}

Класс Contract – обязательная часть соглашения. Данный класс должен содержать строку Identifier. Строка Identifier представляет собой URI в следующем формате:

http://somehost.domain/path/year/month/servicename.html

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

Идентификатор выглядит как URL. Данный URL не обязательно должен существовать в Интернете. По умолчанию (для примеров данной книги) именем хоста в идентификаторе является www.promrds.com, но большинство сервисов Майкрософт используют путь schemas.microsoft.com/robotics. Включение в идентификатор года и месяца создания сервиса помогает сохранить URI уникальным. Ограничения на имена узлов и пути не накладываются. Все URI должны быть записаны в нижнем регистре во избежание проблем с регистром символов.

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

Каждый сервис включает класс описания состояния. Добавление полей в состояние сервиса является одним из шагов при создании нового сервиса:

/// <summary>

/// состояние serviceA

/// </summary>

[DataContract]

public class serviceAState { }

Перед описанием состояния сервиса указан атрибут [DataContract]. Он необходим для того, чтобы класс был скопирован в Proxy DLL при компиляции кода. Следующая часть кода – описание главного порта операций, PortSet:

/// <summary>

/// порт главных операций serviceA

/// </summary>

[ServicePort]

public class ServiceAOperations: Portset<DsspDefaultLookup, DsspDefaulDrop, Get>

{

}

Главный порт операций сервиса должен иметь атрибут [ServicePort], который отмечает его как порт операций. Подобные атрибуты используются в MRDS для декларативного создания инфраструктуры сервиса.

При создании нового сервиса порт операций (PortSet) по умолчанию содержит только операции DssDefaultLoolup, DssDefaultDrop и Get.

Операция Lookup позволяет получать информацию о данном сервисе другим сервисам, Drop используется для завершения работы сервиса, Get – для получения копии состояния сервиса. Этот набор типов сообщений создается автоматически при создании нового сервиса.

ServiceA является управляющим сервисом, поэтому в описание операций данного сервиса не требуется добавлять операции, помимо операций, добавленных по умолчанию. ServiceB вызывается ServiceA, поэтому при описании ServiceB нужно определить дополнительные операции.

Последняя секция файла содержит определение операций для запроса состояния сервиса. В ServiceA описывается только запрос типа Get:

/// <summary>

/// операция Get

/// </summary>

public class Get: Get<GetRequestType, Portset<serviceAState, Fault>>

{

/// <summary>

/// операция Get

/// </summary>

public Get()

{

}

/// <summary>

/// операция Get

/// </summary>

public Get(Microsoft.Dss.serviceModel.Dssp.GetRequestType body): base(body)

{

}

/// <summary>

/// операция Get

/// </summary>

public Get(Microsoft.Dss.serviceModel.Dssp.GetRequestType body,

Microsoft.ccr.core.Portset<serviceAState, w3c.soap.Fault> responsePort):

base(body, responsePort)

{

}

}

В коде выше не определены операции DssDefaultLookup и DssDefaultDrop, так как они неявно обрабатываются классом DssServiceBase. Однако эти операции можно переопределить.

 

Файл реализации сервиса

Файл ServiceA.cs включает код, выполняющий инициализацию сервиса, а также код, содержащий определение операций сервиса:

namespace ServiceA

{

/// <summary>

/// класс, реализующий ServiceA

/// </summary>

[DisplayName("ServiceA")]

[Description("The ServiceA service)"]

[contract(contract.identifier)]

public class serviceAService: DsspServiceBase

{

Атрибуты [DisplayName] и [Description] используются для оформления документации. Соглашение сервиса задается в поле Contract.Identifier, описание которого находится в файле ServiceATypes.cs. Класс разрабатываемого сервиса наследуется от класса DsspServiceBase, что даёт доступ к множеству вспомогательных функций.

Каждый сервис включает определение экземпляра своего состояния (даже если состояние пустое) и определение главного порта операций:

/// <summary>

/// состояние сервиса

/// </summary>

private ServiceAState _state = new ServiceAState();

/// <summary>

/// порт операций

/// </summary>

[servicePort("/servicea", AllowMultipleinstances=false)]

private ServiceAOperations _mainPort = new ServiceAOperations();

Состояние сервиса в данном примере хранится в переменной _state. Порт операций _mainPort создан для отправки сообщений ServiceA. Конструктор класса всегда пустой:

/// <summary>

/// конструктор сервиса по умолчанию

/// </summary>

public ServiceAService(DsspServiceCreationPort creationPort):base(creationPort)

{

}

Метод Start вызывается во время создания сервиса для выполнения операций по инициализации:

/// <summary>

/// метода Start сервиса

/// </summary>

protected override void Start()

{

base.Start();

}

Вызов base.Start вставляется автоматически. Обработчик для операции Get:

/// <summary>

/// обработчик Get

/// </summary>

[ServiceHandler(ServiceHandlerBehavior.Concurrent)]

public virtual IEnumerator <ITask> GetHandler(Get get)

{

get.ResponsePort.Post(_state);

yield break;

}

Данный обработчик возвращает копию состояния сервиса. Атрибут [ServiceHandler] указывает на принадлежность обработчика группе Concurrent.

Соглашение (contract.identifier) включается в Proxy DLL. Эта библиотека автоматически создается при компиляции сервиса с помощью DSSProxy. Напрямую DLL сервиса не вызывается, вместо этого методы вызываются с помощью соответствующих сигнатур в ProxyDLL. При создании связи с другими сервисами в References проекта нужно делать ссылку на Proxy DLL, а не на DLL.

 

Манифест сервиса

Файл ServiceA.manifest.xml – манифест сервиса ServiceA:

<?xml version="1.0"?>

<Manifest

xmlns="http://schemas.microsoft.com/xw/2004/10/manifest.html"

xmlns:dssp="http://schemas.microsoft.com/xw/2004/10/dssp.html">

<CreateServiceList>

<ServiceRecordType>

<dssp:Contract>http://schemas.tempuri.org/2008/01/servicea.html</dssp:Contract>

</ServiceRecordType>

</CreateServiceList>

</Manifest>

После запуска отладчика, этот манифест отправляется в DssHost. В манифесте указываются запускаемые сервисы и партнерские отношения между ними. Приведённый манифест включает одну запись ServiceRecord с идентификатором соглашения ServiceA.

Перед компиляцией сервиса ServiceA нужно добавить ссылки на ServiceB и выполнить реализацию операций сервиса. После этого необходимо выполнить компиляцию обоих сервисов.

Для компиляции сервиса выберите Build Solution в меню Build. В метод Start в ServiceA.cs добавьте строку кода, информирующую о запуске сервиса:

protected override void Start()

{

base.Start();

Console.WriteLine("ServiceA starting");

Скомпилируйте ServiceA. Откройте ServiceB в Visual Studio и добавьте команду Console.WriteLine в метод Start (файл ServiceB.cs).

 

Рассмотрим задачу умножения матрицы A (m × n элементов) на вектор B (1 × n элементов). Алгоритм вычисления вектора C (m × 1 элементов) легко распараллеливается, так как значение i-го элемента вектора не зависит от значений других его элементов.

Библиотека Concurrent and Coordination Runtime (CCR) предназначена для организации обработки данных с помощью параллельно и асинхронно выполняющихся методов. Взаимодействие между такими методами организуется на основе сообщений. Рассылка сообщений основана на использовании портов.

Основные понятия CCR:

1) сообщение – экземпляр любого типа данных;

2) порт – очередь сообщений типа FIFO (First-In-First-Out), сообщение остаётся в порте пока не будут извлечено из очереди порта получателем.

Определение порта:

Port<int> p = new Port<int>();

Отправка сообщения в порт:

p.Post(1);

3) получатель – структура, которая выполняет обработку сообщений.

Данная структура объединяет:

а) один или несколько портов, в которые отправляются сообщения;

б) метод (или методы), которые используются для обработки сообщений (такой метод называется задачей);

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

Описание получателя:

Arbiter.Receive(<тип_получателя>, <порт>, <выполняемый_делегат>);

Пример:

Activate(Arbiter.Receive(false, intPort, delegate(int n)

{

Console.WriteLine("Получено сообщение: " + n.ToString());

}));

Делегат, входящий в получатель, выполнится, когда в порт intPort придёт сообщение.

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

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

Описание диспетчера с двумя потоками в пуле:

Dispatcher d = new Dispatcher(2, "MyPool");

Описание очереди диспетчера, в которую задачи ставятся на выполнение:

DispatcherQueue dq = new DispatcherQueue("MyQueue", d);

Выполните следующие действия:

1) установите библиотеку CCR (CCR входит в состав Microsoft Robotics Developer Studio);

2) создайте проект консольного приложения и добавьте к проекту библиотеку Microsoft.Ccr.Core.dll.

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

Время выполнения вычислений будем определять с помощью класса

Stopwatch:

Stopwatch sWatch = new Stopwatch();

sWatch.Start();

<выполняемый код>

sWatch.Stop();

Console.WriteLine(sWatch.ElapsedMilliseconds.ToString());

Матрицу A, векторы B и C, переменные для хранения их размеров определим глобально:

int[,] A;

int[] B;

int[] C;

int m;

int n;

Метод TestFunction() запускает вычисления. Сначала в методе выполняется умножение матрицы на вектор с помощью последовательного алгоритма, затем та же задача решается с помощью параллельных вычислений. Рассмотрим этот метод.

Выполняется инициализация структур данных:

nc = 2; // количество ядер

m = 11000; // количество строк матрицы

n = 11000; // количество столбцов матрицы

A = new int[m, n];

B = new int[n];

C = new int[m];

Генерируется матрица A и вектор B:

Random r = new Random();

for (int i = 0; i < m; i++)

{

for (int j = 0; j < n; j++)

A[i, j] = r.Next(100);

}

for (int j = 0; j < n; j++)

B[j] = r.Next(100);

Рассмотрим метод SequentialMul:

Stopwatch sWatch = new Stopwatch();

sWatch.Start();

for (int i = 0; i < m; i++) {

C[i] = 0;

for (int j = 0; j < n; j++)

C[i] += A[i, j] * B[i]; }

sWatch.Stop();

Console.WriteLine("Последовательный алгоритм = {0} мс.",

sWatch.ElapsedMilliseconds.ToString());

Параллельная обработка выполняется с помощью запуска нескольких копий вычислительного метода. Каждая копия метода выполняет обработку определённой части исходных данных. Для описания задания для каждого метода используется класс InputData:

public class InputData

{

public int start; // начало диапазона

public int stop; // начало диапазона

}

Поля start / stop класса хранят номер начальной / конечной строки вектора C. Поля рассчитываются с помощью экземпляра вычислительного метода. Рассмотрим метод ParallelMul:

// создание массива объектов для хранения параметров

InputData[] ClArr = new InputData[nc];

for (int i = 0; i < nc; i++)

ClArr[i] = new InputData();

Далее, задаются исходные данные для каждого экземпляра вычислительного метода:

// делим количество строк в матрице на nc частей

int step = (Int32)(m / nc);

// заполняем массив параметров

int c = -1;

for (int i = 0; i < nc; i++)

{

ClArr[i].start = c + 1;

ClArr[i].stop = c + step;

c = c + step;

}

Создаётся диспетчер с пулом из двух потоков:

Dispatcher d = new Dispatcher(nc, "Test Pool");

DispatcherQueue dq = new DispatcherQueue("Test Queue", d);

Описывается порт, в который каждый экземпляр метода Mul() отправляет сообщение после завершения вычислений:

Port<int> p = new Port<int>();

Метод Arbiter.Activate помещает в очередь диспетчера две задачи (два экземпляра метода Mul):

for (int i = 0; i < nc; i++)

Arbiter.Activate(dq, new Task<InputData, Port<int>>(ClArr[i], p, Mul));

Первый параметр метода Arbiter.Activate – очередь диспетчера, который будет управлять выполнением задачи, второй параметр – запускаемая задача.

С помощью метода Arbiter.MultipleItemReceive запускается задача (приёмник), которая обрабатывает получение двух сообщений портом p:

Arbiter.Activate(Environment.TaskQueue,

Arbiter.MultipleItemReceive(true, p, nc, delegate(int[] array)

{

Console.WriteLine("Вычисления завершены");

}));

Приёмник используется для определения момента окончания вычислений. Он сработает только после того, как в порт p придёт два сообщения. В делегат, описанный в приёмнике, можно включить действия, которые должны быть выполнены после завершения процесса умножения.Метод Mul() выполняет перемножение части матрицы на вектор:

void Mul(InputData data, Port<int> resp)

{

Stopwatch sWatch = new Stopwatch();

sWatch.Start();

for (int i = data.start; i < data.stop; i++)

{

C[i] = 0;

for (int j = 0; j < n; j++)

C[i] += A[i, j] * B[j];

}

sWatch.Stop();

Console.WriteLine("Поток № {0}: Паралл. алгоритм = {1} мс.",

Thread.CurrentThread.ManagedThreadId,

sWatch.ElapsedMilliseconds.ToString());

resp.Post(1);

}

Метод Mul() имеет два параметра:

1) индекс, хранящий значение элемента массива, который определяет параметры, передаваемые на вход метода;

2) порт завершения, в который отправляется целое число после завершения вычислений.

После завершения вычислений метод Mul отправляет в порт p целое значение (отправляемое значение может быть любым).

 

Задание на лабораторную работу

 

1. Изучить методические рекомендации по выполнению лабораторной работы;

2. Выполнить вариант задания, выданный преподавателем;

3. Подготовить отчёт по лабораторной работе;

4. Подготовить ответы на контрольные вопросы.

 

Варианты заданий

Создать сервис, предназначенный для решения одной из задач, приведённой в лабораторной работе № 1.

 

Контрольные вопросы

1. Опишите структуру DSS сервиса;

2. Опишите назначение DSS сервиса;

3. Опишите порядок создания DSS сервиса;

4. Опишите принцип организации вычислений с помощью DSS сервиса.

 



Поделиться:


Последнее изменение этой страницы: 2016-04-26; просмотров: 187; Нарушение авторского права страницы; Мы поможем в написании вашей работы!

infopedia.su Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав. Обратная связь - 18.117.188.64 (0.207 с.)