Как ServiceStack помогает поставить разработку веб-сервисов на поток

    На хабре незаслужено обойден вниманием замечетальный .Net-фреймворк ServiceStack. Упомянут он очень кратко, в одной лишь статье, и то косвенно, мельком, и в самом конце, и упомянута там лишь мизерная его часть. Очевидно, это связано с тем, что основная масса .Net-разработчиков использует стандартные решения от Microsoft для решения задач по разработке веб-сервисов и веб-приложений, а именно ASP.Net MVC/WebAPI или WCF и не заморачивается. Как мы попытаемся показать в этой статье, современный .Net хорош далеко не только своими стандартными технологиями.



    Если интересно, прошу под кат.

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

    ServiceStack? Не, не слышал...



    Для начала ответим на вопрос, чем является собственно ServiceStack. Это полноценный фреймворк для создания веб-сервисов и веб-приложений, вобравший в себя лучшее из мира C# и ASP.Net, и – да, вы угадали! – избавившийся от всего худшего, лишнего и ненужного. Однако, дорогой хабражитель, предупредим сразу, холивара не будет, не в этот четверг и не с нами! Забегая вперед, отметим, что существуют архитектурные вариации, соединяющие в единую систему компоненты, написанные и на ServiceStack (в роли сервера) и, к примеру, на ASP.Net MVC (в роли клиента).

    Фреймворк зародился в недрах компании BBC, в то время, когда там работал Демис Беллот. Началом развития фреймворка можно считать 2007 год, когда BBC, использующая стандартные Enterprise-решения от Microsoft в купе с водопадным подходом к разработке в условиях быстроизменяющихся требований попала прямиком в просак, причем “просак” более близкий к определению, данному героем Никиты Мигалкова из фильма “Жмурки”, нежели к определению из Толкового Словаря*.



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

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

    • Это НЕ способ навязать конкретную платформу или технологию
    • Это НЕ способ навязать заранее определенные ограничения
    • Это НЕ повод [вечно] использовать заранее определенный формат или следовать заранее определенной архитектуре
    • Это предоставление услуг, а если говорить техническим языком, инкапсуляция некоторых возможностей, которые могут быть использованы повторно, и организация их доступности
    • Организация их доступности как можно большему числу клиентов, самым простым и эффективным способом, с минимальными затратами и максимальной возможностью повторного использования


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

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

    И все же, чем он лучше?



    • Более интуитивен и прост в использовании
    • Более высокая скорость работы по сравнению со стандартными .Net-решениями
    • Позволяет даже при не самых прямых руках достигать высокого уровня повторного использования кода


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

    И, наверное, для некоторых следующий момент будет и вовсе решающим:



    ServiceStack проекты могут развертываться на Linux прямо из коробки!

    Таким образом вам не обязательно даже оплачивать дороговатый Windows Azure или, что еще накладнее, содержать собственный Windows Server. Для средного по величине проекта может сгодиться и обычный сервер, скажем, с Debian или Ubuntu Server, или даже VPS от Hetzner за $30 в месяц. Для небольших проектов пойдет и cамый дешевый VPS за 10$ на Hetzner или даже за 5$ на Digital Ocean.

    Наши партнеры имеют опыт развертывания и хостинга ServiceStack-проектов на Linux при помощи nginx + FastCGI Mono, и если нам всем это интересно, если мы их дружно попросим, я думаю, они не откажут нам в том, чтобы рассказать на хабре о том, как это делается в деталях.

    А можно больше технических деталей?


    Нельзя. Разработчики ServiceStack не стали рушить весь ASP.Net на корню, чтобы строить новое. Корень – а именно IHttpHandler – они оставили. Однако, все, что идет далее так и разрушили и использовали более легковесные альтернативы, которые местами сами немного адаптировали специально для решения задач разработки веб-сервисом. Фреймворк предоставляет свой кэш-менеджер, свои провайдеры аутентификации и сессии и даже предлагает своего MQ-брокера, работающего на базе Redis. Кстати, в рамках фреймворка также создан собственный Redis-клиент, который, на мой личный взгляд, просто шикарен, и он может быть использован отдельно от фреймворка в различных контекстах. В качестве штатного IOC-контейнера используется слегка модифицированная версия достаточного популярного легковесного контейнера Func. Правда, вместо него можно использовать и любой другой IOC-контейнер из множества доступных.

    Краегоульным камнем ServiceStack являются DTO (Data Transfer Objects, Объекты Передачи Данных), которые базируются на плоских объектах POCO. Провайдеры аутентификации и сессии, также как и пользовательские сервисы базируются на POCO, что позволяет использовать различные бэкенды для различных реализаций. Из коробки доступны, к примеру, типизированные сессии для хранения непосредственно в памяти и в Redis. Также доступна и нетипизированная сессия для любителей динамизма, но она, к сожалению, работает несколько медленнее.

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

    Хорош лить воду, давай коду!


    Итак, для того, чтобы создать веб-сервис в ServiceStack-проекте необходимо сделать четыре простые вещи, а именно:
    • Создать класс для DTO запроса к сервисы
    • Создать класс для DTO ответа сервиса
    • Создать класс самого сервиса, отнаследовав его от базового класса, и переопределив методы, одноименные с HTTP-методами, которые вам необходимо реализовать
    • Наслаждаться жизнью и получать профит (шутка… хотя, нет, не шутка)


    Давайте напишем простой сервис.

    Создаем новый проект в Visual Studio или Xamarin Studio. За неимением первого под рукой, я воспользуюсь вторым, благо, под Mono все шикарно работает (а изначально для нас этот фактор и вовсе был решающим). И в качестве компенсации за неиспользование лучшей IDE, мы вам покажем как этот сервис задействовать из мобильного приложения для лучшей мобильной ОС.

    Итак, создаем пустой ASP.Net-проект (Empty Web Application). Решение мы назовем SSHelloWorld, а проект – SSHelloAPI.

    По проекту в Обозревателе Решения (Solution Explorer) делаем правый клик, и выбираем пункт “Управление пакетами NuGet” (Manage Nuget Packages).



    После того, как вы кликнете по кнопочке “Добавить” (Add), вас спросят, согласны ли вы с лицензионным соглашением? Ну т.к. русскоязычные пользователи читают лицензионные соглашения быстрее всех, мы соглашаемся, и к нашему проекту подключаются скачанные dll-ки ServiceStack'a.

    Если в вашем проекте отсутствует Global.asax (а по идее должен отсутствовать), добавьте его и скопируйте в Global.asax.cs следующий код вместо того, что там есть:

    using System;
    using System.Collections;
    using System.ComponentModel;
    using System.Web;
    using System.Web.SessionState;
    using ServiceStack;
    
    namespace SSHelloAPI
    {
    	public class Global : System.Web.HttpApplication
    	{
    
    		public class AppHost : AppHostBase
    		{
    			public AppHost() : base("Hello API", typeof(HelloService).Assembly) {}
    		
    
    			public override void Configure (Funq.Container container)
    			{
    
    			}
    		}
    
    		protected void Application_Start (Object sender, EventArgs e)
    		{
    			new AppHost ().Init ();
    		}
    	}
    }
    


    Не обращайте внимания на подсвечивание ошибок в коде сейчас. Нам нужно создать класс, в котором будет находиться наш сервис.

    Также скорее всего придется вручную создать файл web.config. И весь его код должен быть заменен следующим:

    <?xml version="1.0"?>
    <configuration>
    	<system.web>
    	  <httpHandlers>
    	    <add path="*" type="ServiceStack.HttpHandlerFactory, ServiceStack" verb="*"/>
    	  </httpHandlers>
    	</system.web>
    
    	<!-- Required for IIS 7.0 (and above?) -->
    	<system.webServer>
    	  <validation validateIntegratedModeConfiguration="false" />
    	  <handlers>
    	    <add path="*" name="ServiceStack.Factory" type="ServiceStack.HttpHandlerFactory, ServiceStack" verb="*" preCondition="integratedMode" resourceType="Unspecified" allowPathInfo="true" />
    	  </handlers>
    	</system.webServer>
    </configuration>
    


    Ну и, наконец, создадим новый файл HelloService.cs, в котором у нас будут три класса:

    using System;
    using ServiceStack;
    
    namespace SSHelloAPI
    {
    	[Route("/hello")]
    	[Route("/hello/{Name}")]
    	public class HelloRequest
    	{
    		public string Name { get; set; }
    	}
            
    	public class HelloResponse 
    	{
    		public string Result { get; set; }
    	}
    
    	public class HelloService : Service
    	{
    		public object Any(HelloRequest request)
    		{
    			return new HelloResponse { Result = "Hello, " + request.Name };
    		}
    	}
    	
    }
    


    Все просто и понятно, такой код мне даже стыдно комментировать. Компилируем и запускаем наш проект, и в адресной строке браузера введем после имени хоста и порта “/hello/Habrauser”. ServiceStack по заголовку запроса сам определяет тип клиента, и в данном случае покажет нам ответ сервиса в html-обертке. И в поле Result мы увидим, как сервис приветствует нас.

    Название метода Any говорит о том, что мы определили одну и ту же логику для всех HTTP-методов.

    Обратите внимание на страничку metadata, которая открывается первым делом при запуске проекта. Она содержит автоматически сгенерированную документацию к сервисам. Как видите, ServiceStack на ходу создает различные конечные точки, и мы можем работать с нашим сервисом как с RESTful-сервисом, так и как с SOAP-сервисом (да, я имею в виду именно SOAP, дорогой читатель).



    Сервис, конечно, скучный, но дабы не перегружать статью мы не стали сразу включать более сложные примеры. Однако, он дает почувствовать вкус SericeStack и если вам были интересно, то теперь – с нами или без нас – вы можете отведать больше.

    Как ServiceStack облегчает жизнь мобильному разработчику



    Еще чем хорош ServiceStack, так это тем, что созданный сервис весьма удобно использовать в Windows Phone, Xamarin.iOS или Xamarin.Android-приложении (правда, для последних двух необходима бизнес-лицензия на Xamarin). Существуют различные способы, но самый простой (однако, не самый красивый), это выделить в отдельные файлы классы Запросов и Ответов, и подключить их через связывание в проект мобильного приложения. Также из Nuget установить сборку ServiceStack.Client.

    Далее, допустим, на событие нажатия кнопки вешаем такой обработчик:

    			button.TouchUpInside +=  (object sender, EventArgs e) => {
    				try {
    					var client = new JsonServiceClient("http://127.0.0.1:8080");
    					HelloResponse response = client.Get<HelloResponse>(new HelloRequest() { Name = "iPhone" });
    					resultLabel.Text = response.Result;
    				}
    				catch (Exception ex)
    				{
    					resultLabel.Text = ex.Message;
    				}
    			};
    


    Более подробно этот вопрос также можно рассмотреть в будущем.

    А что с лицензией?



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

    • 10 операций (т.е. 10 DTO запросов)
    • 10 Таблиц в OrmLite
    • 20 Типов с JSON, JSV и СSV-сериализаторами
    • 20 Типов для Redis-клиента
    • 6000 запросов в час к Redis


    Ну и ладно, не Redis'ом единым, кроме OrmLite есть и другие ORM, а вот 10 операций — это уже существенное, конечно, ограничение.

    Более подробная информация о лицензировании и ценах доступна на этой страницe.

    На данный момент мы пользуемся в наших проектах 3й версией, которая доступна под лицензией BSD, и переход на 4ю только рассматриваем. Если не хотите быть привязанными к “коммерческой стороне” можете спокойно брать 3ю ветку и использовать ее в своем проекте, она вполне зрелая и стабильная.

    Ссылки и сноски


    ПРОСАК (Толковый Словарь Даля) м. новг. твер. прядильня; крутило, канатный, веревочный стан, на котором сучат, спускают веревки. || *Затруднительное положение, бедушка, где не знаешь, как быть. Он впросак попал, сидит впросаках. Просак (от сучить?), пространство от прядильного колеса до саней, где снуется и крутится бичевка, спускается вервь; если попадешь туда концом одежи, волосами, то скрутит, и не выдерешься; от этого поговорка.

    А если серьезно, то вот они:

    Официальный сайт
    servicestack.net

    Вики
    github.com/ServiceStack/ServiceStack/wiki

    Слайдшоу с презентацией Демиса Беллота о ServiceStack
    www.slideshare.net/newmovie

    Некоторые примеры рабочих проектов (код открыт и доступен на github)
    Аналог imgur
    imgur.servicestack.net

    RestMovies
    mono.servicestack.net/ServiceStack.MovieRest

    Аналог StackOverflow
    mono.servicestack.net/RedisStackOverflow

    И многие другие примеры доступны на этой странице , а их исходники на github.

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

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

    Продолжить рассмотрение ServiceStack в следующих статьях?

    • +6
    • 18,7k
    • 5
    Поделиться публикацией
    AdBlock похитил этот баннер, но баннеры не зубы — отрастут

    Подробнее
    Реклама

    Комментарии 5

      +2
      Под Linux есть проблемы с производительностью, утечками.
      Но фреймворк замечательный, да.
        +2
        Я бы еще добавил упоминание Swagger плагина
          0
          Да уже одно требование по лицензии- сразу минус. Не потому что бедные мы, а потому что на распробовать не хватит.
          Hello World- писать всегда легко, а дальше обычно начинаются подводные грабли.

          Rest/Soap вместе на базе одного сервиса- как минимум странно, как максимум плохо. Это два разных принципа построения Api. Один — манипуляции с ресурсов, второй по сути Remote Procedure Call. Если они мешаются, значит что-то явно не так.

          Хостинг на Linux- вообще-то Asp.Net WebApi тоже прекрастно хостится там, не совсем из коробки, но можно.

          Я ни в коем случаи не против этого framework, и даже его пробовал… просто ваши аргументы- не аргументы.
            +2
            ServiceStack действительно пожалуй луший фреймфорк, что есть на платформе .net, для создания полноценных RESTfull сервисов. У него много разных возможностей, и по производительности отдельных компонент он уделывает большинство своих конкурентов. Взять хотя бы ту же json сериализацию — http://mono.servicestack.net/benchmarks/NorthwindDatabaseRowsSerialization.1000000-times.2010-02-06.html или OrmLite — https://code.google.com/p/dapper-dot-net/

            Однако у него на текущий момент есть много других «недостатков», которые приходится решать нестандартными методами.
            • нельзя делать более сложные Route, чем test/{f}/{fd} — маршрут {tr}-{tr} уже не пройдет (как пример: ru-RU)
            • В отрыве от всего фреймворка сложно использовать его IoC контейнер — приходится извращаться.
              Например, если требуется выделить слой DAL на основе встроенного OrmLite и связать его со слоем бизнес логики (а эти все компоненты в других сборках) — то просто получить доступ к контейнеру IoC не получится, а это значит что большинство функций контейнера не доступно. Например нельзя вызывать нигде TryResolveNamed (даже в классе Service), хотя зарегистрировать можно.
              Опять же IoC настолько упрощен, что отсутствуют некоторые весьма востребованные вещи — например управление жизненным циклом объекта.
            • Вмешаться в процесс сериализации задача не тривиальная — внутренний сериализатор настолько упрощен, что, например, обертку вокруг данных приходится делать для всех классов. Просто так получить json вида response {тут описание объекта} очень непросто. Это можно было бы сделать какой-нить настройкой или атрибутом для класса — однако такая вещь отсутсвует. Т.е. если использовать простые описания классов, то для того что бы получить унифицированный вид ответов от сервисов — надо описывать доп контейнеры.
            • В системе автодокументации тоже много мелочей, из-за которых сложные оисания сделать не просто. К примеру, если в шаблоне задан путь /v1/entities/{params} то пресловутый свагер, будет считать что наша сущность это не entity, а v1.
            • перечень не полный, выделил те, с которыми наиболее серьезно столкнулся...


            Ну и конечно 4 версия внесла много изменений, из-за которой код, написанный под 3-ю версия пришлось переделывать. Основные изменений коснулось части конфигурирования фреймворка. Не мало параметров либо удалили, либо перенесли на други уровни, либо переделали полностью. Совсем не порадовали ограничения введенные в 4 версии особенно всего 10 запросов. Однако из-за открытости кода — эти вещи можно самому поправить :) Только вот о лицензионной чистоте уже можно будет забыть.



              0
              Соглашусь — с влезанием в сериализацию есть траблы.

              Но все равно — после него про WCF вспоминаю как о страшном сне :)

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

            Самое читаемое