I have an application which receive messages through a Azure ServiceBus using the Rebus library.
I observe a different behaviour when the application runs on Azure as a Web app, compared to when I run it locally.
When it runs in Azure, my message handlers are not being invoked.
Everything works as expected when the application runs local.
This is my setup.
In my web project Portal, Global.asax.cs:
protected void Application_Start()
{
_webContainer = new WindsorContainer();
_queueContainer = new WindsorContainer();
ConfigureWebContainer();
_queueContainer.Install(FromAssembly.Containing<ServiceBusInstaller>());
ApplicationInsightsConfig.Configure();
}
In the project ServiceBus (where ServiceBusInstaller lies)
public void Install(IWindsorContainer container, IConfigurationStore store)
{
var adapter = new CastleWindsorContainerAdapter(container);
var connectionString = CloudConfigurationManager.GetSetting("QueueUrl");
var configurer = Configure
.With(adapter)
.Options(o =>
{
o.SimpleRetryStrategy(maxDeliveryAttempts: 0);
})
.Logging(l => l.Log4Net())
.Routing(r => r.TypeBased()
.MapAssemblyOf<ResetPasswordMessage>("Email"))
Transport(t => t.UseAzureServiceBus(connectionString, "Email"));
// Create and starts the bus
configurer.Start();
}
My ResetPasswordHandler looks like this:
public class ResetPasswordHandler : IHandleMessages<ResetPasswordMessage>
{
private readonly IContactAndEmailService _contactAndEmailService;
public ResetPasswordHandler(IContactAndEmailService contactAndEmailService)
{
_contactAndEmailService = contactAndEmailService;
}
public async Task Handle(ResetPasswordMessage message)
{
_contactAndEmailService.SendEmail(message);
}
}
I am using Service Bus Explorer to connect to the service bus, and I can see that the message is in the "email" queue. It does however stay there, and thus is not consumed.
Any pointers to either setup or azure subscribtion limitations or something else that can help me move forward is highly appreciated.
I think it might have something to do with the casing of the queue name, Email.
Could you try and make it lowercase throughout?
Related
All the code samples I've seen so far for Azure WebJobs rely on some kind of trigger (e.g. TimerTrigger or QueueTrigger).
I am looking specifically at WebJobs SDK 3.x, by the way.
So. For a triggerless WebJob (Windows Service-alike one), am I expected to use NoAutomaticTrigger and find a way to kickoff my "main" code manually?
Or should I resort to implementing and registering a class that implements the IHostedService interface?
So far that's the approach I'm taking but it feels more of a hack than a recommended way.
I have not even tried to deploy this code and only ran it on my local machine, so I am afraid that the publishing process will confirm my code is not suitable for Azure WebJobs in its current form.
EntryPoint.cs
This is how the application is being bootstrap when the process is starting.
using Microsoft.Azure.ServiceBus;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
namespace AbcCorp.Jobs
{
public static class Program
{
static async Task Main(string[] args)
{
var config = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
.AddJsonFile($"appsettings.{Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT")}.json", false)
.Build();
var hostBuilder = new HostBuilder()
.ConfigureWebJobs(builder => { builder.AddAzureStorageCoreServices(); })
.ConfigureServices(serviceCollection =>
{
ConfigureServices(serviceCollection, config);
serviceCollection.AddHostedService<ConsoleApplication>();
});
using (var host = hostBuilder.Build())
await host.RunAsync();
}
private static IServiceCollection ConfigureServices(IServiceCollection services, IConfigurationRoot configuration)
{
services.AddTransient<ConsoleApplication>();
// ... more DI registrations
return services;
}
}
}
ConsoleApplication.cs
This would normally be implemented as a function with a trigger.
The thing is, I want this code to only run once on the process startup.
It will start listening on the service bus events using the regular Microsoft.Azure.ServiceBus SDK package.
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Hosting;
using AbcCorp.Internal.Microsoft.Azure.ServiceBus;
using AbcCorp.Api.Messaging;
namespace AbcCorp.Jobs
{
public sealed class ConsoleApplication: IHostedService
{
private readonly IReceiver<SubmissionNotification> _messageReceiver;
private readonly MessageHandler _messageHandler;
public ConsoleApplication(IReceiver<SubmissionNotification> messageReceiver, MessageHandler messageHandler)
{
_messageReceiver = messageReceiver;
_messageHandler = messageHandler;
}
public Task StartAsync(CancellationToken cancellationToken)
{
_messageReceiver.StartListening(_messageHandler.HandleMessage, _messageHandler.HandleException);
return Task.Delay(Timeout.Infinite);
}
public Task StopAsync(CancellationToken cancellationToken)
{
_messageReceiver.Dispose();
return Task.CompletedTask;
}
}
}
So you want a console application to run in a WebJob and listen to messages. You don't really care about WebJob magic like triggers, it's just a place to run your console app. I've done the exact same thing before.
I found the IHostedService abstraction to be very helpful, but I didn't like their SDK. I found it bloated and hard to use. I didn't want to take a large dependency in order use a large array of special magic Azure stuff, when all I wanted to do was run a console application in a WebJob for now, and maybe move it elsewhere later.
So I ended just deleting that dependency, stealing the Shutdown code from the SDK and writing my own Service Host. The result is on my Github Repo azure-webjob-host. Feel free to use it or raid it for ideas. I don't know, maybe if I did it again I'd have another attempt at getting the SDK to work, but I present this is a bit of an alternative to the SDK.
Basically I wrote an IServiceHost not too different from yours (except that StartAsync exited when stuff started instead of just hanging). Then I wrote my own service host, which is basically just a loop:
await _service.StartAsync(cancellationToken);
while (!token.IsCancellationRequested){await Task.Delay(1000);}
await _service.StopAsync(default);
Then I stole the WebJobsShutdownWatcher code from their repo.
Then I created an IServiceHost that started my message handler. (I was using Rabbit, which has nothing to do with triggers or azure stuff)
public class MessagingService : IHostedService, IDisposable
{
public MessagingService(ConnectionSettings connectionSettings,
AppSubscriberSettings subscriberSettings,
MessageHandlerTypeMapping[] messageHandlerTypeMappings,
ILogger<MessagingService> logger)
{
....
}
public async Task StartAsync(CancellationToken cancellationToken)
{
cancellationToken.ThrowIfCancellationRequested();
await Task.WhenAll(subscribers.Value.Select(s => s.StartSubscriptionAsync()));
}
public async Task StopAsync(CancellationToken cancellationToken)
{
...
}
public void Dispose()
{
...
}
}
Then I put that all together into something like this:
IHostedService myService = new MyService();
using (var host = new ServiceHostBuilder().HostService(myService))
{
await host.RunAsync(default);
}
I have some workers attached to service bus topics and what we do is the following (ServiceBusClient is a custom Class that contains our Subscription Client):
public override Task StartAsync(CancellationToken cancellationToken)
{
_serviceBusClient.RegisterOnMessageHandlerAndReceiveMessages(MessageReceivedAsync);
_logger.LogDebug($"Started successfully the Import Client. Listening for messages...");
return base.StartAsync(cancellationToken);
}
public void RegisterOnMessageHandlerAndReceiveMessages(Func<Message, CancellationToken, Task> ProcessMessagesAsync)
{
// Configure the message handler options in terms of exception handling, number of concurrent messages to deliver, etc.
var messageHandlerOptions = new MessageHandlerOptions(ExceptionReceivedHandler)
{
// Maximum number of concurrent calls to the callback ProcessMessagesAsync(), set to 1 for simplicity.
// Set it according to how many messages the application wants to process in parallel.
MaxConcurrentCalls = 1,
// Indicates whether MessagePump should automatically complete the messages after returning from User Callback.
// False below indicates the Complete will be handled by the User Callback as in `ProcessMessagesAsync` below.
AutoComplete = false
};
// Register the function that processes messages.
SubscriptionClient.RegisterMessageHandler(ProcessMessagesAsync, messageHandlerOptions);
}
And then you can use DI to instantiate your service bus client and inject on the constructor of your Worker class.
Here i have the initialization of the singleton instance of my custom class Service Bus Client
services.AddSingleton<IServiceBusClient, ServiceBusClient>((p) =>
{
var diagnostics = p.GetService<EventHandling>();
var sbc = new ServiceBusClient(
programOptions.Endpoint,
programOptions.TopicName,
programOptions.Subscriber,
programOptions.SubscriberKey);
sbc.Exception += exception => diagnostics.HandleException(exception);
return sbc;
});
Then on this custom class, i initialize my subscription client
public ServiceBusClient(
string endpoint,
string topicName,
string subscriberName,
string subscriberKey, ReceiveMode mode = ReceiveMode.PeekLock)
{
var connBuilder = new ServiceBusConnectionStringBuilder(endpoint, topicName, subscriberName, subscriberKey);
var connectionString = connBuilder.GetNamespaceConnectionString();
ConnectionString = connectionString;
TopicName = topicName;
SubscriptionName = topicName;
SubscriptionClient = new SubscriptionClient(connectionString, topicName, subscriberName, mode);
}
You can check #george chen's answer from this post How to create service bus trigger webjob?
where instead of creating a receiver and registering a message handler, you can use the in built queue trigger and and write your message handler logic inside it.
I have a .net core 3.0 web application. In Startup.cs, I register an EventProcessor (from Microsoft.Azure.EventHubs.Processor) that listens to an Azure EventHub for events. I do it like this:
await eventProcessorHost.RegisterEventProcessorAsync<TwinChangesEventHandler>();
I'm interested in device twin changes in an IoT Hub that's connected to the EventHub.
So, in the EventProcessor, I want to access the SignalR IHubContext interface (from Microsoft.AspNetCore.SignalR - the new version of SignalR) to be able to notify connected browsers of a device twin property change. My problem is that the EventProcessor can't get a handle to IHubContext. How can I get it?
I see online that people are using dependency injection but because my EventProcessor is created by RegisterEventProcessorAsync() like I showed above, its default constructor is ALWAYS called and NOT the one with IHubContext as a parameter! Even if I use a factory to create the EventProcessor and call RegisterEventProcessorFactoryAsync() in Startup.cs, I can't get the IHubContext handle in the factory, because the call is not originating from a controller. It either originates from Startup.ConfigureServices() or a callback from whenever something happens in the EventHub, which is not a controller method. I'm really stuck, so any help would be much appreciated. Does anyone know the answer to this?
You can add your Factory and processor to services
.ConfigureServices((hostContext, services) =>
{
...
services.AddSingleton<IEventProcessorFactory, EventProcessorFactory>();
services.AddSingleton<IEventProcessor, TwinChangesEventHandler>();
...
});
public class EventProcessorFactory : IEventProcessorFactory
{
private readonly IEventProcessor _fluxEventProcessor;
public EventProcessorFactory(IEventProcessor fluxEventProcessor)
{
_fluxEventProcessor = fluxEventProcessor;
}
public IEventProcessor CreateEventProcessor(PartitionContext context)
{
return _fluxEventProcessor;
}
}
Then in your handler you can have access to the injected hub
public class TwinChangesEventHandler : IEventProcessor
{
private readonly IHubContext<MyHub> _myHubContext;
public TwinChangesEventHandler(IHubContext<MyHub> myHubContext)
{
_myHubContext= myHubContext;
}
...
async Task IEventProcessor.ProcessEventsAsync(PartitionContext context, IEnumerable<EventData> messages)
{
foreach (var eventData in messages)
{
await _myHubContext.Clients.All.SendAsync("Update", eventData);
}
//Call checkpoint every 5 minutes, so that worker can resume processing from 5 minutes back if it restarts.
if (_checkpointStopWatch.Elapsed > TimeSpan.FromMinutes(5))
{
await context.CheckpointAsync();
_checkpointStopWatch.Restart();
}
}
}
Please help, this is driving me crazy!
My Server -> SignalR (JS) Client method execution works fine via the SignalR pipeline, but not when called via a background service. I'm using ASP.NET MVC / Castle Windsor DI
I use a custom Dependency Resolver which I register during Owin Startup
I observe that via the NotificationHub (SignalR pipeline), Clients is resolved to a HubConnectionContext class:
However, via my background service, Clients resolves to a HubConnectionContextBase class:
... so I'm pretty sure it's a DI issue. I just can't see what I'm doing wrong. Also any tips to Debug would be greatly appreciated. Thanks
Application Start:
bootstrapper = ContainerBootstrapper.Bootstrap();
this.container = bootstrapper.Container;
var resolverSignalR = new DependencyResolverSignalR(container);
GlobalHost.DependencyResolver = resolverSignalR;
OwinConfig:
app.MapSignalR(url, DependencyResolverSignalR.CreateHubConfiguration());
DependencyResolverSignalR:
public class DependencyResolverSignalR : DefaultDependencyResolver
{
public static HubConfiguration CreateHubConfiguration()
{
var signalrDependencyResolver = new DependencyResolverSignalR(_container);
var configuration = new HubConfiguration {
EnableDetailedErrors = true,
Resolver = signalrDependencyResolver
};
return configuration;
}
private static IWindsorContainer _container;
public DependencyResolverSignalR(IWindsorContainer container)
{
if (container == null)
{
throw new ArgumentNullException(nameof(container));
}
_container = container;
}
public override object GetService(Type serviceType)
{
return _container.Kernel.HasComponent(serviceType) ? _container.Resolve(serviceType) : base.GetService(serviceType);
}
public override IEnumerable<object> GetServices(Type serviceType)
{
return _container.Kernel.HasComponent(serviceType) ? _container.ResolveAll(serviceType).Cast<object>() : base.GetServices(serviceType);
}
}
NotificationService:
(runs via a loop every 10 seconds - AFTER client has connected)
// Not working
var hubContext = GlobalHost.ConnectionManager.GetHubContext<NotificationHub>();
var clients = hubContext.Clients;
clients.All.receiveMessage(testMessage);
NotificationHub:
public override Task OnConnected()
{
var connectionId = Context.ConnectionId;
...
...
// Working fine
base.Clients.All.receiveMessage(testMessage);
return base.OnConnected();
}
Client:
omitted since it works fine via the signalr pipeline
I would expect the receiveMessage() client method called via the NotificationService to be executed on the client in exactly the same way it does when called via the SignalR pipeline. Instead nada. No error message, the call just silently does nothing.
I've even tried following the guide here (though it's geared towards Ninject) SignalR document to resolve the Clients (IHubConnectionContext) directly. Exactly the same result.
Code:
var resolverSignalR = new DependencyResolverSignalR(container);
container.Register(Component
.For<IHubConnectionContext<dynamic>>()
.UsingFactoryMethod(() =>
resolverSignalR.Resolve<IConnectionManager().GetHubContext<NotificationHub>().Clients));
Solved! It was an issue with the HubConfiguration - I'm not sure what exactly. But just bypassing it (which is possible because I'm already replacing the DependencyResolver in Application_Start()) fixed the issue.
Old OwinConfig:
app.MapSignalR(url, DependencyResolverSignalR.CreateHubConfiguration());
Replaced by:
app.MapSignalR();
but make sure you have something like this in Application_Start() (or wherever you initialise your DI container)
var resolverSignalR = new DependencyResolverSignalR(container);
GlobalHost.DependencyResolver = resolverSignalR;
Using .NET 4.5.2, Visual studio 2017, C# 7.1, Unity, NServiceBus 6.
I receive the following error:
My application is a console app, here's some of the Program.cs code:
private static async Task ConfigureUnity()
{
IUnityContainer container = new UnityContainer();
var endpointConfiguration = new EndpointConfiguration("NSB.ChannelAdvisorService");
var transport = endpointConfiguration.UseTransport<LearningTransport>();
endpointConfiguration.AssemblyScanner().ExcludeAssemblies("netstandard");
endpointConfiguration.UseContainer<UnityBuilder>(
customizations =>
{
customizations.UseExistingContainer(container);
});
var endpointInstance = Endpoint.Start(endpointConfiguration).GetAwaiter().GetResult();
//register
container.RegisterType(typeof(IGenericHttpRequestRepository<>), typeof(GenericHttpRequestRepository<>), new TransientLifetimeManager());
container.RegisterType<IOrderRepository, OrderRepository>();
container.RegisterType<IShipmentRepository, ShipmentRepository>();
container.RegisterType<IOrderProcessService, OrderProcessService>();
container.RegisterType<IShipmentService, ShipmentService>();
container.RegisterInstance(endpointConfiguration);
//resolve
var orderProcessService = container.Resolve<IOrderProcessService>();
var shipmentService = container.Resolve<IShipmentService>();
.....
As you can see I'm using Unity and NServiceBus, this is to register DI and also use it withing NServicebus so i can DI it into my service to send a command.
The service trys to DI "IEndpointInstance"
public class OrderProcessService : IOrderProcessService
{
private static Logger logger = LogManager.GetCurrentClassLogger();
private readonly IEndpointInstance _endpoint;
public OrderProcessService(IEndpointInstance endpoint)
{
_endpoint = endpoint;
}
public async Task PostNewOrderBatch()
{
var list = _orderRepository.GetBatchedOrders();
foreach(var item in list)// parallel this?
{
await _endpoint.Send(item.ToObject<ProcessBatchOrdersCommand>()).ConfigureAwait(false);
_orderRepository.DeleteFile(item.Property("FilePath").Value.ToString());
}
}
}
I get the feeling it could be an issue about the order of things, I don't think I've missed anything out as far as i can tell in some examples?
In NServiceBus v6 and later the endpoint instance is no longer automatically registered in the container. You need to register the endpoint instance returned from Endpoint.Start(configuration) on the existing container.
See https://docs.particular.net/nservicebus/dependency-injection/#using-an-existing-instance-endpoint-resolution
I am working on an MVC 5 application that uses a windows service to perform some processing; I am using signal R so that I can show if the windows service is working on the UI and also allow the user to manually start processing (as opposed to running on a schedule).
On the server side I have the following config:
public class SignalRStartup
{
public static IAppBuilder App = null;
public void Configuration(IAppBuilder app)
{
app.Map("/signalr", map =>
{
map.UseCors(CorsOptions.AllowAll);
var hubConfiguration = new HubConfiguration
{
EnableDetailedErrors = true,
};
map.RunSignalR(hubConfiguration);
});
}
}
Which is used like so:
SignalR = WebApp.Start<SignalRStartup>(_settings.LoaderServiceUrl);
Right now the loader service url is: http://localhost:8080
Then on the client side:
var adminHubProxy = $.connection.adminHub;
adminHubProxy.client.updateProcessing = function(status) {
if (status === true) {
$('#processing').show();
} else {
$('#processing').hide();
}
};
$.connection.hub.url = 'http://localhost:8080/signalr';
$.connection.hub.start();
$('#startProcessingLink').on('click', function() {
adminHubProxy.server.startProcessing();
});
And if it matters the code that includes the generated proxy:
<script src="http://localhost:8080/signalr/hubs"></script>
So the problem I'm having is that when I trigger the startProcessing function the server throws back this error message:
XMLHttpRequest cannot load http://localhost:8080/signalr/send?transport=serverSentEvents&connectionTok…Pp7JqCJOnkJEA%3D%3D&connectionData=%5B%7B%22name%22%3A%22adminhub%22%7D%5D.
No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost' is therefore not allowed access.
From all the reading I've done my configuration should be resolving this issue by allowing all CORS requests but it isn't and I can't see why.
Edit
After some more debugging I pulled up the details of the response on the negotiate call and am seeing the following headers:
Access-Control-Allow-Credentials:true
Access-Control-Allow-Origin:http://localhost
I'm not sure why the credentials header is being added at all, but the origin header again makes me believe that this should be working.
I figured out the problem, first off the error message has absolutely nothing to do with what is going on.
TL;DR;
The problem was that the AdminHub could not be resolved on the server side because of my dependency injection setup
I am using Castle Windsor for dependency injection and originally the AdminHub looked like this:
public class AdminHub : Hub
{
private readonly IMyService _myService;
public AdminHub(IMyService myService)
{
_myService= myService;
_myService.OnProcessingUpdate += (sender, args) => UpdateProcessingStatus();
}
public void UpdateProcessingStatus()
{
Clients.All.updateProcessing(_myService.IsProcessing);
}
public void GetProcessingStatus()
{
Clients.Caller.updateProcessing(_myService.IsProcessing);
}
public void StartProcessing()
{
_myService.Process();
}
}
The default dependency resolver cannot resolve this as it requires a parameterless constructor. This answer both served to point out what was happening and provide the basis for a solution.