加载缓存时如何平衡Azure Cloud Services WebRoles

时间:2018-11-27 19:59:46

标签: azure azure-web-sites azure-web-roles azure-cloud-services azure-load-balancer

给予使用.net 4.5.2上的Azure SDK 3.0和OS系列“ 4”(Windows 2012)到Azure云服务WebRoles(2)的部署。

该Web应用程序启动时,我们希望加载大约10分钟的缓存(从blob存储中加载)(我们已经考虑过移动它,但目前无法这样做)

然后,当IIS应用程序池回收时,我们希望网站保持正常运行。

当前Cloud Services的默认IIS设置为:

  • 不在加载时启动(autoStart / startMode)
  • 每20分钟空闲一次(idleTimeout)
  • 每29小时进行一次回收(定期重启)
  • 作为HTTP 503(loadBalancerCapabilities)失败

由于我们默认使用2个WebHost,因此我们希望在不同时间回收应用程序池。理想情况下,如果其中一个Web主机正在加载缓存,则我们希望重定向来自站点的现有连接。

到目前为止,我们已经有了一个启动任务脚本来重新配置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()的{​​{1}}中太难了。

注意;默认情况下,我们还启用了“保持活动”。

问题;

  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. 我们应该尝试错开回收站吗?当我们扩展(添加更多实例)Does azure prevent that role instances are recycled at the same time?
  6. 时该怎么办?

3 个答案:

答案 0 :(得分:1)

请参见https://blogs.msdn.microsoft.com/kwill/2012/09/19/role-instance-restarts-due-to-os-upgrades/,特别是常见问题#5:

  

如果您的网站需要几分钟的时间进行预热(预编译和模块加载的标准IIS / ASP.NET预热,或者预热缓存或其他特定于应用程序的任务),则您的客户端可能会遇到中断或随机超时的情况。角色实例重新启动并且您的OnStart代码完成之后,您的角色实例将被放回负载平衡器循环中,并开始接收传入的请求。如果您的网站仍在预热,则所有这些传入请求都将排队并超时。如果您只有2个Web角色实例,则IN_0(仍在预热中)将在重新启动IN_1进行Guest OS更新时接收100%的传入请求。这可能导致您的服务完全中断,直到您的网站在两个实例上均完成预热为止。建议将您的实例保持在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 ();  
   }  
 }  

答案 1 :(得分:0)

根据您的描述,基于我的理解和经验,我认为在当前场景中几乎不可能满足您的所有需求,而这需要在体系结构上进行更改。

这是我的想法,如下。

  1. 我猜想缓存blob文件太大,这导致需要更多时间从blob存储中加载缓存。这样可以减少时间成本。我认为解决方案是将使用情况统计信息将缓存blob文件拆分为多个较小的文件,并同时加载它们,或者使用表存储而不是blob存储作为L2缓存,只需从表存储中查询缓存数据并将其存储到内存中即可作为具有到期时间的L1缓存,即使您可以使用Azure Redis缓存来存储缓存数据,其速度也比表存储快。
  2. 请确保存在用于keep-alive连接的重试机制。然后,当某个角色实例停止或重新启动时,现有的连接将被重定向到另一个角色实例。
  3. 要实现用于重新启动角色实例的功能,可以使用REST API Reboot Role Instance

希望有帮助。

答案 2 :(得分:0)

这就是我们最终得到的:

编辑:已更改为HttpWebRequest,因此支持重定向

a)部署虚拟机/对操作系统进行修补后,我们将在OnStart()

内轮询 httpsIn 端点
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回收,我们在 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();
    }
}

c)对于AppPool回收,我们选择一天中的某个时间(03:00 AM),并将角色错开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>