Welcome to Azure. Пишем код и масштабируем приложение!

вторник, 12 апреля 2011, Малеев Дмитрий

Если бы у меня был апельсин, я бы с тобой поделился! Да, жаль что у тебя нету апельсина. (с) Ералаш

Ну что, мы немного посмотрели на то, как разрабатываются приложения для Ажура, и увидели что они немного не похожи на те приложения которые мы обычно разрабатываем. Сегодня я бы хотел поговорить про разработку приложений как для Ажура, так и для приложений которые никогда в жизни не увидят облаков. Итак, что бросается в глаза в первую очередь? Естественно чтение конфигурации. Но тут есть простое как валенок решение. Помните, в одном из прошлых постов мы обсуждали с Вами метод определения или приложение крутиться на Ажуре? Вот его то надо и использовать! Давайте посмотрим такой пример?

/// <summary>
        /// Gets the setting.
        /// </summary>
        /// <param name="settingName">Name of the setting.</param>
        /// <returns></returns>
        public static string GetSetting(string settingName)
        {
            if (RoleEnvironment.IsAvailable)
            {
                return RoleEnvironment.GetConfigurationSettingValue(settingName);
            }
            else
            {
                return ConfigurationManager.AppSettings[settingName];
            }
        }

Самый элементарный метод. Собственно, если использовать данный метод, то в случае когда у нас приложение будет крутиться на Ажуре, за настройками оно будет лезть в файл конфигурации наших веб ролей, если же приложение будет не Ажурское, то все настройки будут браться с web.config. Единственная штука в том, что Ваши библиотеки должны иметь ссылку на Microsoft.WindowsAzure.ServiceRuntime.

Что было бы более правильное?

Правильнее использоват IoC.

Инверсия управления (Inversion of Control, IoC) — важный принцип объектно-ориентированного программирования, используемый для уменьшения связанности в компьютерных программах и входящий в пятерку важнейших принципов SOLID. Наиболее популярной реализацией IoC является Dependency Injection Principle (Принцип внедрения зависимостей). Dependency Injection используется во многих framework'ах, они называются IoC контейнеры. Он применяется программистами, использующими такие объектно-ориентированные языки программирования, как Smalltalk, C++, Java или языки платформы .NET. (c)Wikipedia

В .Net лучше всего использовать такую штуку, как Unity Application Block ( часть Microsoft Enterprise Library 5.0 ). Идея в чем - у Вас должно быть две библиотеки, каждая из которые реализирует вытаскивание определенных настроек - одна из web.config или app.config, другая же будет вытаскивать настройки из файла конфигурации Ажур проекта.

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

        interface IConfigurationSettings
        {
            string GetSetting(string settingName);
        }

Этот интерфейс реализует единственный метод, который называется GetSettings. Он получает имя параметра, и возвращает его строчное значение параметра.

Теперь давайте создадим объект который будет читать значние параметров из конфигурационного файла не Azure приложения.

class AppConfigConfiguratonSettings : IConfigurationSettings
        {
            public override string GetSetting(string settingName)
            {
                return ConfigurationManager.AppSettings[settingName];
            }
        }

Как видим, этот класс наследует наш интерфейс, и при вызове метода GetSetting читает конфигурационный файл и возвращает значение выбранного параметра.

Давайте создадим класс который читате настройки Azure проекта.

   class RoleEnvironmentConfigurationSettings : IConfigurationSettings
        {
            public override string GetSetting(string settingName)
            {
                return RoleEnvironment.GetConfigurationSettingValue(settingName);
            }
        }

Этот класс может жить в отдельной библиотеке, и иметь ссылку на Microsoft.WindowsAzure.ServiceRuntime.

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

using Microsoft.Practices.Unity;
using Microsoft.Practices.Unity.Configuration;
using Microsoft.Practices.Unity.StaticFactory;

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

IUnityContainer myContainer = new UnityContainer();
IConfigurationSettings configurationSettings = myContainer.Resolve();
configurationSettings.GetSetting("testSettings");

Теперь в Нашей не Azure версии приложения, мы должны использовать следующий код:

IUnityContainer myContainer = new UnityContainer();
myContainer.RegisterType();

Для Azure версии, стоит использовать следующий код:

IUnityContainer myContainer = new UnityContainer();
myContainer.RegisterType();

Вот собтсвенно и все. Как видим, все достаточно просто, потому вперед использовать!:) Соственно этот метод можна использовать и для других участков где необходима разная функциональность:)

Теперь давайте немного о той штуке, из за которой клауды такие отличные - масштабировании веб ролей!

Масштабирование веб ролей.

Тут надо поговорить для начала, почему именно этим клауд такой клевый. Я уже приводил пример как клауды могут сохранить деньги в первом своем посте об Azure: "Для того чтобы запустить свое решение Вам больше не нужно строить свои датацентры, нанимать целую орду одминов, и платить за суппорт этой всей радости. Вы платите только за то что Вы используете. Представте ситуацию, Вы купили сервер. На нем висит Ваш сайт, который продает футболки к чемпионату мира. Понятное дело, что чем ближе чемпионат – тем заказов будеть больше, а следовательно количество посетителей возрастет. В какой то определенный момент Ваш сайт начнет очень жутко тормозить от наплыва клиентов, для того чтобы сайт работал как прежде Вы будете вынуждены купить новый сервер. Чемпионат закончился, количество посетителей упало, теперь Ваш новый сервер тихонько гудит под столом, и собирает пыль. А деньги вы уже потратили." Да, тут все отлично - как только растет нагрузка на сервера, вы просто увеличиваете количество ролей на 1..n, и после того как нагрузка спадает - отключаете некоторые роли. Все просто - Вы платите больше только какой то определенный промежуток времени.

Давайте же теперь посмотрим как это все происходит в Azure! Собственно рассказывать наверное не имеет смысла, потому как надо показывать:) Так что, давайте напишем простую программулину, которая будет делать нигде не применимые вещи, но которая явно покажет че и как. Итак, начнем с того что создадим обычную ASP.Net веб роль. В default.aspx добавим такой код:

<asp:ScriptManager ID="ScriptManager1" runat="server" />
    <asp:UpdatePanel ID="RequestsPanel" runat="server" UpdateMode="Always">
        <ContentTemplate>
            <asp:Timer ID="Timer1" runat="server" Interval="5000" OnTick="Timer1_Tick" />
            <asp:Label ID="resultLabel" runat="server" />
        </ContentTemplate>
    </asp:UpdatePanel>

А в код бихайнд такое:

        /// <summary>
        /// Handles the Load event of the Page control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> 
instance containing the event data.</param>
        protected void Page_Load(object sender, EventArgs e)
        {

        }

        /// <summary>
        /// Handles the Tick event of the Timer1 control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> 
instance containing the event data.</param>
        protected void Timer1_Tick(object sender, EventArgs e)
        {
            resultLabel.Text += DateTime.Now.ToString() + " <br/>";
        }

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

 

07.03.2011 23:48:53
07.03.2011 23:48:58
07.03.2011 23:49:03
07.03.2011 23:49:08
07.03.2011 23:49:13
07.03.2011 23:49:18
07.03.2011 23:49:23
07.03.2011 23:49:28
07.03.2011 23:49:33
07.03.2011 23:49:38
07.03.2011 23:49:43
07.03.2011 23:49:48

Вроде все пашет отлично. Давайте же теперь завалим наш сайт огромным количеством запросов. Сделать сие элементарно - пишем консольное приложение которое сэмулирует огромное количество посещений на наш сайт:

        /// <summary>
        /// Mains the specified args.
        /// </summary>
        /// <param name="args">The args.</param>
        static void Main(string[] args)
        {
            var webAddress = new Uri("http://127.0.0.1:83/Default.aspx");
            for (int i = 0; i < 100; i++)
            {
                var t1 = new System.Threading.Thread
                (() =>
                {
                    for (int j = 0; j < 30; j++)
                    {
                        WebClient client = new WebClient();
                        client.DownloadStringAsync(webAddress);
                    }
                });
                t1.Start();
            }
        }

После запуска этого приложения , у меня даже музыка начала тормозить:) вообщем , что выдал на сайт:

08.03.2011 0:03:41
08.03.2011 0:03:46
08.03.2011 0:03:51
08.03.2011 0:03:56
08.03.2011 0:04:06
08.03.2011 0:04:12
08.03.2011 0:04:19

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

Как решать?

Самый просто способ это масштабировать железяки. Тут надо разделять две возомжности: а. Масштабированность по горизонтали. Это тупое увеличение количества машин: если это ваш приватный датацентр - вы просто доставляете количество машин, если же это облако на Ажуре - то это увеличение количества инстансов виртуалок, на которых крутиться ваша веб роль. Делается это просто, как мы уже говорили через конфигурационный файл веб или воркер роли:

<WebRole name="MyWebRole"
  <Instances count="1" />
  <ConfigurationSettings />
</WebRole>

Легким нажатием клавиатуры мы увеличиваем количество виртуалок на которых крутиться наш сайт в два раза, просто изменив один параметр:

<Instances count="2" />

Вот он - рост в ширину:)

б. Масштабируемость по вертикали. Это когда вы идете к своему серверу, вытаскиваете из него 2 гигабайта мозгов, а обратно засовываете 16. Масштабируемость по вертикали - это просто улучшение железа Вашего сервера. Если же у нас все крутится на Ажуре - то это увеличение размера виртуальной машины. Это значание прописывается в описании ( не конфигурации!!! ) сервиса.

<WebRole name="MyWebRole" vmsize="Medium">

Тут одним изменением значения параметра, мы перекинули нашу аппликацию на более мощную виртуалку.

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

Compute Instance Size CPU Memory Instance Storage I/O Performance Cost per hour
Extra Small 1.0 GHz 768 MB 20 GB Low $0.05
Small 1.6 GHz 1.75 GB 225 GB Moderate $0.12
Medium 2 x 1.6 GHz 3.5 GB 490 GB High $0.24
Large 4 x 1.6 GHz 7 GB 1,000 GB High $0.48
Extra large 8 x 1.6 GHz 14 GB 2,040 GB High $0.96

Следовательно, перед выбором пути масштабирования, стоит пересмотреть архитектуру приложения, и посчитать какой из вариантов масштабирования принесет Вам больше пользы!

Полезные ссылки: http://msdn.microsoft.com/en-us/magazine/cc500561.aspx - Scaling Strategies for ASP.NET Applications

http://technet.microsoft.com/en-us/library/cc754833(WS.10).aspx - Network Load Balancer в домашних условиях:) Ессно только если у вас есть Windows Server 2008:)

http://unity.codeplex.com/ - Unity 2.0

Компании из статьи


Microsoft Украина


Сайт:
http://www.microsoft.com/ukr/ua/

Microsoft Украина Украинское подразделение компании Microsoft.

Ищите нас в интернетах!

Комментарии

Свежие вакансии