0

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

web 应用程序启动时,我们想要加载一个缓存(从 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()移入OnStart().RoleEntryPoint

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

问题;

  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 会阻止角色实例同时被回收吗?
4

3 回答 3

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 ();  
   }  
 }  
于 2018-11-28T14:53:28.957 回答
0

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

这是我的想法如下。

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

希望能帮助到你。

于 2018-11-29T08:54:48.550 回答
0

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

编辑:更改为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 回收,我们在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:00AM) 并将角色错开 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>
于 2018-11-30T17:02:17.170 回答