According to release note for MassTransit 7.1.0 version the bus restart should be possible - https://masstransit-project.com/releases/v7.1.0.html#re-start-the-bus-finally.
Unfortunately for MassTransit configured to use RebbitMQ I'm getting exception on message publish after another bus restart. It works fine to and after first restart but after the second restart publish operation throws MassTransit.TransportUnavailableException: The RabbitMQ send transport is stopped.
Below code of simple application (using MassTransit 7.1.3) exposing the problem:
using MassTransit;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using System;
using System.Threading.Tasks;
namespace MassTransitRestartIssue
{
class Program
{
static async Task Main(string[] args)
{
IServiceCollection services = new ServiceCollection();
services.AddLogging(logConfig =>
{
logConfig.AddConsole();
});
services.AddMassTransit(config =>
{
config.AddConsumer<TestConsumer>();
config.UsingRabbitMq((ctx, cfg) =>
{
cfg.Host("localhost", "test", h =>
{
h.Username("guest");
h.Password("guest");
});
cfg.ConfigureEndpoints(ctx);
});
});
var serviceProvider = services.BuildServiceProvider();
var busControl = serviceProvider.GetRequiredService<IBusControl>();
var logger = serviceProvider.GetRequiredService<ILogger<Program>>();
await busControl.StartAsync();
try
{
await busControl.Publish(new TestEvent("test message 1"));
// restart bus
await busControl.StopAsync();
await busControl.StartAsync();
await busControl.Publish(new TestEvent("test message 2"));
// another restart bus
await busControl.StopAsync();
await busControl.StartAsync();
await busControl.Publish(new TestEvent("test message 3"));
// throws - MassTransit.TransportUnavailableException: The RabbitMQ send transport is stopped: MassTransitRestartIssue:TestEvent
Console.ReadLine();
}
catch (Exception ex)
{
logger.LogError(ex, "error");
}
finally
{
await busControl.StopAsync();
}
}
}
public record TestEvent(string Message)
{
}
public class TestConsumer :
IConsumer<TestEvent>
{
private readonly ILogger _logger;
public TestConsumer(ILogger<TestConsumer> logger)
{
_logger = logger;
}
public Task Consume(ConsumeContext<TestEvent> context)
{
_logger.LogInformation($"Test event consummed - {context.Message.Message}");
return Task.CompletedTask;
}
}
}
Is it a bug or intended behavior? If it intended how can I fix the problem?
Related
I am trying to build a simple test. I have gotten the Kestrel code to work within a console app but I am not able to within a Worker Service. When I make a call against the port, I get the null exception. In debugging I can't tell which part is null, everything looks like it has values. I feel like its a scope issue. I have tried saving the webbuilderhost several different ways. Still get the same error. I have tried starting the host in several different places including it's own class. Any Advice would be appreciated.
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
namespace ServiceWorker
{
public class WindowsBackgroundService : BackgroundService
{
private readonly ILogger<WindowsBackgroundService> _logger;
public WindowsBackgroundService(ILogger<WindowsBackgroundService> logger)
{
_logger = logger;
}
public override async Task StartAsync(CancellationToken cancellationToken)
{
var host = new WebHostBuilder()
.UseUrls("http://*:5555")
.UseKestrel()
.Configure(app =>
{
app.Run(async context => await context.Response.WriteAsync("Current Date: " + DateTime.Now));
}
).Build();
host.RunAsync();
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
while (!stoppingToken.IsCancellationRequested)
{
//_logger.LogInformation("Worker running at: {time}", DateTimeOffset.Now);
await Task.Delay(1000, stoppingToken);
}
}
}
}
using ServiceWorker;
IHost host = Host.CreateDefaultBuilder(args)
.UseWindowsService(options =>
{
options.ServiceName = "Porthog Service";
})
.ConfigureServices(services =>
{
services.AddHostedService<WindowsBackgroundService>();
})
.ConfigureLogging(logging =>
{
logging.ClearProviders();
logging.SetMinimumLevel(Microsoft.Extensions.Logging.LogLevel.Trace);
})
.Build();
await host.RunAsync();
I created a Solution with ASP.NET Core WEB API project, some class libraries (Domain, DI and ect), and a console application.
A console application that I use as a RabbitMQ Consumer with Masstransit library it should take messages from RabbitMQ (I have Producer project and it sends for RabbitMQ messages without problems)
My ConsoleApplication:
like this Program.cs:
public class Program
{
public static void Main(string[] args)
{
CreateHostBuilder(args).Build().Run();
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureServices((hostContext, services) =>
{
services.AddMassTransit(x =>
{
x.AddConsumer<MessageConsumer>();
x.UsingRabbitMq((context, cfg) =>
{
var connectionString = new Uri("RabbitMQ_URL");
cfg.Host(connectionString);
cfg.ConfigureEndpoints(context);
});
});
services.AddMassTransitHostedService(true);
services.AddHostedService<Worker>();
});
}
With Worker.cs:
public class Worker : BackgroundService
{
readonly IBus _bus;
public Worker(IBus bus)
{
_bus = bus;
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
while (!stoppingToken.IsCancellationRequested)
{
var factory = new ConnectionFactory() { Uri = new
Uri("RabbitMQ_URL"), DispatchConsumersAsync = true };
using (var connection = factory.CreateConnection())
using (var channel = connection.CreateModel())
{
channel.QueueDeclare(queue: "MessageQueue",
durable: true,
exclusive: false,
autoDelete: false,
arguments: null);
var consumer = new AsyncEventingBasicConsumer(channel);
consumer.Received += async (model, ea) =>
{
var body = ea.Body.ToArray();
var message = Encoding.UTF8.GetString(body);
var #event = JsonConvert.DeserializeObject<Event>
(message);
await _bus.Publish(new Event { DataJson = #event });
await Task.Yield();
};
channel.BasicConsume(queue: "MessageQueue",
autoAck: true,
consumer: consumer);
_logger.LogInformation("Received Text: {Text}", context.Message.DataJson);
return Task.CompletedTask;
}
}
}
MessageConsumer.cs:
public class MessageConsumer :
IConsumer<Event>
{
readonly ILogger<MessageConsumer> _logger;
public MessageConsumer(ILogger<MessageConsumer> logger)
{
_logger = logger;
}
public Task Consume(ConsumeContext<Event> context)
{
_logger.LogInInformation("Recieved Text: {Text},
context.Message.DataJson");
return Task.CompletedTask;
}
}
And my Event.cs:
public class Event
{
public ServiceType ServiceType { get; set; }
public string DataJson { get; set; }
}
public enum ServiceType
{
ComplareSitter
}
Please help me
Thanks a lot.
You might start with a clean and simple worker service using one of the MassTransit templates, just to verify your setup/configuration. There is a video available showing how to setup and use the templates.
But an obvious question, why on earth are you connecting to RabbitMQ and creating a basic consumer inside the Consume method? The message has already been deserialized as your Event type and is ready to be used. There is absolutely no need to use any part of the RabbitMQ Client library in your application when using MassTransit.
I would like to create a SignalR Self hosting Server within a console app using .NetCore.
I am completely new to web development and .Net Core but would like to use SignalR as a real-time web based protocol. No web page is required, and so I would like to have a console app.
I have successfully tested the .Net Framework example below and would like to replicate this using .Net Core 3.1, so that it can run on Linux. However I cannot find any suitable examples.
using System;
using Microsoft.AspNet.SignalR;
using Microsoft.Owin.Hosting;
using Owin;
using Microsoft.Owin.Cors;
namespace SignalRSelfHost
{
class Program
{
static void Main(string[] args)
{
// This will *ONLY* bind to localhost, if you want to bind to all addresses
// use http://*:8080 to bind to all addresses.
// See http://msdn.microsoft.com/library/system.net.httplistener.aspx
// for more information.
string url = "http://localhost:8088";
using (WebApp.Start<Startup>(url))
{
Console.WriteLine("Server running on {0}", url);
Console.ReadLine();
}
}
}
class Startup
{
public void Configuration(IAppBuilder app)
{
app.UseCors(CorsOptions.AllowAll);
app.MapSignalR();
}
}
public class MyHub : Hub
{
public void Send(string name, string message)
{
Clients.All.addMessage(name, message);
Clients.All.addMessage(name, "World");
}
}
}
In an attempt to use Owin to create a server console app I have the following code and this compiles, however complains about no server service being registered when I run the program. Could someone please advise what to add to have a web server without web page? The example I copied specified UseKestrel() but I think this is for a web page, so I think I need something else.
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Text;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.DependencyInjection;
namespace OwinConsole
{
public class Startup
{
// For more information on how to configure your application, visit http://go.microsoft.com/fwlink/?LinkID=398940
public void ConfigureServices(IServiceCollection services)
{
}
public void Configure(IApplicationBuilder app)
{
app.UseOwin(pipeline =>
{
pipeline(next => OwinHello);
});
}
public Task OwinHello(IDictionary<string, object> environment)
{
string responseText = "Hello World via OWIN";
byte[] responseBytes = Encoding.UTF8.GetBytes(responseText);
// OWIN Environment Keys: http://owin.org/spec/spec/owin-1.0.0.html
var responseStream = (Stream)environment["owin.ResponseBody"];
var responseHeaders = (IDictionary<string, string[]>)environment["owin.ResponseHeaders"];
responseHeaders["Content-Length"] = new string[] { responseBytes.Length.ToString(CultureInfo.InvariantCulture) };
responseHeaders["Content-Type"] = new string[] { "text/plain" };
return responseStream.WriteAsync(responseBytes, 0, responseBytes.Length);
}
}
}
using System.IO;
using Microsoft.AspNetCore.Hosting;
namespace OwinConsole
{
public class Program
{
public static void Main(string[] args)
{
var host = new WebHostBuilder()
.UseContentRoot(Directory.GetCurrentDirectory())
.UseStartup<Startup>()
.Build();
host.Run();
}
}
}
Thanks.
For those who want to achive this in .NET 6:
To create a simple server as a console application, you have to create a new empty ASP.NET Core project. In .NET 6 you don't need the 'startup.cs' anymore. You just need to change a few things in 'Program.cs' to configure SignalR.
Program.cs
var builder = WebApplication.CreateBuilder(args);
//builder.Services.AddRazorPages();
builder.Services.AddSignalR();
var app = builder.Build();
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Error");
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthorization();
//app.MapRazorPages();
app.MapHub<ChatHub>("/chatHub");
app.Run();
Add a Hub
public class ChatHub : Hub
{
public async Task SendMessage(string user, string message)
{
Console.WriteLine("Received message, sending back echo");
await Clients.All.SendAsync("ReceiveMessage", user, message);
}
}
Client (console application)
For example:
using Microsoft.AspNetCore.SignalR.Client;
namespace Client
{
public class Program
{
private static HubConnection _connection;
public static async Task Main(string[] args)
{
_connection = new HubConnectionBuilder()
.WithUrl("https://localhost:7116/chatHub")
.Build();
_connection.Closed += async (error) =>
{
await Task.Delay(new Random().Next(0, 5) * 1000);
await _connection.StartAsync();
};
await ConnectAsync();
bool stop = false;
while (!stop)
{
Console.WriteLine("Press any key to send message to server and receive echo");
Console.ReadKey();
Send("testuser", "msg");
Console.WriteLine("Press q to quit or anything else to resume");
var key = Console.ReadLine();
if (key == "q") stop = true;
}
}
private static async Task ConnectAsync()
{
_connection.On<string, string>("ReceiveMessage", (user, message) =>
{
Console.WriteLine("Received message");
Console.WriteLine($"user: {user}");
Console.WriteLine($"message: {message}");
});
try
{
await _connection.StartAsync();
}
catch(Exception e)
{
Console.WriteLine("Exception: {0}", e);
}
}
private static async void Send(string user, string msg)
{
try
{
await _connection.InvokeAsync("SendMessage", user, msg);
}
catch (Exception e)
{
Console.WriteLine($"Exception: {e}");
}
}
}
}
The client will connect to the server, after that in a loop you can send a message to the server by pressing any key and the server will send you the same message back.
In 'launchSettings.json' (Server) you can find the applicaitonUrl
As mentioned by Noah, my solution was based on
[https://learn.microsoft.com/en-us/aspnet/core/tutorials/signalr?view=aspnetcore-5.0&tabs=visual-studio]
But instead was built as a console app referencing Microsoft.AspNetCore.App (2.2.8).
ChatHub
using Microsoft.AspNetCore.SignalR;
using System.Threading.Tasks;
namespace SignalRServer
{
public class ChatHub : Hub
{
public async Task SendMessage(string user, string message)
{
await Clients.All.SendAsync("ReceiveMessage", user, message);
}
}
}
Startup
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
namespace SignalRServer
{
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
//services.AddRazorPages();
services.AddSignalR();
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Error");
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
//endpoints.MapRazorPages();
endpoints.MapHub<ChatHub>("/chatHub");
});
}
}
}
Program
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Hosting;
namespace SignalRServer
{
public class Program
{
public static void Main(string[] args)
{
CreateHostBuilder(args).Build().Run();
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
webBuilder.UseUrls("http://localhost:2803");
});
}
}
I'm new to .NET Core. I currently have a WCF host service that hosts another service that I'm trying to convert to .NET core. Using .NET 5, I created a worker service that handles the host background tasks and setup another service w/ an endpoint to handle incoming responses from another client. I'm having trouble using the EndpointAddress and ChannelFactory approach to create the endpoint and channel so the endpoint can be accessible via the outside world for response messages, but in doing so, I get the following error:
"No connection could be made because the target machine actively refused it. (localhost:8000)"
Maybe I'm going about this in the wrong way to host the service, not sure. Does anyone know?
public class Program
{
public static void Main(string[] args)
{
CreateHostBuilder(args).Build().Run();
return;
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.UseWindowsService()
.ConfigureServices((hostContext, services) =>
{
services.AddHostedService<JLinkHostService>();
});
}
}
[ServiceContractAttribute]
public interface IResponseService
{
[OperationContractAttribute]
bool ResponseMessage(string sTermID, string sRespMsg);
}
public class ResponseService : IResponseService
{
public bool ResponseMessage(string sTermID, string sRespMsg)
{
string filePath = $"{c:\test"}\\{DateTime.Now.ToString("yyyy -MM-dd_HHmmssfff")}.txt";
System.IO.File.WriteAllText(filePath, $"{sTermID}\n\n{sRespMsg}");
return true;
}
}
public class HostService : BackgroundService
{
public override Task StartAsync(CancellationToken cancellationToken)
{
return base.StartAsync(cancellationToken);
}
public override Task StopAsync(CancellationToken cancellationToken)
{
return base.StopAsync(cancellationToken);
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
ChannelFactory<IResponseService> factory = null;
try
{
Binding binding = new BasicHttpBinding();
EndpointAddress respAddress = new EndpointAddress("http://localhost:8000/response.svc");
factory = new ChannelFactory<IResponseService>(binding, respAddress);
IResponseService channel = factory.CreateChannel();
// Test service proxy
channel.ResponseMessage("test", "test");
while (!stoppingToken.IsCancellationRequested)
{
// Host background tasks happen here
await Task.Delay(Int32.Parse(GetCfgValue("AppSettings:pollingIntervalMilli")), stoppingToken);
}
}
catch (Exception ex)
{
Log.Fatal(ex.ToString());
}
finally
{
if(factory != null)
factory.Close();
}
}
}
}
I have a signalR hub that needs to be able to receive an event from a client and then notify all other clients connected to the hub.
Is that possible?
I want my 'hub' application to be able to receive messages and send them. I can only figure out how to do the sending of messages. Here is what I have now:
Application 1-- Hub
Startup class:
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
services.AddSignalR().AddHubOptions<EventsHub>(options =>
{
options.HandshakeTimeout = TimeSpan.FromMinutes(5);
options.EnableDetailedErrors = true;
});
services.AddTransient(typeof(BusinessLogic.EventsBusinessLogic));
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
});
app.UseSignalR((configure) =>
{
configure.MapHub<EventsHub>("/hubs/events", (options) =>
{
});
});
}
Set Up of the Hub in Application 1
public class EventsHub : Hub
{
public EventsHub()
{
}
public override Task OnConnectedAsync()
{
if (UserHandler.ConnectedIds.Count == 0)
{
//Do something on connect
}
UserHandler.ConnectedIds.Add(Context.ConnectionId);
Console.WriteLine("Connection:");
return base.OnConnectedAsync();
}
public override async Task OnDisconnectedAsync(Exception exception)
{
//Do something on Disconnect
}
public static class UserHandler
{
public static HashSet<string> ConnectedIds = new HashSet<string>();
}
}
BusinessLogic:
public class EventsBusinessLogic
{
private readonly IHubContext<EventsHub> _eventsHub;
public EventsBusinessLogic(IHubContext<EventsHub> eventsHub)
{
_eventsHub = eventsHub;
}
public async Task<Task> EventReceivedNotification(ProjectMoonEventLog eventInformation)
{
try
{
await _eventsHub.Clients.All.SendAsync("NewEvent", SomeObject);
}
catch (Exception e)
{
throw new Exception(e.Message);
}
}
}
In the second application, that listens for events or messages from the hub:
Startup.cs
private static void ConfigureAppServices(IServiceCollection services, string Orale, string Sql)
{
services.Configure<CookiePolicyOptions>(options =>
{
options.CheckConsentNeeded = context => true;
options.MinimumSameSitePolicy = SameSiteMode.None;
});
services.AddOptions();
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
//set up of singletons and transients
services.AddHostedService<Events.EventingHubClient>();
}
The ClientHub to connect to application 1:
public class EventingHubClient : IHostedService
{
private HubConnection _connection;
public EventingHubClient()
{
_connection = new HubConnectionBuilder()
.WithUrl("http://localhost:61520/hubs/events")
.Build();
_connection.On<Event>("NewEvent",
data => _ = EventReceivedNotification(data));
}
public async Task<Task> EventReceivedNotification(Event eventInformation)
{
try
{
//Do something when the event happens
return Task.CompletedTask;
}
catch (Exception e)
{
throw new Exception(e.Message);
}
}
public async Task StartAsync(CancellationToken cancellationToken)
{
// Loop is here to wait until the server is running
while (true)
{
try
{
await _connection.StartAsync(cancellationToken);
Console.WriteLine("Connected");
break;
}
catch (Exception e)
{
Console.WriteLine(e.Message);
await Task.Delay(100);
}
}
}
public Task StopAsync(CancellationToken cancellationToken)
{
return _connection.DisposeAsync();
}
}
This works, but now I want application 2 to be able to send a message to application 1? So I need a similar piece of code as in the EventsBusinessLogic class in application2 to send messages to application 1.
I hope this is clear enough? Is this the purpose of SignalR?
Please refer to signalR documentation signalR documentation for .net client
I guess in your Hub method like this
public async Task SendTransaction(Transaction data)
{
await Clients.All.SendAsync("TransactionReceived", data);
}
Then add methods in client side
in constructor add
connection.On<Transaction>("TransactionReceived", (data) =>
{
this.Dispatcher.Invoke(() =>
{
var transactionData = data;
});
});
and then SendTransaction expected on server
private async void SendTransaction(Transaction data)
{
try
{
await connection.InvokeAsync("SendTransaction", data);
}
catch (Exception ex)
{
//
throw
}
}