HTTP Request/Response on Azure WebJob - azure

I'm looking to create a WebJob that takes in a request and sends a response, much like an Azure Function with an HTTP trigger. I want to use a WebJob instead because I need to use wkhtmltopdf, which cannot run on a Consumption plan, and we are already paying for an App Service that it can run on.
I know how to run the WebJob using an HTTP POST from this link: https://stackoverflow.com/a/42824776/443044.
What I cannot figure out is how to create the WebJob itself.
Here is my Program class:
public class Program
{
[NoAutomaticTrigger]
public static void TestMethod(TextWriter logger)
{
logger.WriteLine("TEST: " + req.Content.ToString());
}
// Please set the following connection strings in app.config for this WebJob to run:
// AzureWebJobsDashboard and AzureWebJobsStorage
static void Main()
{
var config = new JobHostConfiguration();
...
var host = new JobHost(config);
host.Call(typeof(Program).GetMethod("TestMethod"), null);
}
}
The program throws an exception if I try to give TestMethod the return type of HttpResponseMessage or a parameter of type HttpRequestMessage.
How can I achieve the request/response functionality like with an Azure Function?

we are already paying for an App Service -> You do realize you can host your azure function on an existing app plan as well? learn.microsoft.com/en-us/azure/azure-functions/….
But AFAIK webjobs do not have capabilities to respond to requests.

Related

Is there a way to change the Azure function trigger type programmatically.

Looks like function.json in your Azure function's zip file defines Az func's trigger. Is it possible to change the trigger from Timer based to Event based programatically? Meaning, can I have a python application running perhaps in my local computer which calls Azure python sdk and says hey, change the Azure Function x's trigger from timer based to event hub based trigger and here is the additional event hub namespace name and connection string information that you need.
This is not possible.
However, I can think of the following workaround, which should accomplish the goal.
First, you can define multiple function triggers within one function project (one function app service). They could be bound to different targets. Something like this:
public static class Functions
{
[FunctionName("FunctionTimer")]
public static async Task RunAsync([TimerTrigger("%Schedule%")]TimerInfo myTimer, ILogger log)
{
if (!AppSettings.IsTimerTriggerActive)
return;
...
}
[FunctionName("FunctionEventHub")]
public static async Task RunAsync([EventHubTrigger("", Connection = "eventHubConnectionString")] EventData[] eventDataBatch, ILogger log)
{
if (!AppSettings.IsEventHubTriggerActive)
return;
...
}
}
You cannot enable/disable a function programmatically, but you can control which one of these is active via the App Service Application Settings. The latter could be managed through the Web App API: https://learn.microsoft.com/en-us/rest/api/appservice/webapps/updateapplicationsettings
Thus you can activate either trigger. One can even skip the (de)activation if that's compatible with the logic.

Azure Timer Trigger webjob: Function is being recognised but not being triggered

I have an Azure timer trigger webjob, deployed in two different environments with two different storage accounts. The webjobs have been deployed for about two months now, and were running as expected for quite some time. However, for about a week now, the function is being recognised but not being executed.
Here is how the logs look:
Found the following functions:
ADFSchedulerWebJob.Functions.ProcessTimerMessage
Job host started
Nothing happens after the Job host is started, i.e., the function ProcessTimerMessage is not called/executed. This is how the function looks:
public static void ProcessTimerMessage([TimerTrigger("0 */2 * * * *", RunOnStartup = true)] TimerInfo info, TextWriter log)
{
//function logic
}
This is how the Main method looks like:
static void Main()
{
JobHostConfiguration config = new JobHostConfiguration();
config.UseTimers();
var host = new JobHost(config);
host.RunAndBlock();
}
The same is occurring even when I try to debug the webjob locally. What could be the possible reason/resolution?
Any help appreciated.
What could be the possible reason/resolution?
As Thoms mentioned that it seems that the same storage account is used for another webjob or others.
We could debug it with following ways:
1.Try to use another storage account
2.Check the webjob log from the storage
We could get more info about Webjob log from the blog.

Azure Webjob Queue trigger not working

My queue trigger works only when the azure queue is being populated with messages and when simultaneously the webjob is running.(in my local dev environment)
But when i start the webjob (having the queue trigger) first , and then after a few seconds put new messages into the queue, the trigger doesn't detect them. Its like the trigger stops listening once there is no new messages. is this a normal behaviour for the trigger ? If not how do i resolve this issue ?
Main method :
static void Main()
{
InitializeQueue();
var config = new JobHostConfiguration();
if (config.IsDevelopment)
{
config.UseDevelopmentSettings();
}
var host = new JobHost();
// The following code ensures that the WebJob will be running continuously
host.RunAndBlock();
}
Function with queue trigger
public static void ProcessQueueMessage([QueueTrigger("myqueue")] CloudQueueMessage message)
{
Debug.Write(message.AsString);
}
For on-demand WebJobs, Azure has a configuration setting called WEBJOBS_IDLE_TIMEOUT that is denoted in seconds and defaults to two minutes.
If your on-demand WebJob isn’t triggered within the WEBJOBS_IDLE_TIMEOUT interval, Azure will kill your WebJob.
I recommend setting your Web App’s WEBJOBS_IDLE_TIMEOUT variable to a healthy number of seconds to prevent this from happening.
You can set this variable on the Web App’s Application Settings screen by adding it to the App Settings section of the Web App instance that is hosting your WebJob.

How To migrate windows service in Azure service fabric

I want to migrate typical windows service which is written in .net to Azure using Service fabric.
To implement this , I am creating one service fabric application containing one micro service as guest executable which uses .exe of windows service and deploying application package to service fabric cluster.
After deploying service fabric application on cluster I want windows service should install & start automatically on all nodes however at any time application is running on any single node. I want windows service should run on only one node at a time.
Please kindly help to implement this.
You can certainly run your service as a guest executable. Making sure it only runs on one node can be done by setting the instance count to 1 in the manifest, like so:
<Parameters>
<Parameter Name="GuestService_InstanceCount" DefaultValue="-1" />
</Parameters>
...
<DefaultServices>
<Service Name="GuestService">
<StatelessService ServiceTypeName="GuestServiceType"
InstanceCount="[GuestService_InstanceCount]">
<SingletonPartition />
</StatelessService>
</Service>
</DefaultServices>
Or, you could actually migrate it, not just re-host it in the SF environment...
If your Windows Service is written in .NET and the you wan't to benefit from Service Fabric then the job of migrating the code from a Windows Service to a Reliable Service in Service Fabric should not be to big.
Example for a basic service:
If you start by creating a Stateless Service in a Service Fabric application you end up with a service implementation that looks like (comments removed):
internal sealed class MigratedService : StatelessService
{
public MigratedService(StatelessServiceContext context)
: base(context)
{ }
protected override IEnumerable<ServiceInstanceListener> CreateServiceInstanceListeners()
{
return new ServiceInstanceListener[0];
}
protected override async Task RunAsync(CancellationToken cancellationToken)
{
// TODO: Replace the following sample code with your own logic
// or remove this RunAsync override if it's not needed in your service.
long iterations = 0;
while (true)
{
cancellationToken.ThrowIfCancellationRequested();
ServiceEventSource.Current.ServiceMessage(this.Context, "Working-{0}", ++iterations);
await Task.Delay(TimeSpan.FromSeconds(1), cancellationToken);
}
}
The RunAsync method starts running as soon as the Service is up and running on a node in the cluster. It will continue to run until the cluster, for some reason, decides to stop the service, or move it to another node.
In your Windows Service code you should have a method that is run on start. This is usually where you set up a Timer or similar to start doing something on a continuous basis:
protected override void OnStart(string[] args)
{
System.Timers.Timer timer = new System.Timers.Timer();
timer.Interval = 60000; // 60 seconds
timer.Elapsed += new System.Timers.ElapsedEventHandler(this.OnTimer);
timer.Start();
}
public void OnTimer(object sender, System.Timers.ElapsedEventArgs args)
{
...
DoServiceStuff();
Console.WriteLine("Windows Service says hello");
}
Now grab that code in OnTimer and put it in your RunAsync method (and any other code you need):
protected override async Task RunAsync(CancellationToken cancellationToken)
{
while (true)
{
cancellationToken.ThrowIfCancellationRequested();
DoServiceStuff();
ServiceEventSource.Current.ServiceMessage(this.Context,
"Reliable Service says hello");
await Task.Delay(TimeSpan.FromSeconds(60), cancellationToken);
}
}
Note the Task.Delay(...), it should be set to the same interval as your Windows Service had for it's Timer.
Now, if you have logging in your Windows Service and you use ETW, then that should work out of the box for you. You simply need to set up some way of looking at those logs from Azure now, for instance using Log Analytics (https://learn.microsoft.com/en-us/azure/log-analytics/log-analytics-service-fabric).
Other things you might have to migrate is if you run specific code on shut down, on continue, and if you have any parameters sent to the service on startup (for instance connection strings to databases). Those need to be converted to configuration settings for the service, look at SO 33928204 for a starting point for that.
The idea behind service fabric is so that it manages your services, from deployment and running. Once you've deployed your service/application to the service fabric instance it will be just like running a windows service (kinda) so you wont need to install your windows service. If you're using something like TopShelf you can just run the exe and everything will run totally fine within service fabric.

Does the Azure WebJobs SDK support pushing TextWriter logs into App Insights?

With the Azure WebJobs SDK, the process of adding logging to your functions is relatively straightforward: Add a TextWriter param to your triggered function, and write to it. That's it.
The SDK will will then associate and display these logs with their execution instances in the WebJobs Dashboard, which provides a relatively data-rich, yet frictionless view into the operationalization of your webjobs.
While this data is replicated into a user-accessible Azure Storage Blob Container, more custom code would be required to periodically push these logs to App Insights, which is undesirable.
Looking for ideas or solutions for how to push all logs pushed via the injected TextWriter to be pushed to AppInsights (or OMS, for that matter), complete with the webjobs execution/trigger instance metadata, thereby allowing a unified consumption experience for various log analytics.
Based on this Feature being tracked in the WebJobs SDK, I'm assuming that for now this is not possible? A long while back I looked into trying to inject my own TextWriter instance, but I would've had to fork the WebJobs SDK and use my customized assembly that changed a lot of architecture.
You can write a custom TraceWriter that sends log to AppInsights:
using System.Collections.Generic;
using System.Diagnostics;
using Microsoft.ApplicationInsights;
using Microsoft.Azure.WebJobs.Host;
public class AppInsightsTraceWriter : TraceWriter
{
private readonly TelemetryClient _telemetryClient;
public AppInsightsTraceWriter(TraceLevel level, TelemetryClient telemetryClient)
: base(level)
{
_telemetryClient = telemetryClient;
}
public override void Trace(TraceEvent traceEvent)
{
var eventTelemetry = new EventTelemetry() {Name = "WebjobTraceEvent"};
eventTelemetry.Properties.Add(traceEvent.Level.ToString(), traceEvent.ToString());
_telemetryClient.TrackEvent(eventTelemetry);
}
}
In this example, I inject the TelemetryClient class because you should only have one instance of the TelemetryClient class in your application.
So now you just need to configure the Jobhost to use your custom writer :
// Initialize the webjob configuration.
var config = new JobHostConfiguration();
// Only one instance of the telemetry client is needed
var telemetryClient = new TelemetryClient() {InstrumentationKey = "MyInstrumentationKey"};
// Add the app insights tracer for webjob logs/traces.
config.Tracing.Tracers.Add(new AppInsightsTraceWriter(TraceLevel.Info, telemetryClient));
// Detect when the webjob shut down
var cancellationToken = new WebJobsShutdownWatcher().Token;
cancellationToken.Register(() =>
{
// Before shut down, flush the app insights client.
telemetryClient.Flush();
});
new JobHost(config).RunAndBlock();
So if you have a function like that:
public static void ProcessQueueMessage([QueueTrigger("myqueue")] string logMessage, TextWriter log)
{
log.WriteLine(logMessage);
}
Every time you use log.WriteLine, an event will be sent to App Insights.
Note: if this sample also logs from the JobHost are sent to AppInsights.
This is super old (not sure why SO decided to put it in the sidebar for me after this long) but for anyone else that stumbles upon this, app insights is now the recommended way to monitor webjob executions.
Check out the documentation here which steps through the process of connecting app insights to webjobs.
This link walks you through configuring the logging portion of a new webjobs project. Check through the earlier sections to make sure that you've got all the prerequisites.
https://learn.microsoft.com/en-us/azure/app-service/webjobs-sdk-get-started#add-application-insights-logging
static async Task Main()
{
var builder = new HostBuilder();
builder.UseEnvironment(EnvironmentName.Development);
builder.ConfigureWebJobs(b =>
{
b.AddAzureStorageCoreServices();
b.AddAzureStorage();
});
builder.ConfigureLogging((context, b) =>
{
b.AddConsole();
// If the key exists in settings, use it to enable Application Insights.
string instrumentationKey = context.Configuration["APPINSIGHTS_INSTRUMENTATIONKEY"];
if (!string.IsNullOrEmpty(instrumentationKey))
{
b.AddApplicationInsightsWebJobs(o => o.InstrumentationKey = instrumentationKey);
}
});
var host = builder.Build();
using (host)
{
await host.RunAsync();
}
}
I will share the detailed steps to use Application Insights in Azure Web Job, please refer to it.
Create an new Azure Application Insights in Azure portal
Create Azure Web Job project in Visual Studio and install
Microsoft.ApplicationInsights
Set the instrumentation key and send
telemetry
public static void ProcessQueueMessage([QueueTrigger("queuename")] string message, TextWriter log)
{
TelemetryClient tc = new TelemetryClient();
tc.InstrumentationKey = "key copied from Azure portal";
tc.TrackTrace(message);
tc.Flush();
//log.WriteLine(message);
}
This documentation explained how to monitor usage and performance in Windows Desktop apps, you could refer to it to know how to use Azure Application Insights in non-web application. Besides, ApplicationInsights.Helpers.WebJobs could be also helpful.

Resources