Пишу веб-проекты в visual studio, и с каждой новой версией студии она как будто затачивается для работы с Windows Azure. Мне нравится Азура, хотя я пользуюсь только небольшим набором возможностей. Основное для меня — это Облачная служба. Облачная служба отлично подходит для разворачивания распределенного сервера.
Итак, я создаю облачную службу, в которую добавляю одну веб-роль (виртуальная машина с IIS), один воркер (виртуальная машина без IIS) и общую библиотеку классов. После публикации моим ролям присваивается ip-адрес и разные порты. То есть сразу есть tcp сеть, ручные настройки минимальны и могут делаться в самой студии. Можно, к примеру, сделать общедоступную точку доступа для воркера, но мне это не нужно. Мой воркер будет скрыт от внешних глаз и на нём будет висеть wcf-сервер, а общаться мои роли будут по быстрой локальной сети.
Общие классы я выношу в библиотеку (которую подключаю ко всем ролям), к примеру, интерфейс и канал связи:
Методы в веб-роли вызываю так:
Соответственно, в воркере у меня реализация методов из интерфейса (SendMessage, GetMessage), не буду их расписывать, а так же в воркере при старте выполняется код, который делает его хостом для wcf:
Тут я показал методы типа string, но тип возвращаемого значения может быть абсолютно любой, к примеру, сложный класс, который описан в моей общей библиотеке. Всё, никаких других настроек в вебконфигах, никаких упаковок в xml или SOAP у меня нет. Нет автоматически генерируемых файлов контрактов. Когда я пытался это сделать, в интернетах при поиске информации про wcf всегда всплывает настройка под http со всеми этими упаковками/распаковками. Да, wcf технология изначально придумана для связи разнородных систем, где необходимо сериализовать и передавать как строку. Но если у нас один язык программирования, а клиент и сервер wcf — это всё наша разработка, то не нужно городить огород, всё прекрасно работает.
Таким образом у меня получается распределенный сервер, для затратных операций я делаю отдельный воркер, тут, к примеру, показан чат. Все функции чата вынесены в отдельную виртуальную машину и я могу увеличивать производительность как самого сайта (веб-роли), так, отдельно, и чата.
Такая архитектура мне кажется правильной и может выдержать более сильную нагрузку, чем монолитный проект. Так же всё это разрабатывается и отлаживается как один проект в студии, то есть при вызове метода по wcf я могу в отладке пройти глубже в код, без сообщений типа «внутри отлаживать я не могу, не знаю что там происходит, вот такой вернулся результат».
С Азуровскими инструментами это делается всё легко и просто, я запускаю весь проект одной кнопкой и отлаживаюсь по нему как по одному проекту. Я помню когда-то давно, на одном из проектов была похожая архитектура, но я был молод, и мне там было очень сложно: какие-то фасады, и, если параметры у метода менялись, я изменения в четырёх местах вносил.
Мне нужно нечто подобное сделать на обычных серверах. Не на своих физических серверах, а на vds/vps виртуалках у какого-нибудь обязательно российского хостера. Я нашел забавную технологию у одного из хостеров (1gb.ru), называется ресурс Windows Azure Pack / COSN. По описанию это урезанная версия Азуры и всё должно было бы «взлететь». Конечно же, у них есть пробный период, я посмотрел. В браузере управление как-будто из старой версии портала Азуры (если кто помнит).

Из возможностей есть только создание виртуальных машин, объединение их в сеть (вручную), есть service bus — и всё. Так же нет возможности загрузить эту подписку в студию, а в этом и есть многое из удобств. Плюс нет возможности создания облачной службы. В описании сказано, что это «частное облако» будет дорабатываться и дополняться возможностями с оригинальной Азуры, но поддержка сказала, что всё уже давно заморожено, и вообще, это несовместимые технологии («этот Azure Pack совершенно по смыслу не совместим с настоящим Azure. Там что есть, то есть, и есть там маловато. Идеи там общие, но реализация разная совершенно, API разные и так далее.» (с)).
Если кратко — то не подходит.
Ищем дальше. Я пробегаюсь по виндовым хостерам, ищу возможность аренды обычных vps/vds но с возможностью организовать локальную сеть между ними. У многих попросту отсутствует такая возможность. Да, можно арендовать виртуалку, поднять на ней VPN и другой виртуалкой зацепиться к этой сети. Но такая «локальная» сеть натягивается поверх http, поэтому заведомо медленнее. Я уж было подумал, что у меня ничего не получится, но у хостера 1cloud вижу искомое «Всего в пару кликов бесплатно объединяйте ваши виртуальные машины в частную сеть с пропускной способность 1Гбит/с.» Кстати, у них есть обычные сервера в Питере и высокопроизводительные в Москве. Поддержка говорит, что на московской инфраструктуре разворачиваются локальные сети с пропускной способностью 10Гбит/с.
Отлично, попробуем сделать то же самое, что выше, только для обычного хостинга.
В студии создаем решение, я назвал его sharedTest, в которое добавляем:
1. MVC приложение с аналогичным названием.
2. Библиотеку классов windows, я назвал просто Lib
3. Консольное приложение windows, я назвал его worker

Далее, необходимо связать эти проекты воедино, для этого жмём правой кнопкой на наше решение и идём в свойства

Тут необходимо в запускаемом проекте выбрать пункт, что у меня несколько запускаемых и мой Worker поднять повыше, чем сайт.
Тут же следующее свойство — зависимость проектов. Библиотека не зависит ни от кого. Сайт sharedTest зависит и от библиотеки и от воркера. Воркер зависит только от библиотеки.
Не забудем в свойствах воркера (так же правой кнопкой и свойства) указать запускаемый объект, чтобы вначале запускался воркер и никого не ждал.

Следующий шаг — добавляем зависимости. В библиотеке Lib правой кнопкой по связям (Reference) -> добавить ссылку… и нам понадобится пакет System.ServiceModel. Этот же пакет нам нужен и в воркере. Таким же образом в сайте и воркере добавляем ссылку на нашу библиотеку из проекта Lib.
В библиотеку помещаем (удобно отдельными файлами) public interface IwcfChat и public sealed class wcfChat:IDisposable (мой первый листинг наверху). Так же там находится справочник с общими настройками или структурами, там у меня лежит только айпи адрес (откуда я его взял будет немного позже).
Сделаем наш воркер хостом для службы (аналогично верхнему листингу, только перенес в функцию Main, т.к. у меня консольное приложение)
Порты 8003 и 9003 я взял из головы, главное, чтобы они не были стандартными и не были заняты.
Добавляем в воркер класс wcfChat — реализацию нашего интерфейса
Не стал тут ничего расписывать, но значения приходят. Самое время попробовать — модифицируем наш контроллер Home и его представление
Запускаем и видим, что всё работает:

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

Далее создаем локальную сеть, тоже парой кликов, второй пункт из панели управления, я назвал её testLocal. Кстати, я выключил флажок динамичных айпишников (DHCP), так как мой сервис зависит от статичного айпишника, из-за этого придется еще немного повозиться.

После создания, всё так же в панели управления, заходим в каждый сервер, идём на вкладку Настройки, находим Частные сети и переключаем флажок, что мы пользуемся вновь созданной сетью. Этот флаг добавит нам новый адаптер и новую сеть внутри виртуальной машины. Там же нам выдаётся внутренний айпишник, у меня это были 10.0.0.5 и 10.0.0.6 для сайта и воркера. Чтобы сеть заработала лезем по удаленному рабочему столу (RDP) на свои виртуалки и вручную вбиваем эти значения в настройки сети. Инструкции для тех, кто не разбирается, лежат там же рядом.
Хочу предупредить, что сразу у меня не заработало. Я указал сети общедоступными, поэтому пришлось их переделывать в частные вот так . А так же нужно добавить мои используемые порты в исключения брандмауэра. Для этого я вызвал запуск (Run) клавиши win+R и набрал там netsh.exe
после чего в командной строке набрал команду
Теперь расскажу о публикации. Я настроил IIS по этому мануалу. Проверить работоспособность можно из браузера по внешнему айпишнику. Картиночка из IIS говорит нам о том, что всё работает.

Я ничего не делал ни с ftp, ни, тем более, с настройкой публикации из системы контроля версий, это тема другой статьи. Я подправил айпишник на выданный, опубликовал моё веб-приложение в файловую систему и ручками закинул в папочку C:\inetpub\wwwroot на сервере. Обновил страницу и увидел ожидаемую ошибку по адресу Home/Index, так как службы еще не было.
Собственно, мой воркер я так же опубликовал в файловую систему, получился установочник. Тут единственное, нужно не запутаться с DEBUG/RELEASE. Всю папочку я перетащил на рабочий стол второй виртуалки, левой кнопкой мыши установил и запустил. Вылезло моё чёрное окно, которое говорило, что сервис работает. Обновляем сайт, видим, что всё удалось.

Таким образом я получил работоспособный распределенный сервер. Чат я привел как пример, хотя в комментариях говорят, что за это нужно бить по рукам ) Вообще эту технологию я использую в браузерной игре, где функции расчета поединка вынесены на отдельный сервер. Из плюсов то, что я могу повысить производительность только одного конкретного сервера, если мне будет необходимо. Таким же образом можно сделать и для двух веб-сайтов, к примеру, обрабатывать фотографии будет первый сервер, а для хранения изображений будет использоваться второй сервер на поддомене (передача изображения идёт один раз, а дальше второй лишь отображает).
Естественно, реализация не идеальна, это лишь оправная точка. Необходимо настроить корректную и удобную публикацию. К тому же тут жесткая связь 1 к 1, а если нужно будет несколько воркеров, то необходим будет балансировщик нагрузки. Так же, по-хорошему, нужно избавиться от статичных внутренний айпишников. Ну и в целом для быстродействия нужно будет заменить wcf связь на реализацию напрямую через tcp сокеты.
Итак, я создаю облачную службу, в которую добавляю одну веб-роль (виртуальная машина с IIS), один воркер (виртуальная машина без IIS) и общую библиотеку классов. После публикации моим ролям присваивается ip-адрес и разные порты. То есть сразу есть tcp сеть, ручные настройки минимальны и могут делаться в самой студии. Можно, к примеру, сделать общедоступную точку доступа для воркера, но мне это не нужно. Мой воркер будет скрыт от внешних глаз и на нём будет висеть wcf-сервер, а общаться мои роли будут по быстрой локальной сети.
Общие классы я выношу в библиотеку (которую подключаю ко всем ролям), к примеру, интерфейс и канал связи:
[ServiceContract] public interface IwcfChat { [OperationContract] string SendMessage(string userId, string userName, string text); [OperationContract] string GetMessages(string userId, string userName); } //================ public sealed class wcfChat:IDisposable { IwcfChat _channel; ChannelFactory<IwcfChat> factory = null; public wcfChat() { NetTcpBinding b = new NetTcpBinding(); b.Security.Mode = SecurityMode.None; b.Security.Message.ClientCredentialType = MessageCredentialType.None; #if(DEBUG) EndpointAddress address = new EndpointAddress("net.tcp://127.255.0.1:9003/wcfChat"); #else EndpointAddress address = new EndpointAddress("net.tcp://"+spr.wcfIP+":9003/wcfChat"); #endif factory = new ChannelFactory<IwcfChat>(b, address); factory.Faulted += OnChannelFaulted; factory.Open(); } public IwcfChat channel { get { if (factory != null && factory.State == CommunicationState.Opened) { if(_channel==null) _channel = factory.CreateChannel(); return _channel; } return null; } } void OnChannelFaulted(object sender, EventArgs e) { factory.Abort(); } public void Dispose() { factory.Close(); } }
Методы в веб-роли вызываю так:
using (var chat = new wcfChat()) { res = chat.channel.SendMessage(id, name, text); }
Соответственно, в воркере у меня реализация методов из интерфейса (SendMessage, GetMessage), не буду их расписывать, а так же в воркере при старте выполняется код, который делает его хостом для wcf:
public override bool OnStart() { // Задайте максимальное число одновременных подключений ServicePointManager.DefaultConnectionLimit = 12; // Create the host ServiceHost host = new ServiceHost(typeof(wcfChat)); // Read config parameters string hostName = RoleEnvironment.CurrentRoleInstance.InstanceEndpoints["port"].IPEndpoint.Address.ToString(); // Create Metadata ServiceMetadataBehavior metadatabehavior = new ServiceMetadataBehavior(); host.Description.Behaviors.Add(metadatabehavior); Binding mexBinding = MetadataExchangeBindings.CreateMexTcpBinding(); string mexendpointurl = string.Format("net.tcp://{0}:{1}/wcfChatMetadata", hostName, 8003); host.AddServiceEndpoint(typeof(IMetadataExchange), mexBinding, mexendpointurl, new Uri(mexendpointurl)); // Create end point string endpointurl = string.Format("net.tcp://{0}:{1}/wcfChat", hostName, 9003); host.AddServiceEndpoint(typeof(IwcfChat), new NetTcpBinding(SecurityMode.None), endpointurl, new Uri(endpointurl)); // Open the host host.Open(); // Trace output Trace.WriteLine("WCF Listening At: " + endpointurl); Trace.WriteLine("WCF MetaData Listening At: " + mexendpointurl); return base.OnStart(); }
Тут я показал методы типа string, но тип возвращаемого значения может быть абсолютно любой, к примеру, сложный класс, который описан в моей общей библиотеке. Всё, никаких других настроек в вебконфигах, никаких упаковок в xml или SOAP у меня нет. Нет автоматически генерируемых файлов контрактов. Когда я пытался это сделать, в интернетах при поиске информации про wcf всегда всплывает настройка под http со всеми этими упаковками/распаковками. Да, wcf технология изначально придумана для связи разнородных систем, где необходимо сериализовать и передавать как строку. Но если у нас один язык программирования, а клиент и сервер wcf — это всё наша разработка, то не нужно городить огород, всё прекрасно работает.
Таким образом у меня получается распределенный сервер, для затратных операций я делаю отдельный воркер, тут, к примеру, показан чат. Все функции чата вынесены в отдельную виртуальную машину и я могу увеличивать производительность как самого сайта (веб-роли), так, отдельно, и чата.
Такая архитектура мне кажется правильной и может выдержать более сильную нагрузку, чем монолитный проект. Так же всё это разрабатывается и отлаживается как один проект в студии, то есть при вызове метода по wcf я могу в отладке пройти глубже в код, без сообщений типа «внутри отлаживать я не могу, не знаю что там происходит, вот такой вернулся результат».
С Азуровскими инструментами это делается всё легко и просто, я запускаю весь проект одной кнопкой и отлаживаюсь по нему как по одному проекту. Я помню когда-то давно, на одном из проектов была похожая архитектура, но я был молод, и мне там было очень сложно: какие-то фасады, и, если параметры у метода менялись, я изменения в четырёх местах вносил.
Мне нужно нечто подобное сделать на обычных серверах. Не на своих физических серверах, а на vds/vps виртуалках у какого-нибудь обязательно российского хостера. Я нашел забавную технологию у одного из хостеров (1gb.ru), называется ресурс Windows Azure Pack / COSN. По описанию это урезанная версия Азуры и всё должно было бы «взлететь». Конечно же, у них есть пробный период, я посмотрел. В браузере управление как-будто из старой версии портала Азуры (если кто помнит).

Из возможностей есть только создание виртуальных машин, объединение их в сеть (вручную), есть service bus — и всё. Так же нет возможности загрузить эту подписку в студию, а в этом и есть многое из удобств. Плюс нет возможности создания облачной службы. В описании сказано, что это «частное облако» будет дорабатываться и дополняться возможностями с оригинальной Азуры, но поддержка сказала, что всё уже давно заморожено, и вообще, это несовместимые технологии («этот Azure Pack совершенно по смыслу не совместим с настоящим Azure. Там что есть, то есть, и есть там маловато. Идеи там общие, но реализация разная совершенно, API разные и так далее.» (с)).
Если кратко — то не подходит.
Ищем дальше. Я пробегаюсь по виндовым хостерам, ищу возможность аренды обычных vps/vds но с возможностью организовать локальную сеть между ними. У многих попросту отсутствует такая возможность. Да, можно арендовать виртуалку, поднять на ней VPN и другой виртуалкой зацепиться к этой сети. Но такая «локальная» сеть натягивается поверх http, поэтому заведомо медленнее. Я уж было подумал, что у меня ничего не получится, но у хостера 1cloud вижу искомое «Всего в пару кликов бесплатно объединяйте ваши виртуальные машины в частную сеть с пропускной способность 1Гбит/с.» Кстати, у них есть обычные сервера в Питере и высокопроизводительные в Москве. Поддержка говорит, что на московской инфраструктуре разворачиваются локальные сети с пропускной способностью 10Гбит/с.
Отлично, попробуем сделать то же самое, что выше, только для обычного хостинга.
В студии создаем решение, я назвал его sharedTest, в которое добавляем:
1. MVC приложение с аналогичным названием.
2. Библиотеку классов windows, я назвал просто Lib
3. Консольное приложение windows, я назвал его worker

Далее, необходимо связать эти проекты воедино, для этого жмём правой кнопкой на наше решение и идём в свойства

Тут необходимо в запускаемом проекте выбрать пункт, что у меня несколько запускаемых и мой Worker поднять повыше, чем сайт.
Тут же следующее свойство — зависимость проектов. Библиотека не зависит ни от кого. Сайт sharedTest зависит и от библиотеки и от воркера. Воркер зависит только от библиотеки.
Не забудем в свойствах воркера (так же правой кнопкой и свойства) указать запускаемый объект, чтобы вначале запускался воркер и никого не ждал.

Следующий шаг — добавляем зависимости. В библиотеке Lib правой кнопкой по связям (Reference) -> добавить ссылку… и нам понадобится пакет System.ServiceModel. Этот же пакет нам нужен и в воркере. Таким же образом в сайте и воркере добавляем ссылку на нашу библиотеку из проекта Lib.
В библиотеку помещаем (удобно отдельными файлами) public interface IwcfChat и public sealed class wcfChat:IDisposable (мой первый листинг наверху). Так же там находится справочник с общими настройками или структурами, там у меня лежит только айпи адрес (откуда я его взял будет немного позже).
public static class spr { public static string wcfIP = "10.0.0.6"; }
Сделаем наш воркер хостом для службы (аналогично верхнему листингу, только перенес в функцию Main, т.к. у меня консольное приложение)
class Program { static void Main(string[] args) { // Задайте максимальное число одновременных подключений ServicePointManager.DefaultConnectionLimit = 12; // Create the host ServiceHost host = new ServiceHost(typeof(wcfChat)); // Read config parameters #if (DEBUG) string hostName = "127.255.0.1"; #else string hostName = spr.wcfIP; #endif // Create Metadata ServiceMetadataBehavior metadatabehavior = new ServiceMetadataBehavior(); host.Description.Behaviors.Add(metadatabehavior); Binding mexBinding = MetadataExchangeBindings.CreateMexTcpBinding(); string mexendpointurl = string.Format("net.tcp://{0}:{1}/wcfChatMetadata", hostName, 8003); host.AddServiceEndpoint(typeof(IMetadataExchange), mexBinding, mexendpointurl, new Uri(mexendpointurl)); // Create end point string endpointurl = string.Format("net.tcp://{0}:{1}/wcfChat", hostName, 9003); host.AddServiceEndpoint(typeof(IwcfChat), new NetTcpBinding(SecurityMode.None), endpointurl, new Uri(endpointurl)); // Open the host host.Open(); Console.WriteLine("Host opened on "+hostName); Console.ReadLine(); } }
Порты 8003 и 9003 я взял из головы, главное, чтобы они не были стандартными и не были заняты.
Добавляем в воркер класс wcfChat — реализацию нашего интерфейса
using Lib; namespace Worker { class wcfChat : IwcfChat { public string SendMessage(string userId, string userName, string text) { return "Sending message...."; } public string GetMessages(string userId, string userName) { return "Get messages"; } } }
Не стал тут ничего расписывать, но значения приходят. Самое время попробовать — модифицируем наш контроллер Home и его представление
public ActionResult Index(bool isGet = true) { string res = ""; using (var chat = new wcfChat()) { res = isGet ? chat.channel.GetMessages("1", "name") : chat.channel.SendMessage("1", "name", "text"); } return View((object)res); }
@model string @{ ViewBag.Title = "Home Page"; } <br /><br /> <div>@Model</div>
Запускаем и видим, что всё работает:

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

Далее создаем локальную сеть, тоже парой кликов, второй пункт из панели управления, я назвал её testLocal. Кстати, я выключил флажок динамичных айпишников (DHCP), так как мой сервис зависит от статичного айпишника, из-за этого придется еще немного повозиться.

После создания, всё так же в панели управления, заходим в каждый сервер, идём на вкладку Настройки, находим Частные сети и переключаем флажок, что мы пользуемся вновь созданной сетью. Этот флаг добавит нам новый адаптер и новую сеть внутри виртуальной машины. Там же нам выдаётся внутренний айпишник, у меня это были 10.0.0.5 и 10.0.0.6 для сайта и воркера. Чтобы сеть заработала лезем по удаленному рабочему столу (RDP) на свои виртуалки и вручную вбиваем эти значения в настройки сети. Инструкции для тех, кто не разбирается, лежат там же рядом.
Хочу предупредить, что сразу у меня не заработало. Я указал сети общедоступными, поэтому пришлось их переделывать в частные вот так . А так же нужно добавить мои используемые порты в исключения брандмауэра. Для этого я вызвал запуск (Run) клавиши win+R и набрал там netsh.exe
после чего в командной строке набрал команду
firewall set portopening protocol = TCP port = 9003 name = myService mode = ENABLE scope = SUBNET profile = CURRENT
Теперь расскажу о публикации. Я настроил IIS по этому мануалу. Проверить работоспособность можно из браузера по внешнему айпишнику. Картиночка из IIS говорит нам о том, что всё работает.

Я ничего не делал ни с ftp, ни, тем более, с настройкой публикации из системы контроля версий, это тема другой статьи. Я подправил айпишник на выданный, опубликовал моё веб-приложение в файловую систему и ручками закинул в папочку C:\inetpub\wwwroot на сервере. Обновил страницу и увидел ожидаемую ошибку по адресу Home/Index, так как службы еще не было.
Собственно, мой воркер я так же опубликовал в файловую систему, получился установочник. Тут единственное, нужно не запутаться с DEBUG/RELEASE. Всю папочку я перетащил на рабочий стол второй виртуалки, левой кнопкой мыши установил и запустил. Вылезло моё чёрное окно, которое говорило, что сервис работает. Обновляем сайт, видим, что всё удалось.

Таким образом я получил работоспособный распределенный сервер. Чат я привел как пример, хотя в комментариях говорят, что за это нужно бить по рукам ) Вообще эту технологию я использую в браузерной игре, где функции расчета поединка вынесены на отдельный сервер. Из плюсов то, что я могу повысить производительность только одного конкретного сервера, если мне будет необходимо. Таким же образом можно сделать и для двух веб-сайтов, к примеру, обрабатывать фотографии будет первый сервер, а для хранения изображений будет использоваться второй сервер на поддомене (передача изображения идёт один раз, а дальше второй лишь отображает).
Естественно, реализация не идеальна, это лишь оправная точка. Необходимо настроить корректную и удобную публикацию. К тому же тут жесткая связь 1 к 1, а если нужно будет несколько воркеров, то необходим будет балансировщик нагрузки. Так же, по-хорошему, нужно избавиться от статичных внутренний айпишников. Ну и в целом для быстродействия нужно будет заменить wcf связь на реализацию напрямую через tcp сокеты.
