Rabbit MQ
Ćwiczenie 8 - Konfiguracja biblioteki RawRabbit
Przygotowanie projektów
Do wykonania tego ćwiczenia będziesz potrzebować stworzyć więcej niż jeden projekt. Stwórz dwa nowye projekt MVC. i jeden ClassLib. Projekty możesz nazwać odpowiednio:
- cw8-client (Aplikacja MVC)
- cw8-server (Aplikacja MVC)
- cw8-common (ClassLib)
Musisz pamiętać, aby tym dwóm projektom ustawić różne porty do uruchomienia. Inaczej te dwie aplikacji będą się "gryźć", w sensie ze będą zajmować sobie porty nawzajem, dlatego tylko jedna z nich wstanie. Aby przejść przez ten tutorial obie aplikacje muszą być up & running.
Zarówno w nowym jak i starymWe wszystkich stworzonych projekcietach zainstaluj poniższe paczki nuget-a.
$ dotnet add package RawRabbit
$ dotnet add package RawRabbit.vNext
D Do aplikacji cw8-common dodaj jeszcze jedną paczkę nuget:
$ dotnet add package Microsoft.AspNetCore.Hosting.Abstractions
Następnie dodaj referencję do projektu cw8-common w projektach cw8-client i cw8-server.
Konfiguracja połączenia z RabbitMQ
W aplikacjach cw8-client i cw8-server do pliku appsettings.json dodaj konfigurację potrzebną do połączenie się z Rabbit-em.
"rabbitmq": {
"Username": "guest",
"Password": "guest",
"VirtualHost": "/",
"Port": 5672,
"Hostnames": [ "localhost" ],
"RequestTimeout": "00:00:10",
"PublishConfirmTimeout": "00:00:01",
"RecoveryInterval": "00:00:10",
"PersistentDeliveryMode": true,
"AutoCloseConnection": true,
"AutomaticRecovery": true,
"TopologyRecovery": true,
"Exchange": {
"Durable": true,
"AutoDelete": true,
"Type": "Topic"
},
"Queue": {
"AutoDelete": true,
"Durable": true,
"Exclusive": true
}
}
W obu projektach, w metodach ConfigureServices, dodaj inicjalizację clienta RabbitMQ i dodawanie go jako Singletona do kontenera DI.
var section = Configuration.GetSection("rabbitmq");
var options = new RawRabbitConfiguration();
section.Bind(options);
var client = BusClientFactory.CreateDefault(options);
services.AddSingleton<IBusClient>(_ => client);
Stwórz interfejs, którym będziesz oznaczał przesyłane wiadomości
Definiowanie wiadomości
W projekcie cw8-common utwórz interfejs znacznikowy, którym posłuży do oznaczania klas, które chcemy przesyłaneć jako wiadomości przez szynę RabbitMQ:
public interface IMessage { }
Przejdź do stworzenia przykładowego obiektu wiadomości:, ta klasa nie musimy być bardzo rozbudowana. Wystarczy nam pojedyncze pole Message typu string.
public class SendMessage : IMessage
{
public string Message { get; }
public SendMessage(string message)
{
Message = message;
}
}
Obsługa Wiadomości
Następnie zdefiniuj generyczny interfejs do oznaczania klas, które będą obsługiwały wiadomości:
public interface IHandler<in T> where T : IMessage
{
Task HandleAsync(T message, CancellationToken token);
}
Od razu dodajW projekcie cw8-server utwórz klasę, która obsłuży wcześniej zdefiniowana wiadomość:, najlepiej utwórz katalog Handlers gdzie będziesz przechowywać wszystkie klasy odpowiedzialne za przetwarzanie wiadomości pobranych przez szynę wiadomości.
public class SendMessageHandler : IHandler<SendMessage>
{
public async Task HandleAsync(SendMessage @event, CancellationToken token)
{
Console.WriteLine($"Receive: {@event.Message}");
return Task.CompletedTask;
}
}
W projekcie cw8-server, który będzie odbierał tą wiadomości, musisz zarejestrować ten handler w kontenerze DI
services.AddTransient<IHandler<SomeendMessage>>, SendMessageHandler>();
Zdefiniuj e### Integracja z .NET Core MVC
Wróć na chwilę do projektu cw8-common. Tutaj utwórz Extension mMethod do la interfejsu IApplicationBuilder, który pozwoli Ci na sprawę dodawania handlerow.na automatyczne uruchamianiae handlerow.-ów, dla przychodzących wiadomości.
public static class ApplicationBuilderExtensions
{
public static IApplicationBuilder AddHandler<T>(this IApplicationBuilder app, IBusClient client)
where T : IMessage
{
if (!(app.ApplicationServices.GetService(typeof(IHandler<T>)) is IHandler<T> handler))
throw new NullReferenceException();
client
.SubscribeAsync<T>(async (msg, context) =>
{
await handler.HandleAsync(msg, CancellationToken.None);
});
return app;
}
public static IApplicationBuilder AddHandler<T>(this IApplicationBuilder app)
where T : IMessage
{
if (!(app.ApplicationServices.GetService(typeof(IBusClient)) is IBusClient busClient))
throw new NullReferenceException();
return AddHandler<T>(app, busClient);
}
}
Dzięki temu, w metodzie Configure, w pliku Startup.cs, będziesz mógł się zapisać na wiadomości w postaci:
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
// ...
app.UseMvc();
app.AddHandler<SomeMessage>()
.AddHandler<SomeAnotherMessage>()
.AddHandler<MayberOneMoreMessage>();
}
Na koniec, w drugim projekcie, wyślij wiadomości z kontrolera. Dla przykładu:
public class MessageController : Controller
{
private readonly IBusClient _client;
public ProfilesMessageController(IBusClient client)
{
_client = client;
}
[HttpGet]
[Route("Creat/sendMessage")]
public async Task<IActionResult> Create()
{
await _client.PublishAsync(new SomeMessage("Test Message"));
return Accepted();
}
}
Ostatnie uwagi
Jeżeli planujesz, aby połączenia webSocket były obsługiwane przez inną aplikację i dopiero potem wysyłane przez szynę RabbitMQ do serwisu (albo w drugą stronę, żeby wydzielić taki "notification service") poza scope aplikacji MVC, pamiętaj o odpowiednim ustawieniu nagłówków CORS. W lokalny środowisku będziesz używać aplikacji na różnych portach, co powoduje złamanie cors policy.
Dlatego w pliku Startup.cs w metodzie Configure dodaj poniższa konfiguracje middleware do obsługi CORS-a
app.UseCors(builder => builder
.WithOrigins(
"https://localhost:5001"
)
.AllowAnyMethod()
.AllowAnyHeader()
.AllowCredentials());
Pamiętaj tez ze nie można ze sobąpowinno się łączyć funkcji AllowAnyOrigin i AllowCredentials.