【问题标题】:How do you load balance Azure Cloud Services WebRoles when loading a cache加载缓存时如何对 Azure 云服务 WebRoles 进行负载平衡
【发布时间】:2018-11-27 19:59:46
【问题描述】:

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

网络应用程序启动时,我们想要加载一个缓存(从 blob 存储),这大约需要 10 分钟(我们已经考虑过移动它,但目前不能)

那么当 IIS 应用程序池回收时,我们希望站点保持正常运行。

目前云服务的默认 IIS 设置是:

  • 加载时不启动 (autoStart / startMode)
  • 每 20 分钟空闲一次 (idleTimeout)
  • 每 29 小时循环一次 (periodicRestart)
  • 以 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() 移动到RoleEntryPointOnStart() 中太难了。

注意;我们还默认开启了“保持活动”。

问题;

  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?

【问题讨论】:

    标签: azure azure-web-app-service azure-web-roles azure-cloud-services azure-load-balancer


    【解决方案1】:

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

    如果您的网站需要几分钟来预热(预编译和模块加载的标准 IIS/ASP.NET 预热,或者预热缓存或其他特定于应用程序的任务),那么您的客户端可能会遇到中断或随机超时。在角色实例重新启动并且您的 OnStart 代码完成后,您的角色实例将被放回负载均衡器轮换中并开始接收传入请求。如果您的网站仍在预热,那么所有这些传入请求都将排队并超时。如果您的 Web 角色只有 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 ();  
       }  
     }  
    

    【讨论】:

    • 看起来不错,但如果将缓存代码从 Application_Start() 移动到 OnStart() 太难怎么办?
    • 您不必将任何内容从 Application_Start() 移动到 OnStart()。 OnStart() 只是轮询网站,直到它响应。在 Application_Start() 完成之前网站不会响应。
    • 我们的站点强制使用 https :( [System.InvalidOperationException: ID1059: 由于 URL 方案不是 https 并且在配置中将 requireSsl 设置为 true,因此无法对用户进行身份验证,因此不会发送身份验证 cookie . ]
    • 另外,这并不能解决应用程序池回收的问题。他们应该将 WebHost 的状态更改为忙,直到 Application_Start 完成
    【解决方案2】:

    根据您的描述,根据我的理解和经验,我认为在当前场景下几乎不可能满足您的所有需求,需要在架构上进行更改。

    这是我的想法如下。

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

    希望对你有帮助。

    【讨论】:

    • 1.这不仅仅是读取,这是我们进行自定义搜索索引的方式。从长远来看,我们可能会使用我们的 redis 缓存/天蓝色搜索 - 2. 我同意,我们可能需要客户端(浏览器/JavaScript)来控制这种行为(超时和重试) - 3. 不确定强制重启会有什么帮助
    【解决方案3】:

    这就是我们最终得到的结果:

    编辑:更改为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 回收,我们选择一天中的某个时间(凌晨 3: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>
    

    【讨论】:

    • 请注意,client.GetStringAsync(address).Wait(); 仅在 VM 重新启动时调用。我们有一个轮询器,它会在 apppool 回收后预热网站。如果需要,可以使用applicationPoolDefaults.autoStartstartMode:"AlwaysRunning"
    猜你喜欢
    • 2013-04-18
    • 1970-01-01
    • 2018-10-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-05-24
    相关资源
    最近更新 更多