给予使用.net 4.5.2上的Azure SDK 3.0和OS系列“ 4”(Windows 2012)到Azure云服务WebRoles(2)的部署。
该Web应用程序启动时,我们希望加载大约10分钟的缓存(从blob存储中加载)(我们已经考虑过移动它,但目前无法这样做)
然后,当IIS应用程序池回收时,我们希望网站保持正常运行。
当前Cloud Services的默认IIS设置为:
由于我们默认使用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}}中太难了。
注意;默认情况下,我们还启用了“保持活动”。
问题;
答案 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)
根据您的描述,基于我的理解和经验,我认为在当前场景中几乎不可能满足您的所有需求,而这需要在体系结构上进行更改。
这是我的想法,如下。
keep-alive
连接的重试机制。然后,当某个角色实例停止或重新启动时,现有的连接将被重定向到另一个角色实例。希望有帮助。
答案 2 :(得分:0)
这就是我们最终得到的:
编辑:已更改为HttpWebRequest
,因此支持重定向
a)部署虚拟机/对操作系统进行修补后,我们将在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: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>