Why should i use a LoadBalancerProbe instead of subscribing to the RoleEnvironment.StatusCheck Event? - azure

I was fiddling with the options azure provides to balance the load between multiple webroles.
I found three possible ways to do this.
the first would be to do nothing at all and let the default ( round robin) implementation do the job.
the second possibility would be to define a custom LoadBalancerProbe in the ServiceDefinitionFile, which i tried and did not get to work: From my understanding the custom aspx page is called each time a status check is performed on the role. Depending on the http response code the role changes its status to busy. - but this is never happening.
Also, i couldnt really find any examples for defining a custom LoadBalancingProbe.
Thus i looked for an alternative way to do this.
Now iam subscribing to the RoleEnvironment.StatusCheck Event, which allows me to implement some logic and depending on the results set the role state to busy and available.
My Questions:
1) Supposing the Custom LoadBalancerProbe works as described in the MSDN, what is the difference between subscribing to the StatusCheckEvent and using a custom probe?
2) Why does my custom load balancer probe not work ? - iam just testing with the azure emulator for now and iam well aware that traffic still gets routed to the webrole instances allthough they are set to busy in the emulator.
But my Custom Probe does not change the status of the webroleinstances at all.
Here is the very rudimentary code, which should - to my knowledge set the status of webrole instance_n_0 to busy.
public class LoadBalanceController : Controller
{
public ActionResult Index()
{
WebOperationContext woc = WebOperationContext.Current;
if(RoleEnvironment.CurrentRoleInstance.Id.ToLower().Contains("_0"))
{
woc.OutgoingResponse.StatusCode = System.Net.HttpStatusCode.ServiceUnavailable;
}else
{
woc.OutgoingResponse.StatusCode = System.Net.HttpStatusCode.OK;
}
return View(); //not relevant
}
Ive also configered my servicedefinitionfile and set a Route to redirect to this controller/action when calling the healthcheck.aspx defined in the custom probe.
<LoadBalancerProbes>
<LoadBalancerProbe name="WebRoleBalancerProbeHttp" protocol="http" path="healthcheck.aspx" intervalInSeconds="5" timeoutInSeconds="100"/>
</LoadBalancerProbes>
...
<InputEndpoint name="EndpointWeb" protocol="http" port="80" loadBalancerProbe="WebRoleBalancerProbeHttp"/>
The Route:
routes.MapRoute(
name: "HealhCheck",
url: "healthcheck.aspx",
defaults: new { controller = "LoadBalance", action = "Index", id = UrlParameter.Optional }
);

Not sure why the custom probe isn't working, but for the differences: The health-check event lets you announce whether an instance is available, but you don't have any flexibility in terms of how often this is called. Also, you can't launch a separate service that listens on a custom port (or port type).
You have much more flexibility with custom probes, since you can create any type of port listener to determine health, even a separate exe.
With Virtual Machines, this is the only method of health probes, since Virtual Machines don't have the guest agent running and don't provide the health-check event.

In your service definition file, you have marked your probe to be hosted on an http endpoint (and not HTTPS).
Is that the case with your web app as well ? Is it exposing that endpoint on HTTP and not on HTTPS ? If yes, then also check if there is any automatic redirection to HTTPS happening.
I think you have mostly setup everything properly. Here is a post that has some example code and another question from SO, where they were successful in setting it up (should provide some insight into the csdef file).
I agree with David Makogon's points about the differences between the two.

Related

Azure Web Role warm up after Deploy

As described in various other related questions here, I am also expecting long lasting (30 seconds) first call after (re)deploying a web role with pretty large EF6 Model and a plenty of referenced nuget-packages. After trying out different proposed solutions with preloadEnabled and serviceAutoStartProviders I am still consufed and decided to reopen this topic in hope, that somebody has come upon a better solution in the meantime.
The main goal for me is to make a web role answering the first request almost as fast as subsequent calls as soon as the role quits its busy state on a fresh deployment and gets accessible by load balancer and external clients. Unfortunately I experienced following problems with proposed solutions so far:
preloadEnabled:
I do add Application Initialization module via PKGMGR.EXE /iu:IIS-ApplicationInit in a startup task. So far so good.
When I then try to execute %windir%\system32\inetsrv\appcmd set site "MySiteName" -applicationDefaults.preloadEnabled:true it fails as at the time of execution of a startup script there are still no websites created in IIS on a fresh deployment.
If I try to set the preloadEnabled-setting via ServerManager-class in my Application_Start method instead, I cannot understand, how this code is intended to be executed before a first external call on the web role is made, as the preloadEnabled setting is defaulted to false after a fresh web role deploy and thus the Application_Start method in my understanding does not get a chance to be executed by the Application Initialization module?
serviceAutostartProviders:
here we need to put a name of our AutostartProvider implementing the IProcessHostPreloadClient interface in applicationhost.config i.e by using either appcmd script or the ServerManager class, BUT:
serviceAutostartProvider is like preloadEnabled a site related setting, so we have the same problem here as with %windir%\system32\inetsrv\appcmd set site "MySiteName" -applicationDefaults.preloadEnabled:true in 1.2 - at execution time of startup script after a fresh deployment the websites are not yet created in IIS and the script fails to execute properly
Another possibility would be to include applicationhost.config into the deployment package, but I did not find any solution to do this for a web role.
So how did you guys managed to ensure, that preloading assemblies and some initialization code (like filling memory caches) is run before the role gets it's first hit from outside?
We start to gain some traffic now and get approx. 1-2 requests per second on our WebApi, and thus a 30 second delay for preloading "visible" to clients after each update deployment is becoming a major issue now.
We are scheduling update deploys at low traffic times, but what if I need to make an urgent hotfix deploy?
Perhaps the best way to accomplish this is to use deployment slots. Deploy updates to your staging slot first. Before a switch from a staging slot to a production slot takes place, Kudu will hit the root of the staging slot with a request in order to warm up the application. After the request to the staging slot's root returns, the IP switch occurs and your slots are swapped.
However, sometimes you need to warm up other pages or services to get the app ready to handle traffic, and hitting the root with a warmup request is insufficient. You can update your web.config so that Kudu will hit additional pages and endpoints before the IP switch occurs and the slots are swapped.
These warmup request URLs should be in the tag. Here's an example:
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<system.webServer>
<applicationInitialization>
<add initializationPage="/pagetowarmup1" />
<add initializationPage="/pagetowarmup2" />
<add initializationPage="/pagetowarmup3" />
</applicationInitialization>
</system.webServer>
</configuration>
You can read the Kudu docs on this issue here.
OK. Now I got it.
The point is to set preloadEnabled-property not inside of the Application_Start method in Global.asax (as it will not be hit before a first request to the Role anyway), but inside RoleEntryPoint.OnStart.
The difference is that RoleEntryPoint.OnStart is called directly after deployment package is extracted and everything is set up for starting the role. At this moment the azure instance is still in it's busy state and is not yet available from outside as long as some code inside RoleEntryPoint.OnStart is being executed.
So here is the code I came up so far for warming up the instance before it gets its first call from outside
public class WebRole : RoleEntryPoint
{
public override bool OnStart()
{
// set preloadEnabled of each Site in this deployment to true
using (var serverManager = new ServerManager())
{
foreach (var mainSite in serverManager.Sites)
{
var mainApplication = mainSite.Applications["/"];
mainApplication["preloadEnabled"] = true;
}
serverManager.CommitChanges();
}
// call my warmup-code which will preload caches and do some other time consuming preparation
var localuri = new Uri(string.Format("https://{0}/api/warmup", RoleEnvironment.CurrentRoleInstance.InstanceEndpoints["Endpoint1"].IPEndpoint));
try
{
var request = (HttpWebRequest)WebRequest.Create(localuri);
request.Method = "GET";
var response = request.GetResponse();
}
catch { }
// send this thread to sleep in order to give the warmup-request a chance to complete BEFORE the Role will get started
// and becomes available to the outside world
System.Threading.Thread.Sleep(60000);
return base.OnStart();
}
}

Web api routing only permits one controller

I created a new Web API project and created the following routing spec (actually I have simplified, looking for the bug):
// Web API configuration and services
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{action}/{id}"
);
When I make a call via ajax with the route:
/api/account/GetSUID/0
everything works fine. When I just use a different controller:
/api/tile/GetTileSet/0
it returns a status of 200 but instead of hitting my controller, it just returns the contents of the default page in jqXHR.responseText! It is as if it is just skipping any API routing like I am requesting the default site page.
I am baffled by this one as I have written literally hundreds of web API functions over the past few years in several other projects. I have never had any issue making calls to multiple controllers. I have looked high and low for what could be happening here and am hoping that someone here might have an idea.
Here is a sample method on the controller:
[HttpGet]
public HttpResponseMessage CheckRequestedID(int id, [FromUri]string Search)
{
if (!BSDIUtil.HasAllAcceptableCharacters(Search))
return Request.CreateResponse(HttpStatusCode.BadRequest);
if (FolderModel.IDAlreadyExists(DAL, Search)) // We can check this because this function is only called when staff members are creating accounts for other people (participants always use their email).
return Request.CreateResponse(HttpStatusCode.OK, false);
else
return Request.CreateResponse(HttpStatusCode.OK, true);
}
This will work if on the account controller but not on the tile controller.
One other thing, I am using the "Community" edition of Visual Studio and Windows 8.1
This is not a problem that is likely to occur often but I have solved it and figured I would post it here in case anyone else has the same issue.
I am using web api in the context of a standard web forms app (although I am only using webforms for my reporting pages). In the web.config for a web forms app, you can declare the paths that the user has access to before authenticating. I was only providing access to the account controller: all others were not permitted due to my authentication mechanism. Once I authenticate (e.g. the forms authentication call) or if I change the location path to include only "api", the problem goes away.
I was facing same problem but in different context. Had many controllers and respective routing templates. Only one controller was responding to requests.
Later i realized my other controller classes were not public!!

Microsoft Unity - How to register connectionstring as a parameter to repository constructor when it can vary by client?

I am relatively new to IoC containers so I apologize in advance for my ignorance.
My application is a asp.net 4.0 MVC app that uses the Entity Framework with a Repository layer on top of that. It is a multi tenant application so the connection string that is used varies by the logged in client.
The connection string is determined by a 'key' that gets passed in as part of the route which indicates the client. This route data is only present on the first request of the user's session.
The route looks kind of like this: http://{host}/login/dev/
where 'dev' indicates we are using the dev database.
Currently the IoC container is registering all dependencies in the global.asax Application_Start event handler and I have the 'key' hardcoded as follows:
var cnString = CommonServices.GetDBConnection("dev");
container.RegisterType<IRequestMgmtRecipientRepository, RequestMgmtRecipientRepository>(
new InjectionConstructor(cnString));
Is there a way with Unity to dynamically register the repository based on the logged in client using the route data that is supplied initially?
Note: I am not manually resolving the repositories. They are getting constructed by the container when the controllers get instantiated.
I am stumped.
Thanks!
Quick assumption, you can use the host to identify your tenant.
the following article has a slightly different approach http://www.agileatwork.com/bolt-on-multi-tenancy-in-asp-net-mvc-with-unity-and-nhibernate-part-ii-commingled-data/, its using NH, but it is usable.
based on the above this hacked code may work (not tried/complied the following, not much of a unity user, more of a windsor person :) )
Container.RegisterType<IRequestMgmtRecipientRepository, RequestMgmtRecipientRepository>(new InjectionFactory(c =>
{
//the following you can get via a static class
//HttpContext.Current.Request.Url.Host, if i remember correctly
var context = c.Resolve<HttpContextBase>();
var host = context.Request.Headers["Host"] ?? context.Request.Url.Host;
var connStr = CommonServices.GetDBConnection("dev_" + host); //assumed
return new RequestMgmtRecipientRepository(connStr);
}));
Scenario 2 (i do not think this was the case)
if the client identifies the Tenant (not the host, ie http: //host1), this suggests you would already need access to a database to access the client information? in this case the database which holds client information, will also need to have enough information to identify the tenant.
the issue with senario 2 will arise around anon uses, which tenant is being accessed.
assuming senario 2, then the InjectionFactory should still work.
hope this helps

Global request/response interceptor

What would be the easiest way to setup a request/response interceptor in ServiceStack that would execute for a particular service?
A request filter (IHasRequestFilter) works fine but a response filter (IHasResponseFilter) is not triggered if the service returns non 2xx status code. I need to retrieve the status code returned by the method as well as the response DTO (if any).
A custom ServiceRunner and overriding the OnBeforeExecute and OnAfterExecute methods seems to work fine but I find it pretty intrusive as the service runner need to be replaced for the entire application and I couldn't find a way clean way to isolate per functionality the tasks that need to be executed in those methods.
Is there some extension point in ServiceStack that I am missing that would allow me to execute some code before each service method and after each service method? A plugin would be ideal but how can I subscribe to some fictitious BeforeExecute and AfterExecute methods that would allow me to run some custom code?
UPDATE:
Just after posting the question I found out that global response filters are executed no matter what status code is returned by the service which is exactly what I needed. So one last question: Is it possible to retrieve the service type that will handle the request in a request filter? I need to check whether this service is decorated by some custom marker attribute.
I have found out a solution to my question about how to retrieve the service type in a custom request/response filter:
appHost.RequestFilters.Add((req, res, requestDto) =>
{
var metadata = EndpointHost.Metadata;
Type serviceType = metadata.GetServiceTypeByRequest(requestDto.GetType());
...
}
A custom ServiceRunner and overriding the OnBeforeExecute and OnAfterExecute methods seems to work fine but I find it pretty intrusive as the service runner need to be replaced for the entire application
Quick note, you can opt-in and choose only what requests should use a custom service runner, e.g:
public override IServiceRunner<TRequest> CreateServiceRunner<TRequest>(
ActionContext actionContext)
{
return useCustomRunner(actionContext.RequestType)
? new MyServiceRunner<TRequest>(this, actionContext)
: base.CreateServiceRunner<TRequest>(actionContext);
}
IHttpRequest has OperationName. I think thats what you are after.

This controller won't show up in Web Api on Azure

namespace StorageRoleMVC4.Controllers
{
public class SearchController : ApiController
{
public Dictionary<string, string> Get([FromUri] string searchString, [FromUri] string searchObject)
{
var searchHelper = new SearchStorageHelper();
var objectList = searchHelper.Retrieve(searchString, searchObject);
return objectList;
}
}
}
Is there anything about this controller that makes it unreachable once it's deployed (to an Azure web role)? I just get a 404 error when I try to reach it. It works great on the local emulator.
The last 2 times I've deployed my project, all the controllers in my web service have returned 404 errors for several hours, until the project seems to fix itself. I'm not sure why, but it might be related.
UPDATE
There is a WARNING in the event log on the web role VM after I publish:
The application '/' belonging to site '1273337584' has an invalid AppPoolId 'ea7a2e15-9390-49e1-a16b-67ff1cdb7dcb' set. Therefore, the application will be ignored.
This is the id of my site, but the AppPoolId is not correct. Changing the app pool turns the 404 into a 502.
Also, after publishing, the World Wide Web Publishing Service is turned off. When I turn it on and do an IIS reset, after the reset it's turned off again.
When I reboot the web role VM, most of the controllers work again, and the World Wide Web Publishing Service is turned on. But still, this SearchController doesn't work. Or any other new controllers I've created since this problem started happening.
Well, after a ridiculous amount of unsuccessful troubleshooting, I just rolled back to an earlier version of the code and found that it didn't read the web service when I deployed it. So I started over with that version and re-built the delta.
This involved removing a few web.config entries and removing some libraries, creating a few classes and referencing them in the global.asax (I think that's where it was) in order to override te Authorize attribute...
If anyone has a better answer, I will switch the answer to what you post.

Resources