Дипломная работа студента 545 группы



страница9/9
Дата29.07.2016
Размер0.61 Mb.
ТипДипломная работа
1   2   3   4   5   6   7   8   9

Язык сервисов


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

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



  • Абстрактный stub-класс сервера, который содержит в себе перечисление всех методов, а также неабстрактный метод, который умеет обработать входящий запрос, разобрать его и выдать ответ, то есть из входящего потока данных создать исходящий.

  • Stub-класс клиента, который поддерживает синхронное выполнение запросов.

  • Stub-класс клиента, который поддерживает асинхронное выполнение запросов.

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

Кроме того были описаны концепты «реализации сервиса», то есть реализация сервиса не на уровне Java, а на уровне MPS, то есть с полной системой наследования, полей и прочих атрибутов стандартных классов. Это позволяет добавлять методы в RPC, которые будут реализованы с помощью своего языка, также это избавляет от необходимости переделывать код имплементаций при рефакторинге интерфейса сервиса.

Ниже (лист.10) приведен пример простого сервиса.

service myService {

AccessLevel getAccessLevel(User user);

}

Листинг 10. Пример сервиса

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


Исключения


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

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

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


  • В заголовке сервиса задается узел, в котором содержатся все возможные неожидаемые исключения – это нужно для того, чтобы не включать все возможные существующие исключения, а также для того, чтобы можно было описать в одном пакете два различных сервиса с разными исключениями.

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

Ниже (лист.11) приведен пример создания сервиса с подключением контейнера исключений, а также пример метода, с объявлением бросаемого им исключения.



service myService throws exceptions from MyExceptions {

AccessLevel getAccessLevel(User user) throws UserExeption;

}

Листинг 11. Пример сервиса с исключениями

Кроме этого существует ServerException, который имеет только идентификатор: при неизвестной ошибке на сервере клиенту будет передано это исключение. Если на сервере реализована возможность логирования ошибок (запись в базу с id), то в исключении будет номер исключения в базе данных: это нужно для того, чтобы пользователь мог послать серверу отчёт о случившейся ошибке, добавив описание того, что он делал. Также будет добавлено состояние клиентского приложения в момент возникновения ошибка. Таким образом, разработчик может увидеть ошибку, её описание и два стектрейса: серверный и клиентский, это помогает править недоработки в кратчайшие сроки.

Исключение должно генерироваться сразу же в два класса: сообщение и обычное исключение Java: первое для передачи, второе для использования в коде. Это вызвало некоторые сложности, поскольку стандартно предполагается, что каждый концепт генерируется ровно в одну сущность. Однако для данной задачи было найдено решение.

Также нужно было реализовать возможность написать «throw myException», то есть использовать стандартные конструкции для исключений применительно к реализованным. Для этого потребовалось написать несколько генераторов, которые заменяют данные конструкции на эквивалентные, но с оборачиванием ошибки в сгенерированный класс исключения. Кроме того нужно было переопределить и конструкцию catch, поскольку иначе ошибка никогда не будет поймана.

После замены расширения стандартных конструкций возникла сложность в том, что в блоке catch перехватывается исключение одного типа, а внутри блока идёт работа не и с исключением, а с сообщением, которое обернуто в это исключение. Поэтому потребовалось написать генератор, который делает замену всех операций с исходным исключением, для примера назовём его myException, на myException.getMessage(). То есть происходит переопределение всех local variable reference на это исключение.

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

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

Генерация в Objective C


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

Язык Objective C был разработан в 1986, поэтому его синтаксис кажется странным для разработчика, который пишет на C# или Java. В платформе iOS отсутствует сборщик мусора, поэтому реализация генератора для ObjC является более трудоёмким, нежели для Java.

Также в данном языке отсутствуют привычные шаблоны из C++ или дженерики из C# и Java. Поэтому это вызывает особые неудобства при использовании коллекций, так как требует приведение типов в некоторых случаях.

Задача реализации генератора в Objective C началась с разработки паттерна, который бы позволил генерировать привычные для языка Java перечисления, поскольку они отсутствуют как таковые.

Было решено генерировать класс, который имеет статические методы с названиями соответствующих значений. При вызове метода возвращается инстанс этого класса, который создается один раз для каждого элемента перечисления, содержит в себе имя значения, а также порядковый номер. Таким образом, получение какого-то значения из enum сводится к такому вызову: [MyEnum VALUE]. То есть это выглядит практически так же, как и в других языках, где есть enum и позволяет делать сравнение обычным равенством, а не вызовом метода isEquals.

Следующей задачей было найти решение, как делать асинхронные вызовы. Так как разработка на Objective C ведется не в MPS, а в XCode [26] или в новом AppCode [27], то это накладывает ограничение: невозможно написать своё решение языка асинхронных вызовов для Objetive C. Таким образом, нужно было придумать способ позволить программисту вызвать асинхронные методы и делать это удобно. Самым очевидным способом было бы создание класса, у которого есть 2 метода: onSuccess и onFailure. Но не так давно в язык Objective C были введены блоки. Другое общепринятое название для них – closure. Можно передавать их в качестве параметров асинхронному методу. Когда выполнение обращения к серверу будет завершено, будет вызван блок соответствующий success и передан в него результат.

- (void) getUserUsingSuccessBlock: (void(^)(User*)) successBlock onFailBlock: (void(^)(NSException* e)) failBlock

Листинг 12. Пример асинхронного вызова метода
В данном примере приведено описание метода, который получит пользователя. Язык Objective C является избыточным, так как вместо оператора «точка» используются квадратные скобки, также потому, что при вызове метода нужно указать названия параметров. На самом деле, названием данного метода является не «getUserUsingSuccessBlock», а более длинное: «getUserUsingSuccessBlock:onFailBlock», то есть включаются имена всех параметров.

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

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

Разработка на языке Objective C являлась трудоёмкой, поскольку в среде разработки XCode нет удобных средств для навигации по коду и его рефакторингу. Также синтаксис не является привычным. В ходе разработки генератора были изучены многие тонкости языка Objective C, исследована система управления памятью и создана оптимальная по скорости и потреблению оперативной памяти система RPC и сообщений.


События


В данный момент существуют серверы [28], которые позволяют создавать comet-соединения [29]. То есть такие, что сервер, может отправлять (push) данные клиенту. Это похоже на сокеты, но в данной модели важно, что сервер может посылать уведомления, клиент же не может.

Таким образом, клиент может соединиться с сервером и получать от него уведомления о произошедших событиях. Решение поддержки такой возможности было принято уже после разработки основных возможностей RPC и показывает, что использование метапрограммирования и своей реализации системы удаленного вызова процедур, позволяет сколь угодно расширять решение и получать результаты, которые не реализованы ещё нигде или реализованы, но не применимы. Например, comet-соединение поддержано в GWT-RPC, то есть только для Java.

События расширили существующее RPC. Для этого потребовалось добавить конструкцию, которая позволяла повестить листенер не некоторое событие, а также посылать эти события с сервера на клиент.

Ниже (лист.13) указан пример того, как в сервисе выглядит описание добавления слушателя на некоторый объект.



add listener (UserListener listener) for (User user);

Листинг 13. Пример добавление слушателя на событие

Также потребовалось описать язык listeners, который содержит интерфейсы листенеров. Он практически эквивалентен языку сервисов с некоторыми уточнениями.

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

Тестирование


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

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

Дополнительно было реализовано сравнение производительности и сжимающих способностей основных алгоритмов, которые были рассмотрены в обзоре литературы.

Глава 4. Внедрение


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

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

Проект является высоконагруженной клиент-серверной системой. Уже на момент тестирования имеется передача порядка 10 гигабайтов запросов к серверу в день. Таким образом, производительность RPC важна для проекта. Неоднократно был использован профилятор для выявления узких мест системы, были проведены работы по их устранению.

Также внедрение позволило выявить некоторые утечки в памяти системы на языке Objective C. Поэтому были использование инструменты для выявления утечек памяти. Данные инструменты поставляются компанией Apple и часто являются непривычными для разработчика на языке Java или C#. К концу разработки абсолютно все утечки памяти были устранены.


Заключение


В рамках данной работы были достигнуты следующие основные результаты:

  • Изучен язык Objective C

  • Создан дизайн и реализация собственных языков на платформе MPS

  • Изучены существующие подходы в реализации RPC

  • Реализована система удаленного вызова процедур

  • Придумана и реализована система сжатия

  • Проведено тестирование результатов работы

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

Основной целью являлось реализовать систему, которая сможет быть пригодной для использования на разных платформах и разных языках, а также будет лучше по некоторым параметрам, чем существующие решения. Одним из таких параметров является простота: решение удобно и просто использовать. Другой критерий – функциональность: в языке присутствуют события, такого нет в аналогичных системах. Ещё одним параметром является степень сжатия, при этом производительность не должна сильно ухудшиться. Были произведены замеры сжатия данного RPC и других популярных кодирующих систем. Стоит учесть, что оптимизация сжатия производилась не основе предположения о существовании идентификаторов сущностей, а также идентификаторов вызываемых методов. Кроме того полагалось, что передаваемые данные в среднем достаточно велики, а не ограничены 100 байтами. Поэтому тестирование проводилось на реальных данных и результаты являются среднестатистическими.

Ниже указана диаграмма (д-ма.1), в которой приведены результаты сравнения степени сжатия сообщений различными решениями, а также диаграмма (д-ма.2), в которой указана скорость сжатия этих сообщений. Технология, разработанная в рамках данного диплома, носит название myRPC в этих диаграммах.


Диаграмма 1. Сравнение степени сжатия
Диаграмма 2. Сравнение скорости сжатия

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



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

Литература


  1. Sergey Dmitriev, Language Oriented Programming: The Next Programming Paradigm, 2004 http://www.jetbrains.com/mps/docs/Language_Oriented_Programming.pdf

  2. Среда разработки JetBrains MPS

http://confluence.jetbrains.net/display/MPS/Welcome+to+JetBrains+MPS+Space

  1. Спецификация XML-RPC

http://www.xmlrpc.com/spec

  1. Спецификация SOAP

http://www.w3.org/TR/soap/

  1. Официальный портал JSON-RPC

http://json-rpc.org/

  1. Accessing Objects in Other Application Domains Using .NET Remoting

http://msdn.microsoft.com/en-us/library/72x4h507%28v=VS.71%29.aspx

  1. Официальный портал Remote Method Invocation

http://www.oracle.com/technetwork/java/javase/tech/index-jsp-136424.html

  1. Официальный портал Corba

http://www.corba.org/

  1. Документация по GWT-RPC

http://code.google.com/intl/ru-RU/webtoolkit/doc/latest/tutorial/RPC.html

  1. Спецификация ONC-RPC версии 2

http://tools.ietf.org/html/rfc5531

  1. Официальный портал OpenDCE

http://www.opengroup.org/dce/

  1. Портал DCE/RPC поставляемой Apple

http://www.dcerpc.org/

  1. Официальный портал Routix.PRC

http://www.routix.net/rpc/

  1. Michi Henning, Mark Spruiell, Distributed Programming with Ice

http://www.zeroc.com/doc/Ice-3.4.1-IceTouch/manual/index.html

  1. Спецификация Burlap

http://hessian.caucho.com/doc/burlap-1.0-spec.xtp

  1. Спецификация Hessian

http://hessian.caucho.com/doc/hessian-1.0-spec.xtp

  1. Specification of Packed Encoding Rules

http://www.itu.int/ITU-T/studygroups/com17/languages/X.691-0207.pdf

  1. Apache Etch Documentation

https://cwiki.apache.org/ETCH/documentation.html

  1. Thrift documentation

http://wiki.apache.org/thrift/

  1. Protobuf documentation

http://code.google.com/intl/ru-RU/apis/protocolbuffers/docs/overview.html

  1. Kryo homepage

http://code.google.com/p/kryo/

  1. Protostuff documentation

http://code.google.com/p/protostuff/w/list

  1. Avro documentation

http://avro.apache.org/docs/current/

  1. Comparing varius aspects of Serialization libraries on the JVM platform

http://code.google.com/p/thrift-protobuf-compare/wiki/BenchmarkingV2

  1. Learning Objective-C: A Primer

http://developer.apple.com/library/ios/#referencelibrary/GettingStarted/Learning_Objective-C_A_Primer/_index.html

  1. XCode official page

http://developer.apple.com/technologies/tools/features.html

  1. AppCode official page

http://www.jetbrains.com/objc/

  1. The Channel API

http://code.google.com/intl/en/appengine/docs/java/channel/

  1. Илья Кантор, Передача данных по инициативе сервера, обзор COMET http://javascript.ru/ajax/comet/overview






Поделитесь с Вашими друзьями:
1   2   3   4   5   6   7   8   9


База данных защищена авторским правом ©uverenniy.ru 2019
обратиться к администрации

    Главная страница