Browse Source

Add Basket handler and subscription to events. Change in EventBus to use broker and one message queue per microservice http://www.rabbitmq.com/tutorials/tutorial-four-dotnet.html

pull/126/head
dsanz 8 years ago
parent
commit
5b38a49f11
10 changed files with 136 additions and 65 deletions
  1. +4
    -0
      src/Services/Basket/Basket.API/Basket.API.csproj
  2. +25
    -0
      src/Services/Basket/Basket.API/Events/CatalogPriceChangedHandler.cs
  3. +6
    -0
      src/Services/Basket/Basket.API/Startup.cs
  4. +1
    -1
      src/Services/Catalog/Catalog.API/Controllers/CatalogController.cs
  5. +10
    -9
      src/Services/Common/Infrastructure/Catalog/CatalogPriceChanged.cs
  6. +0
    -14
      src/Services/Common/Infrastructure/Catalog/CatalogPriceChangedHandler.cs
  7. +85
    -38
      src/Services/Common/Infrastructure/EventBus.cs
  8. +1
    -2
      src/Services/Common/Infrastructure/IIntegrationEvent.cs
  9. +3
    -1
      src/Services/Common/Infrastructure/IIntegrationEventHandler.cs
  10. +1
    -0
      src/Services/Common/Infrastructure/Infrastructure.csproj

+ 4
- 0
src/Services/Basket/Basket.API/Basket.API.csproj View File

@ -39,6 +39,10 @@
<PackageReference Include="Swashbuckle" Version="6.0.0-beta902" /> <PackageReference Include="Swashbuckle" Version="6.0.0-beta902" />
</ItemGroup> </ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\Common\Infrastructure\Infrastructure.csproj" />
</ItemGroup>
<ItemGroup> <ItemGroup>
<None Update="Dockerfile"> <None Update="Dockerfile">
<CopyToOutputDirectory>Always</CopyToOutputDirectory> <CopyToOutputDirectory>Always</CopyToOutputDirectory>


+ 25
- 0
src/Services/Basket/Basket.API/Events/CatalogPriceChangedHandler.cs View File

@ -0,0 +1,25 @@
using Microsoft.eShopOnContainers.Services.Basket.API.Model;
using Microsoft.eShopOnContainers.Services.Common.Infrastructure;
using Microsoft.eShopOnContainers.Services.Common.Infrastructure.Catalog;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace Basket.API.Events
{
public class CatalogPriceChangedHandler : IIntegrationEventHandler<CatalogPriceChanged>
{
private readonly IBasketRepository _repository;
public CatalogPriceChangedHandler()
{
//_repository = repository;
}
public void Handle(CatalogPriceChanged @event)
{
}
}
}

+ 6
- 0
src/Services/Basket/Basket.API/Startup.cs View File

@ -13,6 +13,9 @@ using Microsoft.Extensions.Options;
using System.Net; using System.Net;
using Swashbuckle.Swagger.Model; using Swashbuckle.Swagger.Model;
using Microsoft.eShopOnContainers.Services.Basket.API.Auth.Server; using Microsoft.eShopOnContainers.Services.Basket.API.Auth.Server;
using Microsoft.eShopOnContainers.Services.Common.Infrastructure;
using Microsoft.eShopOnContainers.Services.Common.Infrastructure.Catalog;
using Basket.API.Events;
namespace Microsoft.eShopOnContainers.Services.Basket.API namespace Microsoft.eShopOnContainers.Services.Basket.API
{ {
@ -76,6 +79,9 @@ namespace Microsoft.eShopOnContainers.Services.Basket.API
}); });
services.AddTransient<IBasketRepository, RedisBasketRepository>(); services.AddTransient<IBasketRepository, RedisBasketRepository>();
var eventBus = new EventBus();
services.AddSingleton<IEventBus>(eventBus);
eventBus.Subscribe<CatalogPriceChanged>(new CatalogPriceChangedHandler());
} }
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline. // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.


+ 1
- 1
src/Services/Catalog/Catalog.API/Controllers/CatalogController.cs View File

@ -107,7 +107,7 @@ namespace Microsoft.eShopOnContainers.Services.Catalog.API.Controllers
//hook to run integration tests until POST methods are created //hook to run integration tests until POST methods are created
if (catalogTypeId.HasValue && catalogTypeId == 1) if (catalogTypeId.HasValue && catalogTypeId == 1)
{ {
_eventBus.Publish(new CatalogPriceChanged());
_eventBus.Publish(new CatalogPriceChanged(2, 10.4M));
} }
return Ok(model); return Ok(model);


+ 10
- 9
src/Services/Common/Infrastructure/Catalog/CatalogPriceChanged.cs View File

@ -6,15 +6,16 @@ namespace Microsoft.eShopOnContainers.Services.Common.Infrastructure.Catalog
{ {
public class CatalogPriceChanged : IIntegrationEvent public class CatalogPriceChanged : IIntegrationEvent
{ {
private readonly string _eventName = "catalogpricechanged";
public string Message { get { return "CatalogPriceChanged here!!"; } }
public string Name {
get
{
return _eventName;
}
}
public int ItemId { get; private set; }
public decimal NewPrice { get; private set; }
public string Message { get { return "CatalogPriceChanged!!"; } }
}
public CatalogPriceChanged(int itemId, decimal newPrice)
{
ItemId = itemId;
NewPrice = newPrice;
}
}
} }

+ 0
- 14
src/Services/Common/Infrastructure/Catalog/CatalogPriceChangedHandler.cs View File

@ -1,14 +0,0 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace Microsoft.eShopOnContainers.Services.Common.Infrastructure.Catalog
{
public class CatalogPriceChangedHandler : IIntegrationEventHandler<CatalogPriceChanged>
{
public void Handle(CatalogPriceChanged @event)
{
throw new NotImplementedException();
}
}
}

+ 85
- 38
src/Services/Common/Infrastructure/EventBus.cs View File

@ -1,40 +1,47 @@
 
using Microsoft.eShopOnContainers.Services.Common.Infrastructure.Catalog; using Microsoft.eShopOnContainers.Services.Common.Infrastructure.Catalog;
using Newtonsoft.Json;
using RabbitMQ.Client; using RabbitMQ.Client;
using RabbitMQ.Client.Events; using RabbitMQ.Client.Events;
using System; using System;
using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text; using System.Text;
namespace Microsoft.eShopOnContainers.Services.Common.Infrastructure namespace Microsoft.eShopOnContainers.Services.Common.Infrastructure
{ {
public class EventBus : IEventBus public class EventBus : IEventBus
{ {
private readonly Dictionary<string, List<IIntegrationEventHandler>> _handlers;
private readonly Dictionary<string, Tuple<IModel, IConnection>> _listeners;
private readonly string _brokerName = "event_bus";
private readonly Dictionary<string, List<IIntegrationEventHandler>> _handlers;
private readonly List<Type> _eventTypes;
private Tuple<IModel, IConnection> _connection;
private string _queueName;
public EventBus() public EventBus()
{ {
_handlers = new Dictionary<string, List<IIntegrationEventHandler>>(); _handlers = new Dictionary<string, List<IIntegrationEventHandler>>();
_listeners = new Dictionary<string, Tuple<IModel, IConnection>>();
_eventTypes = new List<Type>();
} }
public void Publish(IIntegrationEvent @event) public void Publish(IIntegrationEvent @event)
{ {
var eventName = @event.GetType().Name;
var factory = new ConnectionFactory() { HostName = "172.20.0.1" }; var factory = new ConnectionFactory() { HostName = "172.20.0.1" };
using (var connection = factory.CreateConnection()) using (var connection = factory.CreateConnection())
using (var channel = connection.CreateModel()) using (var channel = connection.CreateModel())
{
channel.QueueDeclare(queue: @event.Name,
durable: false,
exclusive: false,
autoDelete: false,
arguments: null);
{
channel.ExchangeDeclare(exchange: _brokerName,
type: "direct");
string message = ((CatalogPriceChanged)@event).Message;
string message = JsonConvert.SerializeObject(@event);
var body = Encoding.UTF8.GetBytes(message); var body = Encoding.UTF8.GetBytes(message);
channel.BasicPublish(exchange: "",
routingKey: @event.Name,
channel.BasicPublish(exchange: _brokerName,
routingKey: eventName,
basicProperties: null, basicProperties: null,
body: body); body: body);
} }
@ -50,30 +57,14 @@ namespace Microsoft.eShopOnContainers.Services.Common.Infrastructure
} }
else else
{ {
var factory = new ConnectionFactory() { HostName = "172.18.0.1" };
var connection = factory.CreateConnection();
var channel = connection.CreateModel();
channel.QueueDeclare(queue: eventName,
durable: false,
exclusive: false,
autoDelete: false,
arguments: null);
var consumer = new EventingBasicConsumer(channel);
consumer.Received += (model, ea) =>
{
var body = ea.Body;
var message = Encoding.UTF8.GetString(body);
};
channel.BasicConsume(queue: "hello",
noAck: true,
consumer: consumer);
;
_listeners.Add(eventName, new Tuple<IModel, IConnection>(channel, connection));
var channel = GetChannel();
channel.QueueBind(queue: _queueName,
exchange: _brokerName,
routingKey: eventName);
_handlers.Add(eventName, new List<IIntegrationEventHandler>()); _handlers.Add(eventName, new List<IIntegrationEventHandler>());
_handlers[eventName].Add(handler); _handlers[eventName].Add(handler);
_eventTypes.Add(typeof(T));
} }
} }
@ -88,13 +79,69 @@ namespace Microsoft.eShopOnContainers.Services.Common.Infrastructure
if (_handlers[eventName].Count == 0) if (_handlers[eventName].Count == 0)
{ {
_handlers.Remove(eventName); _handlers.Remove(eventName);
var eventType = _eventTypes.Single(e => e.Name == eventName);
_eventTypes.Remove(eventType);
_connection.Item1.QueueUnbind(queue: _queueName,
exchange: _brokerName,
routingKey: eventName);
if (_handlers.Keys.Count == 0)
{
_queueName = string.Empty;
_connection.Item1.Close();
_connection.Item2.Close();
}
}
}
}
var connectionItems =_listeners[eventName];
_listeners.Remove(eventName);
private IModel GetChannel()
{
if (_connection != null)
{
return _connection.Item1;
}
else
{
var factory = new ConnectionFactory() { HostName = "172.20.0.1" };
var connection = factory.CreateConnection();
var channel = connection.CreateModel();
connectionItems.Item1.Close();
connectionItems.Item2.Close();
channel.ExchangeDeclare(exchange: _brokerName,
type: "direct");
if (string.IsNullOrEmpty(_queueName))
{
_queueName = channel.QueueDeclare().QueueName;
} }
var consumer = new EventingBasicConsumer(channel);
consumer.Received += (model, ea) =>
{
var eventName = ea.RoutingKey;
if (_handlers.ContainsKey(eventName))
{
var message = Encoding.UTF8.GetString(ea.Body);
Type eventType = _eventTypes.Single(t => t.Name == eventName);
var integrationEvent = JsonConvert.DeserializeObject(message, eventType);
var handlers = _handlers[eventName];
var concreteType = typeof(IIntegrationEventHandler<>).MakeGenericType(eventType);
foreach (var handler in handlers)
{
concreteType.GetMethod("Handle").Invoke(handler, new object[] { integrationEvent });
}
}
};
channel.BasicConsume(queue: _queueName,
noAck: true,
consumer: consumer);
_connection = new Tuple<IModel, IConnection>(channel, connection);
return _connection.Item1;
} }
} }
} }


+ 1
- 2
src/Services/Common/Infrastructure/IIntegrationEvent.cs View File

@ -5,7 +5,6 @@ using System.Text;
namespace Microsoft.eShopOnContainers.Services.Common.Infrastructure namespace Microsoft.eShopOnContainers.Services.Common.Infrastructure
{ {
public interface IIntegrationEvent public interface IIntegrationEvent
{
string Name { get; }
{
} }
} }

+ 3
- 1
src/Services/Common/Infrastructure/IIntegrationEventHandler.cs View File

@ -10,5 +10,7 @@ namespace Microsoft.eShopOnContainers.Services.Common.Infrastructure
void Handle(TIntegrationEvent @event); void Handle(TIntegrationEvent @event);
} }
public interface IIntegrationEventHandler { }
public interface IIntegrationEventHandler
{
}
} }

+ 1
- 0
src/Services/Common/Infrastructure/Infrastructure.csproj View File

@ -5,6 +5,7 @@
<RootNamespace>Microsoft.eShopOnContainers.Services.Common.Infrastructure</RootNamespace> <RootNamespace>Microsoft.eShopOnContainers.Services.Common.Infrastructure</RootNamespace>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Newtonsoft.Json" Version="9.0.1" />
<PackageReference Include="RabbitMQ.Client" Version="4.1.1" /> <PackageReference Include="RabbitMQ.Client" Version="4.1.1" />
</ItemGroup> </ItemGroup>
</Project> </Project>

Loading…
Cancel
Save