Web プロジェクトで使用する ServiceBus 実装として MassTransit を検討しています。
Request/Responseパターンで遊んでいますが、消費者がメッセージを受信して応答するまでに長い遅延が発生し、要求発行者が応答を処理します。場合によっては、応答がまったく返ってこないように見えることがあります (10 分間実行したままにしておくと、まだ応答が返されません)。応答でハンドル デリゲートが呼び出されるのを見たのは、30 秒のタイムアウト期間とタイムアウト例外がスローされた後だけです。この状況では、ハンドラー デリゲートに設定されたブレークポイントがヒットします。
セットアップは標準的なものです。Web アプリがコールバックで応答を処理するために、要求を発行する Web アプリ、要求を消費して応答を送信するコンソール アプリがあります。
Castle Windsor を使用しており、コンテナーは WebActivator を使用して Web プロジェクトで初期化されます。
[assembly: WebActivator.PreApplicationStartMethod(typeof(BootStrapper), "PreStart")]
[assembly: WebActivator.PostApplicationStartMethod(typeof(BootStrapper), "PostStart")]
[assembly: WebActivator.ApplicationShutdownMethodAttribute(typeof(BootStrapper), "Stop")]
namespace Web.App_Start
{
public static class BootStrapper
{
internal static IWindsorContainer Container { get; private set; }
public static void PreStart()
{
Container = new WindsorContainer().Install(FromAssembly.This());
}
public static void PostStart()
{
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
ApiConfig.Configure(Container);
MvcConfig.Configure(Container);
}
public static void Stop()
{
if (Container != null)
Container.Dispose();
}
}
}
Web アプリ プロジェクト(ASP.NET Web API プロジェクト) では、MassTransit の WindsorInstaller は次のようになります。
public class MassTransitInstaller : IWindsorInstaller
{
public void Install(IWindsorContainer container, IConfigurationStore store)
{
container.Register(AllTypes.FromThisAssembly().BasedOn<IConsumer>());
var bus = ServiceBusFactory.New(configurator =>
{
configurator.UseMsmq();
configurator.VerifyMsmqConfiguration();
configurator.UseMulticastSubscriptionClient();
configurator.ReceiveFrom("msmq://localhost/web");
configurator.EnableMessageTracing();
configurator.Subscribe(x => x.LoadFrom(container));
});
container.Register(Component.For<IServiceBus>().Instance(bus));
}
}
コンソール アプリ プロジェクトでは、WindsorInstaller は次のようになります。
public class MassTransitInstaller : IWindsorInstaller
{
public void Install(IWindsorContainer container, IConfigurationStore store)
{
container.Register(AllTypes.FromAssemblyContaining<BasicRequestCommandHandler>().BasedOn<IConsumer>());
var bus = ServiceBusFactory.New(configurator =>
{
configurator.UseMsmq();
configurator.VerifyMsmqConfiguration();
configurator.UseMulticastSubscriptionClient();
configurator.ReceiveFrom("msmq://localhost/console");
configurator.Subscribe(x => x.LoadFrom(container));
});
container.Register(Component.For<IServiceBus>().Instance(bus));
}
}
私はApiController
次のGETアクションメソッドを持っています
public class ExampleController : ApiController
{
private readonly IServiceBus _bus;
public HelloController(IServiceBus bus)
{
_bus = bus;
}
// GET api/hello?text={some text}
public Task<IBasicResponseCommand> Get(string text)
{
var command = new BasicRequestCommand {Text = text};
var tcs = new TaskCompletionSource<IBasicResponseCommand>();
_bus.PublishRequest(command, c =>
{
c.Handle<IBasicResponseCommand>(r =>
{
tcs.SetResult(r);
});
});
return tcs.Task;
}
}
BasicRequestCommand と BasicResponseCommand は次のようになります
public interface IBasicRequestCommand
{
Guid CorrelationId { get; set; }
string Text { get; set; }
}
public class BasicRequestCommand :
CorrelatedBy<Guid>, IBasicRequestCommand
{
public Guid CorrelationId { get; set; }
public string Text { get; set; }
public BasicRequestCommand()
{
CorrelationId = Guid.NewGuid();
}
}
public interface IBasicResponseCommand
{
Guid CorrelationId { get; set; }
string Text { get; set; }
}
public class BasicResponseCommand :
CorrelatedBy<Guid>, IBasicResponseCommand
{
public Guid CorrelationId { get; set; }
public string Text { get; set; }
}
そして、コンソール アプリで BasicRequestCommand に応答するハンドラー:
public class BasicRequestCommandHandler : Consumes<IBasicRequestCommand>.Context
{
public void Consume(IConsumeContext<IBasicRequestCommand> context)
{
Console.Out.WriteLine("received message text " + context.Message.Text);
context.Respond(new BasicResponseCommand { Text = "Hello " + context.Message.Text, CorrelationId = context.Message.CorrelationId });
}
}
このすべてをローカルで実行すると、要求/応答がせいぜい数秒程度になると予想していました。構成に何か不足していますか?
さらに、MassTransit を log4net に接続したいと考えました。Windsor の log4net ロギング機能を使用しており、web.config に log4net セクションがあります。これはILogger
、Windsor によって提供される実装 (および NHibernate のログ) ではすべて正常に機能しますが、これをログに使用するように MassTransit を構成する方法がドキュメントから明らかではありません。何か案は?