C# how can I check that is asp.net webapi running - c#

I have an asp.net webapi which sometimes crashes, or it does not give a response. I am trying to solve this problem. However, I temporarily reset the IIS until the problem is solved.
My IIS reset console application;
class Program
{
static void Main(string[] args)
{
while (true)
{
Thread.Sleep(500);
if (CheckConnection() == false)
{
System.Diagnostics.Process.Start(#"C:\Windows\System32\iisreset.exe");
Console.WriteLine("Restarting");
Thread.Sleep(30000);
}
}
}
public static bool CheckConnection(string webApiAddress = null)
{
try
{
using (var client = new WebClient())
{
using (var stream = client.OpenRead("http://localhost:8885/"))
{
return true;
}
}
}
catch
{
return false;
}
}
}
I check per 500 milliseconds. Sometimes my reset application, resets the IIS, probably my asp.net web api crashing. But I am not sure about WebClient.OpenRead() method is best check method.
Thank you for best practice

Related

Only one usage of each socket address (protocol/network address/port) is normally permitted when AppPool is recycled

I have a .net Core application hosted on IIS.
The application initializes WebSocketServer, by adding a wrapper class as a HostedService.
The hosted service execute the Start() method seen below on its StartAsync() and the Destroy() method on its StopAsync() method.
The problem that whenever the related application pool is recycled - the error in the title appears ("Only one usage of each socket address (protocol/network address/port) is normally permitted"). It happens because there is a new working process that starts-up and trying to use the ports of the socket before there is a shutdown of the current Working process which still use the socket with the same ports.
I'm trying to find a way of avoiding that situation, programmatically or by configuration.
Here is my code of the HostedService:
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
using System;
using System.Diagnostics;
using System.Linq;
using System.Net.WebSockets;
using WebSocketSharp.Server;
namespace XXXX
{
public class WebsocketServerWrapper
{
// Thats an websocket server , he will host our service as web socket service.
private static WebSocketServer wss;
private static readonly object LockObject = new object();
public static IConfiguration Config { get; set; }
public static bool IsCreated()
{
return wss != null;
}
public static bool HasChannels()
{
if(IsCreated())
{
return wss.WebSocketServices.Paths.Any();
}
return false;
}
public static bool IsListening()
{
if (wss != null)
{
return wss.IsListening;
}
return false;
}
public static void AddChannel(string channel)
{
if (wss.WebSocketServices.Paths.Any((path) => path == channel) == false)
{
wss.AddWebSocketService(channel,() => new <class that treats the socket behaviour itself>(Config));
}
}
public static bool Start()
{
wss.Start();
if (IsListening())
{
return true;
}
else
{
return false;
}
}
public static bool Destroy()
{
var isDestroyed = false;
if(IsCreated())
{
removeWebSocketServices();
wss.Stop();
wss = null;
isDestroyed = true;
}
return isDestroyed;
}
internal static void removeWebSocketServices()
{
var channels = wss?.WebSocketServices?.Paths?.ToList() ?? null;
if (channels != null)
{
channels.ForEach(channel => { wss.RemoveWebSocketService(channel); channel = null; });
}
}
public static bool CreateWSSServer()
{
bool isCreationSucceeded = false;
if(!IsCreated())
{
lock(LockObject)
{
try
{
var wssUrl = Config.GetValue<string>(<WebsocketServerUrl adress>);
wss = new WebSocketServer($"ws://{wssUrl}");
isCreationSucceeded = true;
}
catch (WebSocketException wse)
{
Debug.WriteLine(wse);
return isCreationSucceeded;
}
catch (Exception e)
{
Debug.WriteLine(e);
return isCreationSucceeded;
}
}
}
isCreationSucceeded = true;
return isCreationSucceeded;
}
}
}
I found a solution to that scenario (edited it in the question) - it turns out that the scenario of the server starts-up with new worker process before the "old" worker process shutdown is not rare - there is a parameter in the application pool under Recycling => Disable Overlapped Recycle - if it set to true (it is false by default), then the startup with new Working process will occur only after the current worker process will shut-down

How to schedule a job using FluentScheduler library with Web Api?

I am unable to get FluentScheduler working in .Net Framework 4.5.2 Web api. Few days ago, I asked a similar question about scheduling through Console application and could get it to work with help but unfortunately facing issues with Web Api now. Below is the code.
[HttpPost]
[Route("Schedule")]
public IHttpActionResult Schedule([FromBody] SchedulerModel schedulerModel)
{
var registry = new Registry();
registry.Schedule<MyJob>().ToRunNow();
JobManager.Initialize(registry);
JobManager.StopAndBlock();
return Json(new { success = true, message = "Scheduled!" });
}
Below is the job I want to schedule which for now is just writing text to a file
public class SampleJob: IJob, IRegisteredObject
{
private readonly object _lock = new object();
private bool _shuttingDown;
public SampleJob()
{
HostingEnvironment.RegisterObject(this);
}
public void Execute()
{
lock (_lock)
{
if (_shuttingDown)
return;
//Schedule writing to a text file
WriteToFile();
}
}
public void WriteToFile()
{
string text = "Random text";
File.WriteAllText(#"C:\Users\Public\TestFolder\WriteText.txt", text);
}
public void Stop(bool immediate)
{
lock (_lock)
{
_shuttingDown = true;
}
HostingEnvironment.UnregisterObject(this);
}
Got this resolved finally. It turns out the issue was with my Registry class. I had to change it as follows.
public class ScheduledJobRegistry: Registry
{
public ScheduledJobRegistry(DateTime appointment)
{
//Removed the following line and replaced with next two lines
//Schedule<SampleJob>().ToRunOnceIn(5).Seconds();
IJob job = new SampleJob();
JobManager.AddJob(job, s => s.ToRunOnceIn(5).Seconds());
}
}
[HttpPost]
[Route("Schedule")]
public IHttpActionResult Schedule([FromBody] SchedulerModel schedulerModel)
{
JobManager.Initialize(new ScheduledJobRegistry());
JobManager.StopAndBlock();
return Json(new { success = true, message = "Scheduled!" });
}
Another point to note: I could get this to work but hosting Api in IIS makes it tricky because we have to deal with App Pool recycles, idle time etc. But this looks like a good start.

PushSharp Separation of Concerns

I'm currently working on a C# web application and I'm trying to get push notifications to work using the PushSharp package. I have all of my code for pushing notifications in the Global.asax file in my project, but I keep getting the error:
The collection has been marked as complete with regards to additions.
Here is my Global.asax file:
using BYC.Models;
using BYC.Models.Enums;
using Newtonsoft.Json.Linq;
using PushSharp.Apple;
using PushSharp.Google;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Cryptography.X509Certificates;
using System.Web.Http;
using System.Web.Mvc;
using System.Web.Optimization;
using System.Web.Routing;
namespace BYC
{
public class WebApiApplication : System.Web.HttpApplication
{
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
GlobalConfiguration.Configure(WebApiConfig.Register);
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
}
protected void Application_End()
{
PushBrokerSingleton pbs = new PushBrokerSingleton();
pbs.SendQueuedNotifications();
}
}
public sealed class PushBrokerSingleton
{
private static ApnsServiceBroker Apns { get; set; }
private static GcmServiceBroker Gcm { get; set; }
private static bool ApnsStarted = false;
private static bool GcmStarted = false;
private static object AppleSyncVar = new object();
private static object GcmSyncVar = new object();
private static readonly log4net.ILog log = log4net.LogManager.GetLogger
(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
public PushBrokerSingleton()
{
if (Apns == null)
{
string thumbprint = (AppSettings.Instance["APNS:Thumbprint"]);
X509Store store = new X509Store(StoreName.My, StoreLocation.CurrentUser);
store.Open(OpenFlags.OpenExistingOnly | OpenFlags.ReadOnly);
ApnsConfiguration.ApnsServerEnvironment production = Convert.ToBoolean(AppSettings.Instance["APNS:Production"]) ?
ApnsConfiguration.ApnsServerEnvironment.Production : ApnsConfiguration.ApnsServerEnvironment.Sandbox;
X509Certificate2 appleCert = store.Certificates
.Cast<X509Certificate2>()
.SingleOrDefault(c => string.Equals(c.Thumbprint, thumbprint, StringComparison.OrdinalIgnoreCase));
ApnsConfiguration apnsConfig = new ApnsConfiguration(production, appleCert);
Apns = new ApnsServiceBroker(apnsConfig);
Apns.OnNotificationFailed += (notification, aggregateEx) => {
aggregateEx.Handle(ex => {
// See what kind of exception it was to further diagnose
if (ex is ApnsNotificationException)
{
var notificationException = ex as ApnsNotificationException;
// Deal with the failed notification
var apnsNotification = notificationException.Notification;
var statusCode = notificationException.ErrorStatusCode;
log.Error($"Notification Failed: ID={apnsNotification.Identifier}, Code={statusCode}");
}
else {
// Inner exception might hold more useful information like an ApnsConnectionException
log.Error($"Notification Failed for some (Unknown Reason) : {ex.InnerException}");
}
// Mark it as handled
return true;
});
};
Apns.OnNotificationSucceeded += (notification) => {
log.Info("Notification Successfully Sent to: " + notification.DeviceToken);
};
}
if(Gcm == null)
{
GcmConfiguration gcmConfig = new GcmConfiguration(AppSettings.Instance["GCM:Token"]);
Gcm = new GcmServiceBroker(gcmConfig);
}
}
public bool QueueNotification(Notification notification, Device device)
{
if (!ApnsStarted)
{
ApnsStarted = true;
lock (AppleSyncVar)
{
Apns.Start();
}
}
if(!GcmStarted)
{
GcmStarted = true;
lock (GcmSyncVar)
{
Gcm.Start();
}
}
switch (device.PlatformType)
{
case PlatformType.iOS:
return QueueApplePushNotification(notification, device.PushRegistrationToken);
case PlatformType.Android:
return QueueAndroidPushNotification(notification, device.PushRegistrationToken);
default: return false;
}
}
private bool QueueApplePushNotification(Notification notification, string pushNotificationToken)
{
string appleJsonFormat = "{\"aps\": {\"alert\":" + '"' + notification.Subject + '"' + ",\"sound\": \"default\", \"badge\": " + notification.BadgeNumber + "}}";
lock (AppleSyncVar)
{
Apns.QueueNotification(new ApnsNotification()
{
DeviceToken = pushNotificationToken,
Payload = JObject.Parse(appleJsonFormat)
});
}
return true;
}
private bool QueueAndroidPushNotification(Notification notification, string pushNotificationToken)
{
string message = "{\"alert\":\"" + notification.Subject + "\",\"badge\":" + notification.BadgeNumber + "\"}";
lock (GcmSyncVar)
{
Gcm.QueueNotification(new GcmNotification()
{
RegistrationIds = new List<string>
{
pushNotificationToken
},
Data = JObject.Parse(message),
Notification = JObject.Parse(message)
});
}
return true;
}
public void SendQueuedNotifications()
{
if(Apns != null)
{
if (ApnsStarted)
{
lock(AppleSyncVar){
Apns.Stop();
log.Info("Sent Apns Notifications");
ApnsStarted = false;
}
}
}
if(Gcm != null)
{
if (GcmStarted)
{
lock (GcmSyncVar)
{
Gcm.Stop();
log.Info("Sent Gcm Notifications");
GcmStarted = false;
}
}
}
}
}
}
That happens when you try and reuse an instance of a service broker (eg: ApnsServiceBroker) which Stop() has been called on.
I'm guessing your Application_End is getting called at some point and Application_Start gets called again, but since PushBrokerSingleton.Apns is not null (it's a static field so it must live on even though the Application has stopped/started), it never gets recreated.
PushSharp is a hard thing to make work nicely with the ASP.NET pattern, some sort of service daemon would be better.
The main issue is that your app may be recycled or ended when you don't expect it to. Unrelated requests in the same app can take down your process, or your AppDomain may be torn down. If this happens and the brokers' Stop() calls can't end successfully, some queued messages could be lost. Here's a great article on some of the caveats: http://haacked.com/archive/2011/10/16/the-dangers-of-implementing-recurring-background-tasks-in-asp-net.aspx/ In practice, this may not be a big deal, and you can certainly mitigate parts of it, but keep it in mind.
Having said all that, I think a simple fix would be to create a new instance of PushBrokerSingleton.Apns and PushBrokerSingleton.Gcm in your Application_Start. This may cause other issues for you so I'm not sure if it's the right fix, but it will work around the issue that the broker is not meant to be reused after Stop() has been called.
I'm also going to consider adding some way to 'reset' the collection. I'm not sure if doing this automatically after .Stop() ends is a good idea, but I may look at adding a .Reset() or similar kind of method to achieve this. In any case, creating a new broker instance is perfectly acceptable for now.

Detecting MSMQ errors

I have this following code (verbatim) that I expect to generate a error:
using System.Messaging;
namespace MsmqTest
{
class Program
{
static void Main()
{
string invalidQueue = #"FormatName:DIRECT=OS:sometrahsname\private$\anothertrahsname";
Enqueue("test",invalidQueue);
}
private static void Enqueue(object o, string queueName)
{
using (MessageQueue msmq = new MessageQueue(queueName))
using (MessageQueueTransaction transaction = new MessageQueueTransaction())
{
msmq.DefaultPropertiesToSend.Recoverable = true;
transaction.Begin();
msmq.Send(new Message(o), transaction);
transaction.Commit();
}
}
}
}
Here we are sending a message to a queue on a server that does not exist. I expect to receive an indication that something went wrong. I do not get any. How do I check for error in this scenario?
Note: in order to run the code above you need to have MSMQ installed on your machine.
I stumbled about this problem before in one of my applications. MSDN documented that in MessageQueue.Send, the message might be sent to the dead-letter queue without throwing an exception. And that is what you are experiencing right now. What I did is to check if the queue exists.
using System;
using System.Messaging;
namespace MsmqTest
{
class Program
{
static void Main(string[] args)
{
string invalidQueue = #"FormatName:DIRECT=OS:sometrahsname\private$\anothertrahsname";
Enqueue("test", invalidQueue);
}
private static void Enqueue(object o, string queueName)
{
try
{
MessageQueue msmq = null;
//check if queueName exists
//it also validates if you have access to MSMQ server
if (!MessageQueue.Exists(queueName))
{
msmq = MessageQueue.Create(queueName);
//you can also set the permission here
//because the other application that may be reading
//has different account with the application that created the queue
//set to Everyone for demonstration purposes
msmq.SetPermissions("Everyone", MessageQueueAccessRights.FullControl);
}
else
{
msmq = new MessageQueue(queueName);
}
using (msmq)
{
using (MessageQueueTransaction transaction = new MessageQueueTransaction())
{
msmq.DefaultPropertiesToSend.Recoverable = true;
transaction.Begin();
msmq.Send(new Message(o), transaction);
transaction.Commit();
}
}
}catch(Exception)
{
//handle error here
}
}
}
}

Unknown command error when using multithread to set redis

I am using the ServiceStack.Redis C# client to talk to Redis.
With few request everything is ok, but when I get LoadRunner to request it or use multi-threading to make requests, I get some errors that say I am using the wrong command.
I check the errors, and it seems that it cut off the command, or it mess up.
Here is my code, very simple. Has anyone come across this problem? The errors happen when I call the Push method using multi-threading.
public class ImpresstionQueueService : IQueueService<InsertImpressionRequest>
{
private string _queueName;
private string _host;
private static IRedisClient redisClient = new RedisClient(ConfigHost);
private static string ConfigHost
{
get
{
return ConfigurationManager.AppSettings.Get("redis_host");
}
}
private string Host
{
get
{
if (!string.IsNullOrEmpty(_host))
return _host;
else
{
return ConfigurationManager.AppSettings.Get("redis_host");
}
}
}
public ImpresstionQueueService(string queue_name)
{
this._queueName = queue_name;
}
public ImpresstionQueueService(string host, string queu_name)
{
this._queueName = queu_name;
this._host = host;
}
#region IQueueService<InsertImpressionRequest> Members
class testData
{
}
public int Push(InsertImpressionRequest value)
{
try
{
//using (var redisClient = new RedisClient(this.Host))
{
//ser
string ser_value = TypeSerializer.SerializeToString<InsertImpressionRequest>(value);
//push
redisClient.AddItemToList(this._queueName, ser_value);//here will be error
}
}
catch (Exception ex)
{
HLogger.GetLogger("RedisLogger").Error(ex.Message + ex.StackTrace);
}
//throw new NotImplementedException();
return 1;
}
public InsertImpressionRequest Pop()
{
InsertImpressionRequest request = null;
//using (var redisClient = new RedisClient(this.Host))
{
string pop_string_value = redisClient.PopItemFromList(this._queueName);
//deseri
if (pop_string_value != null)
{
request = TypeSerializer.DeserializeFromString<InsertImpressionRequest>(pop_string_value);
}
}
return request;
}
#endregion
}
You are probably using the same Redis connection simultaneously from multiple threads. Both threads could possibly send commands or wait for replies at the same time. When this happens, one thread receives data intended for the other thread. This causes your error.
If you use one Redis client per thread (instead of one client per ImpresstionQueueService), each thread can send commands at the same time without interfering with each other.
Alternatively, you can create a client just for the single request (which you commented out just above the error location). The disadvantage of this alternative is the overhead of a new connection every time (which might be large or small or unnoticeable).

Categories