Пишу веб-проекты в 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 сокеты.