Как вы балансируете нагрузку веб-ролей облачных служб Azure при загрузке кэша - PullRequest
0 голосов
/ 27 ноября 2018

При условии развертывания в веб-ролях облачных служб Azure (2) с использованием Azure SDK 3.0 в .net 4.5.2 и семейства ОС "4" (Windows 2012).

Когда запускается веб-приложение, мы хотим загрузить кэш (из хранилища BLOB-объектов), который занимает около 10 минут (мы рассмотрели его перемещение, но в настоящее время не можем)

Затем когда пулы приложений IIS перезагружаются, мы хотим, чтобы сайт работал.

В настоящее время настройки IIS по умолчанию в облачных службах:

  • Не запускаться при загрузке (autoStart /startMode)
  • Для простоя каждые 20 минут (idleTimeout)
  • Для перезапуска каждые 29 часов (periodRestart)
  • с ошибками в виде HTTP 503 (loadBalancerCapabilities)

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

Пока у нас есть скрипт запуска задачи для перенастройки IIS AppPools

appcmd set config -section:system.applicationHost/applicationPools 

с

  /applicationPoolDefaults.autoStart:"True"
  /applicationPoolDefaults.startMode:"AlwaysRunning"
  /applicationPoolDefaults.processModel.idleTimeout:"00:00:00" 
  /applicationPoolDefaults.recycling.logEventOnRecycle:"Time,Requests,Schedule,Memory,IsapiUnhealthy,OnDemand,ConfigChange,PrivateMemory"
  /applicationPoolDefaults.recycling.periodicRestart.time:"00:00:00" 
  /~"applicationPoolDefaults.recycling.periodicRestart.schedule" 
  /+"applicationPoolDefaults.recycling.periodicRestart.schedule.[value='06:00:00']" 
  /applicationPoolDefaults.failure.loadBalancerCapabilities:"TcpLevel" 

например

%windir%\system32\inetsrv\appcmd set config -section:applicationPools /applicationPoolDefaults.autoStart:"True" /commit:apphost

Что касается кода, мы рассматривали использование флага Busy до тех пор, пока кэш не загрузится.Похоже, это не перенаправляет трафик

RoleEnvironment.StatusCheck += WebRoleEnvironment_StatusCheck;

с

        if (Busy)
        {
            e.SetBusy();
        }

Недостатком является то, что это делается в Application_Start из-за требуемых контейнеров.Я думаю, что было бы слишком трудно переместить LoadCache() в OnStart() из RoleEntryPoint.

Примечание;У нас также по умолчанию включено «Keep-alive».

Вопросы;

  1. Как перевести WebHost в автономный режим, когда он загружает кэш?
  2. Следует ли изменить настройки IIS?https://azure.microsoft.com/en-gb/blog/iis-reset-on-windows-azure-web-role/
  3. Должны ли мы использовать инициализацию приложений IIS 8.0?http://fabriccontroller.net/iis-8-0-application-initialization-module-in-a-windows-azure-web-role/
  4. Что должно быть установлено в loadBalancerCapabilities?https://docs.microsoft.com/en-us/iis/configuration/system.applicationhost/applicationpools/add/failure
  5. Должны ли мы попытаться сделать перебор?Как насчет того, когда мы масштабируем (добавляем больше экземпляров) Предотвращает ли Azure предотвращение повторного использования экземпляров ролей?

Ответы [ 3 ]

0 голосов
/ 29 ноября 2018

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

Вот моя идеякак показано ниже.

  1. Я предполагаю, что файл большого двоичного объекта кэша слишком велик, что потребовало больше времени для загрузки кэша из хранилища больших двоичных объектов.Так что для сокращения затрат времени.Я думаю, что решение состоит в том, чтобы разбить файл кеш-буфера по статистике использования на множество более мелких и одновременно загружать их, или использовать хранилище таблиц вместо хранилища блобов в качестве кэша L2, просто запросить данные кэша из хранилища таблиц и сохранить их в памятив качестве кеша L1 со временем истечения, даже вы можете использовать Azure Redis Cache для хранения данных кеша, которые работают быстрее, чем хранилище таблиц.
  2. Убедитесь, что для соединения keep-alive существует механизм повторных попыток.Затем существующее соединение будет перенаправлено на другой экземпляр роли при остановке или перезапуске экземпляра роли.
  3. Для реализации функции перезапуска экземпляра роли существует REST API Reboot Role Instance который может это сделать.

Надеюсь, это поможет.

0 голосов
/ 30 ноября 2018

Вот что у нас получилось:

РЕДАКТИРОВАТЬ : изменено на HttpWebRequest, так что перенаправления поддерживаются

a) Когда виртуальная машина развернута /При исправлении ОС мы опрашиваем конечную точку httpsIn в OnStart()

public class WebRole : RoleEntryPoint
{
    public override bool OnStart()
    {
        ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;

        // Note: the Web Requests all run in IIS, not from this process.
        // So, we aren't disabling certs globally, just for checks against our own endpoint.
        ServicePointManager.ServerCertificateValidationCallback += (o, certificate, chain, errors) => true;

        var address = GetAddress("httpIn");

        var request = (HttpWebRequest)WebRequest.Create(address);
        request.MaximumAutomaticRedirections = 1;
        request.AllowAutoRedirect = false;
        var response = request.GetResponse() as HttpWebResponse;
        //_logger.WriteEventLog($"Response: '{response?.StatusCode}'");
        return base.OnStart();
    }

    static Uri GetAddress(string endpointName)
    {
        var endpoint = RoleEnvironment.CurrentRoleInstance.InstanceEndpoints[endpointName];
        var address = $"{endpoint.Protocol}://{endpoint.IPEndpoint.Address}:{endpoint.IPEndpoint.Port}";
        return new Uri(address);
    }
}

b) Для AppPool Recycle мы сообщаем о занятости в Global.asax

public class RoleEnvironmentReadyCheck
{
    bool _isBusy = true;

    public RoleEnvironmentReadyCheck()
    {
        RoleEnvironment.StatusCheck += RoleEnvironment_StatusCheck;
    }

    void RoleEnvironment_StatusCheck(object sender, RoleInstanceStatusCheckEventArgs e)
    {
        if (_isBusy)
        {
            e.SetBusy();
        }
    }

    public void SetReady()
    {
        _isBusy = false;
    }
}

public class WebApiApplication : HttpApplication
{
    protected void Application_Start()
    {
        var roleStatusCheck = new RoleEnvironmentReadyCheck();
        //SuperLoadCache()
        roleStatusCheck.SetReady();
    }
}

в) Для повторов в AppPool мы выбираем время дня (03:00) и разбиваем роли на 30 минут и останавливаем время простоя в скрипте PowerShell ConfigureIIS.ps1

$InstanceId = $env:INSTANCEID
$role = ($InstanceId -split '_')[-1]
$roleId = [int]$role
$gapInMinutes = 30
$startTime = New-TimeSpan -Hours 3
$offset = New-TimeSpan -Minutes ($gapInMinutes * $roleId)
$time = $startTime + $offset
$timeInDay = "{0:hh\:mm\:ss}" -f $time

Write-Host "ConfigureIIS with role: $role to $timeInDay"

& $env:windir\system32\inetsrv\appcmd set config -section:system.applicationHost/applicationPools /applicationPoolDefaults.processModel.idleTimeout:"00:00:00" /commit:apphost
& $env:windir\system32\inetsrv\appcmd set config -section:system.applicationHost/applicationPools /applicationPoolDefaults.recycling.logEventOnRecycle:"Time,Requests,Schedule,Memory,IsapiUnhealthy,OnDemand,ConfigChange,PrivateMemory" /commit:apphost
& $env:windir\system32\inetsrv\appcmd set config -section:system.applicationHost/applicationPools /applicationPoolDefaults.recycling.periodicRestart.time:"00:00:00" /commit:apphost
& $env:windir\system32\inetsrv\appcmd set config -section:system.applicationHost/applicationPools /~"applicationPoolDefaults.recycling.periodicRestart.schedule" /commit:apphost
& $env:windir\system32\inetsrv\appcmd set config -section:system.applicationHost/applicationPools /+"applicationPoolDefaults.recycling.periodicRestart.schedule.[value='$timeInDay']" /commit:apphost

и передать RoleId в ConfigureIIS.cmd

PowerShell -ExecutionPolicy Unrestricted .\ConfigureIIS.ps1 >> "%TEMP%\StartupLog.txt" 2>&1

EXIT /B 0

, установленный в ServiceDefinition.csdef

 <Task commandLine="ConfigureIIS.cmd" executionContext="elevated" taskType="simple">
    <Environment>
      <Variable name="INSTANCEID">
        <RoleInstanceValue xpath="/RoleEnvironment/CurrentInstance/@id"/>
      </Variable>
    </Environment>
  </Task>
0 голосов
/ 28 ноября 2018

См. https://blogs.msdn.microsoft.com/kwill/2012/09/19/role-instance-restarts-due-to-os-upgrades/,, в частности, общие проблемы № 5:

Если вашему веб-сайту требуется несколько минут на прогрев (либо стандартный прогрев IIS / ASP.NET при предварительной компиляции и загрузке модуля, либо прогревкэша или других специфических задач приложения), тогда ваши клиенты могут испытывать сбой или случайные тайм-ауты.После того, как экземпляр роли перезапустится и ваш код OnStart завершится, ваш экземпляр роли будет возвращен в ротацию балансировщика нагрузки и начнет получать входящие запросы.Если ваш веб-сайт все еще прогревается, все эти входящие запросы будут поставлены в очередь и истечет время ожидания.Если у вас есть только 2 экземпляра вашей веб-роли, то IN_0, который все еще прогревается, будет принимать 100% входящих запросов, пока IN_1 перезапускается для обновления гостевой ОС.Это может привести к полному отключению вашего сервиса, пока ваш сайт не завершит прогрев в обоих случаях.Рекомендуется держать ваш экземпляр в OnStart, который будет держать его в состоянии «Занят», когда он не будет получать входящие запросы от балансировщика нагрузки, пока ваш прогрев не будет завершен.Для этого вы можете использовать следующий код:

 public class WebRole : RoleEntryPoint {  
   public override bool OnStart () {  
     // For information on handling configuration changes  
     // see the MSDN topic at http://go.microsoft.com/fwlink/?LinkId=166357.  
     IPHostEntry ipEntry = Dns.GetHostEntry (Dns.GetHostName ());  
     string ip = null;  
     foreach (IPAddress ipaddress in ipEntry.AddressList) {  
       if (ipaddress.AddressFamily.ToString () == "InterNetwork") {  
         ip = ipaddress.ToString ();  
       }  
     }  
     string urlToPing = "http://" + ip;  
     HttpWebRequest req = HttpWebRequest.Create (urlToPing) as HttpWebRequest;  
     WebResponse resp = req.GetResponse ();  
     return base.OnStart ();  
   }  
 }  
...