Use ISAPI filter to trace and time a WCF call? - wcf

I'm building a web application using WCF that will be consumed by other applications as a service. Our app will be installed on a farm of web services and load balanced for scalability purposes. Occasionally we run into problems specific to one web server and we'd like to be able to determine from the response which web server the request was processed by and possibly timing information as well. For example, this request was processed by WebServer01 and the request took 200ms to finish.
The first solution that came to mind was to build an ISAPI filter to add an HTTP header that stores this information in the response. This strikes me as the kind of thing somebody must have done before. Is there a better way to do this or an off-the-shelf ISAPI filter that I can use for this?
Thanks in advance

WCF offers much nicer extension points than ISAPI filters. You could e.g. create a client side message inspector that gets called just before the message goes out to the server, and then also gets called when the response comes back, and thus you could fairly easily measure the time needed for a service call from a client perspective.
Check out the IClientMessageInspector interface - that might be what you're looking for. Also see this excellent blog entry on how to use this interface.
Marc

I don't have a ready-made solution for you but I can point you towards IHttpModule.
See code in Instrument and Monitor Your ASP.NET Apps Using WMI and MOM 2005 for example.
private DateTime startTime;
private void context_BeginRequest(object sender, EventArgs e)
{
startTime = DateTime.Now;
}
private void context_EndRequest(object sender, EventArgs e)
{
// Increment corresponding counter
string ipAddress = HttpContext.Current.Request.
ServerVariables["REMOTE_ADDR"];
if (HttpContext.Current.Request.IsAuthenticated)
authenticatedUsers.Add(ipAddress);
else
anonymousUsers.Add(ipAddress);
// Fire excessively long request event if necessary
int duration = (int) DateTime.Now.Subtract(
startTime).TotalMilliseconds;
if (duration > excessivelyLongRequestThreshold)
{
string requestPath=HttpContext.Current.Request.Path;
new AspNetExcessivelyLongRequestEvent(applicationName,
duration,requestPath).Fire();
}
}

Related

WCF Service Hosting technique

We have a WCF service called service1 hosted in IIS.
We are creating another WCF service, say, service2, which will always be running in the background and will monitor if a file, say, X, is having enough data that has to be consumed by Service1.
If the file X is not having enough data then service2 will call another component which will load the data to the file.
So please suggest a hosting technique for service2 which fulfils all the above requirements and should be independent, i.e if the service2 is down, it should not impact service1 or vice versa. Both these services are a part of the same app-domain.
We have one scenario where we hosted the same as a window service. Now we are looking to try something else.
Please provide your valuable suggestions.
You're looking for the right kind of screwdriver to hammer a nail. 8-)
WCF services run on demand, based on network activity (a request comes in, the service runs and handles the request). However after a defined period with no activity the service shuts down and the resources are released, and the server waits for the next request, however the existence of a file containing the data you want does not create this type of request.
What you need is a Windows Service (the things that you see in the Services Control Panel). These run continuously and are appropriate for tasks that don't produce network requests, like monitoring to see if you have the data you need.
We have one scenario where we hosted the same as a window service. Now we are looking to try something else.
There really isn't anything else suitable for unattended operation. Windows Services are designed exactly for this type of task, while WCF Services aren't.
Apart from your hosting requirement you should also consider using a FileSystemWatcher if you haven't done so already. You would obviously have to add your own logic to meet your requirements.
Basic example:
protected override void OnStart(string[] args)
{
FileSystemWatcher Watcher = new FileSystemWatcher("some file path");
Watcher.EnableRaisingEvents = true;
Watcher.Changed += new FileSystemEventHandler(Watcher_Changed);
}
// This event is raised when a file is changed
private void Watcher_Changed(object sender, FileSystemEventArgs e)
{
// your code here
}
More complete example:
http://www.rhyous.com/2012/11/27/c-creating-a-service-to-monitor-a-directory/

Using TAP progress reporting in a WCF service

I (new to WCF) am writing a WCF service that acquires and analyzes an X-ray spectrum - i.e. it is a long-running process, sometimes several minutes. Naturally, this begs for asynchronous calls so, using wsDualHttpBinding and defining the following in my ServiceContract
[ServiceContract(Namespace="--removed--",
SessionMode=SessionMode.Required, CallbackContract=typeof(IAnalysisSubscriber))]
public interface IAnalysisController
{
// Simplified - removed other declarations for clarity
[OperationContract]
Task<Measurement> StartMeasurement(MeasurementRequest request);
}
And the (simplified) implementation has
async public Task<Measurement> StartMeasurement(MeasurementRequest request)
{
m_meas = m_config.GetMeasurement(request);
Spectrum sp = await m_mca.Acquire(m_meas.AcquisitionTime, null);
UpdateSpectrum(m_meas, sp);
return m_meas;
}
private void McaProgress(Spectrum sp)
{
m_client.ReportProgress(sp);
}
Where m_client is the callback object obtained from m_client = OperationContext.Current.GetCallbackChannel(); in the "Connect" method - called when the WCF client first connects. This works as long as I don't use progress reporting, but as soon as I add progress reporting by changing the "null" in the m_mca.Acquire() method to "new Progress(McaProgress)", on the first progress report, the client generates an error "The server did not provide a meaningful reply; this might be caused by a contract mismatch..."
I understand the client is probably awaiting a particular reply of a Task rather than having a callback made into it, but how do I implement this type of progress reporting with WCF? I would like the client to be able to see the live spectrum as it is generated and get an estimate of the time remaining to complete the spectrum acquisition. Any help or pointers to where someone has implemented this type of progress reporting with WCF is much appreciated (I've been searching but find mostly references to EAP or APM and WCF and not much related to TAP).
Thanks, Dave
Progress<T> wasn't really meant for use in WCF. It was designed for UI apps, and may behave oddly depending on your host (e.g., ASP.NET vs self-hosted).
I would recommend writing a simple IProgress<T> implementation that just called IAnalysisSubscriber.ReportProgress directly. Also make sure that IAnalysisSubscriber.ReportProgress has OperationContract.IsOneWay set to true.

WCF web service, Entity Framework and keeping SQL sessions alive (slow first start!)

I have a WCF web service hosted on IIS 7. This web service serves up JSON content for use in mobile apps. It uses the Entity Framework on top of MS SQL 2005, and the Interface contract looks like the below:
[ServiceContract]
public interface MyService
{
[WebInvoke(Method = "GET", UriTemplate = "/GetStuff?skip={skip}&take={take}&loanAmt={loanAmt}&propertyVal={propertyVal}&Term={Term}&MonthlyRent={MonthlyRent}", RequestFormat = WebMessageFormat.Json, ResponseFormat = WebMessageFormat.Json)]
[OperationContract]
ProductsDTO GetProducts(int skip, int take, decimal loanAmt, decimal propertyVal, int Term, decimal MonthlyRent);
}
The implementation looks like this:
public ProductsDTO GetProducts(int skip, int take, decimal loanAmt, decimal propertyVal, int Term, decimal MonthlyRent)
{
//Some set up code
using (MyEntities context = new MyEntities())
{
//Get our products
}
return ReturnedList
}
On the first run of this, it can take anywhere up to 15 seconds (unacceptable for a mobile app), on subsequent runs, the data comes back in under a second. After 5 minutes of inactivity, the WCF service reverts to taking 15 seconds to start.
I initially thought the bottle neck was at IIS7 and thought my App Pool was shutting off. After setting the App Pool to never recycle and studying w3wp.exe processes on the IIS server, I realized this was not the case. It is the database session that is shutting off after those five minutes.
This being the case, I want to hold a SQL session open to immediately serve up requests from the WCF application, however, I don't want to set the Entity Context as a singleton or leave this open in the service as I understand this is poor practise. I could pass a SQL connection object TO the context (using (MyEntities context = new MyEntities(MySQLConnection))) and hold that open? Or can someone suggest something else?
I had seen lots of posts with scripts that touch the web service to keep it alive which gave me the creeping horrors, so have avoided going down this route.
What are your thoughts?
Update 1
As per Andomars response, I have added the following initialization code to the WCF service.
public Service1()
{
// Blocking call that initializes
// the service instance
this.Initialize();
}
BackgroundWorker KeepSQLAlive = new BackgroundWorker();
private void Initialize()
{
KeepSQLAlive.DoWork += new DoWorkEventHandler(KeepSQLAlive_DoWork);
KeepSQLAlive.RunWorkerAsync();
}
void KeepSQLAlive_DoWork(object sender, DoWorkEventArgs e)
{
Timer pingback = new Timer(180000);
pingback.Elapsed += new ElapsedEventHandler(pingback_Elapsed);
pingback.Start();
}
void pingback_Elapsed(object sender, ElapsedEventArgs e)
{
using (MyEntities context = new MyEntities ())
{
context.ExecuteStoreCommand("select ##servername");
}
}
This feels like a bit of a fudge, and I'm a bit worried without creating the service as a singleton the service will continue to spawn SQL sessions without ever killing any. However, if it works it is the path of least resistance as hosting this code in a windows service would take more time and I am unclear how to integrate this in with IIS security (I want to use SSL). I will report back whether the above works. (thanks for the assistance all)
You could add a background thread that runs select ##servername (or another trivial query) every minute. That should keep the connection pool warm.
Web service shouldnt run background threads so..
Write Windows Service with WCF contract as a proxy between IIS and Database.
Make sure you are using connection pooling on your Windows Service.
Query something from DB every 30 seconds/ 1 minute / 5 minutes (this time have to be tested) from your windows service. After query remember to close your connection (this will not close real connection but will make connection available for the pool). This will keep at least one active connection in pool ready for your request.
Use pipes (NetNamedPipeBinding) between WCF service on IIS and Windows Service (it's fast).
Think about publishing already compiled application on your IIS. link

WCF Comet Implementation

I have a requirement that needs a real-time updates on the Web client (ASP.NET MVC). The only way I can turn around on it is that to implement the COMET technique (ServerPush/Reverse-AJAX) technique.
The scenario is that:
User A save a message in different website client. Then, User B will automatically get the updates made by User "A" in different browser.
I actually finish the solution by this Architecture:
ASP.NET MVC - did a jquery ajax (post) request (long-pooled) on the WCF.
WCF - do some polling on the database (SQL Server) with the interval of 1 second. If new data has been added to the database, the polling is break with the data being returned on the client.
WCF COMET Method Pseudo Code:
private Message[] GetMessages(System.Guid userID)
{
var messages = new List<Message>();
var found = false;
/* declare connection, command */
while (!found )
{
try
{
/* open connection - connection.Open(); */
/* do some database access here */
/* close connection - connection.Close(); */
/* if returned record > 0 then process the Message and save to messages variable */
/* sleep thread : System.Threading.Thread.Sleep(1000); */
found = true;
}
finally
{
/* for sure, incase of exception */
/* close connection - connection.Close(); */
}
}
return messages.ToArray();
}
My concern and question is: Is it the best approach to do the polling technique in WCF (with 1 second interval)?
Reason: I maximized the use of database connection pooling and I am expecting that there is no issue on that technique.
Note: This is a multi-threaded implementation with the use of WCF given attributes below.
[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall), ConcurrencyMode = ConcurrencyMode.Multiple, UseSynchronizationContext = true)]
I'd recommend using a dedicated realtime server (i.e. don't host the WCF service in IIS) or using a hosted service for realtime updates. As Anders says, IIS isn't all that great at handling multiple long-running concurrent requests.
I'd also suggest you look at using a solution which uses WebSockets with support for fallback solutions such as Flash, HTTP streaming, HTTP long-polling and then possibly polling. WebSockets are the first standardised method of full duplex bi-directional communication between a client and a server and will ultimately deliver a better solution to any such problems.
For the moment implementing your own Comet/WebSockets realtime solution is definitely going to be a time consuming task (as you may have already found), especially when building a public facing app where it could be accessed by users with a multitude of different browsers.
With this in mind the XSockets project looks very interesting as does the SuperWebSocket project.
.NET Comet solutions I know of are from FrozenMountain have a WebSync server for IIS. There is also PokeIn.
I've compiled a list of realtime web technologies that may also be useful.
nstead of polling the database cant you have an event sent when updating instead? Thats the way I've implemented Pub/Sub scenarios anyway and it works great.

Type 'System.Web.HttpRequest' cannot be serialized

I am trying to design an Picture Upload feature into a web site.
I am using ASP.NET 3.5, C#, and WCF.
I have been asked to accomplish the following:
1) Make the Uploader a Web Service
2) Return progress updates to the user as files are uploaded.
3) Log other relevant user-selected options in the database.
So, I have started off by creating a WCF web client with the
below service contract:
IService.UploadPictures(HttpRequest request);
private UploadServiceClient upload;
protected void Page_Load(object sender, EventArgs e)
{
upload = new UploadServiceClient();
upload.UploadPictures(Request.Files);
}
When I compile, I get the below error:
Type 'System.Web.HttpRequest' cannot
be serialized. Consider marking it
with the DataContractAttribute, and
marking all of its members you want
serialized with the
DataMemberAttribute attribute.
So, I went back into my service contract and
changed [OperationContract] to [DataContract]
but the change produced the same error.
Can somebody kindly tell me what I am doing wrong
and provide examples as to how to best move forward?
Thanks for your time.
You cannot use something like a HttpRequest as a WCF parameter. The error messages says it all - the HttpRequest is not serializable, and in order to work with WCF, types have to be serializable.
Also, you need to remember: you're not just passing an object instance to a method here - what you're really doing is having the WCF runtime serialize your request (the method name to call plus all the parameters passed in) into a message (think: e-mail or xml file), sending it to the server, deserialising there and building up a new copy of the given datatype (as defined in your DataContract), and doing something with it.
Your WCF service could well be self-hosted, e.g. running in a NT Service or console app - no HttpRequest available in those circumstances!
You need to definitely rearchitect your solution - you need to either check into WCF streaming to upload files to WCF (google for it - you'll find plenty of hits) or you'll need to find another way to pass the relevant info (e.g. list of filenames) to the WCF service without use of a HttpRequest object.
Marc
You are submitting a request as a parameter to a request. This is not what you want to do. I'm assuming that "Request.Files" is an array of files. This is what you want to upload. So something like:
IService.UploadPictures(List<SomeFileType> request);