I’m interested in building a long running queue processor with Service Stack using the new Core 3.0 Worker Service template and would be nice to have a quick start template available.
My guess is this is similar to web new selfhost but I’m assuming there is something “more” provided using the Worker Service when it comes to system level hooks when running either as a Windows Service or Linux Daemon.
Just had a look at this Worker Service template and this is their background worker example:
public class Worker : BackgroundService
{
private readonly ILogger<Worker> _logger;
public Worker(ILogger<Worker> logger)
{
_logger = logger;
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
while (!stoppingToken.IsCancellationRequested)
{
_logger.LogInformation("Worker running at: {time}", DateTimeOffset.Now);
await Task.Delay(1000, stoppingToken);
}
}
}
It’s running a background task not a HTTP Server so I’m not sure what ServiceStack is supposed to be doing here? What use-case were you planning on using ServiceStack here for?
So I’m trying to build a queue processor, nothing more. I don’t want the added weight of any http stuff as it will simply sit there processing incoming messages from SQS. I’m attracted to this new Worker Service because it seems to have tighter OS level integration with regards to service startup and shutdown.
Because I like ServiceStack so much, I was hoping to use it for this. This Worker Service would handle SQS messages for typed dto’s and then possibly do stuff with AutoQuery/Ormlite to “process” the message.
I’ve added a worker-rabbitmq template based on the .NET Core 3.0 worker service project which runs an headless ServiceStack AppHost and a RabbitMQ Worker background Service which starts the MQ Server and dumps the MQ Stats every 10s until the request is cancelled where it will stop the MQ Server.
There’s an example of calling the Rabbit MQ Server in the MqTest project:
using (var mqClient = MqFactory.CreateMessageQueueClient())
{
var replyToMq = mqClient.GetTempQueueName();
mqClient.Publish(new Message<Hello>(new Hello { Name = "MQ Worker" })
{
ReplyTo = replyToMq,
});
var responseMsg = mqClient.Get<HelloResponse>(replyToMq);
mqClient.Ack(responseMsg);
Assert.That(responseMsg.GetBody().Result, Is.EqualTo("Hello, MQ Worker!"));
}
It should be easy to swap out RabbitMQ to use your preferred MQ Server.
You can create a project from this template using web new:
Ok, I created the sample project with web new worker-rabbitmq MqTest. I swapped out RabbitMQ for SQS with the following:
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureServices((hostContext, services) =>
{
AppHost = new BasicAppHost(typeof(MyService).Assembly)
{
ConfigureAppHost = host =>
{
//var mqServer = new RabbitMqServer(hostContext.Configuration.GetConnectionString("RabbitMq") ?? "localhost:5672");
//mqServer.RegisterHandler<Hello>(host.ExecuteMessage);
//host.Register<IMessageService>(mqServer);
var mqServer = new SqsMqServer(
"access_key",
"secret_key",
RegionEndpoint.USWest2)
{
DisableBuffering = true, // Trade-off latency vs efficiency
};
mqServer.RegisterHandler<Hello>(host.ExecuteMessage);
host.Register<IMessageService>(mqServer);
}
}.Init();
services.AddSingleton(AppHost.Resolve<IMessageService>());
services.AddHostedService<MqWorker>();
});
But when it picks up an already published message from the mq-Hello-inq queue, it immediately moves it to mq-Hello-dlq but this time there is no error attribute on the message, so I can’t figure out why it’s failing. I attempted to debug by placing multiple breakpoints within Visual Studio but none of them get triggered, so I’m not sure what’s going on. Do you have any idea where the problem may be?
Well, the interesting thing is, simply commenting out this line results in it going right back to the dlq. Uncommenting it, makes it go to the response inq.
I’m attempting to add OrmLite capability to this service. I used
mix postgres
It added a Configure.Db class. However, it doesn’t seem to be getting used by this worker service. I have used mix … in a regular web based app and the Configure.X seems to be auto-wired, but not in this case.
What am I missing to get Configure.Db wired up in this worker service?
That worked. However, I need to implement something similar to the RegisterTypedRequestFilter so that I can set the actual database connection per “environment” as it comes across as a property in the message.
Since I’m assuming the request handler is not an actual “request”, it seems this code never gets executed so I have no chance to set the connection string for that environment.
How can I do something similar to the below in the Worker Service?
RegisterTypedRequestFilter<IForEnvironment>((req, res, requestDto) =>
{
if (requestDto.Environment == Guid.Empty)
throw new ArgumentNullException("Environment");
req.Items[Keywords.DbInfo] = new ConnectionInfo() { ConnectionString = GetEnvironmentConnString(dbFactory, requestDto.Environment) };
});