24、Wiring up EasyNetQ with TopShelf and Windsor 让EasyNetQ、TopShelf 和Windsor 一起使用

围绕消息总线架构一个系统需要编写许多小而集中的组件,这些组件趴在总线上等待它们所关心的消息。这些应用程序最好是写成window服务,而实现window服务的最好方式是使用 TopShelf开源框架,用TopShelf写window服务超简单。这个开源库脱胎于优秀的MassTransit 项目。你可以简单地创建一个控制台应用程序,然后从NuGet安装TopShelf,然后调用API描述你的服务。

 

一个IoC(DI)容器应该是应用程序的核心(除了最简单的.NET程序)。如果你还没用过IoC容器,那你真的该好好看看这个文章《read this for why you should》。有相当多的IoC容器可供选择,我们用的是Castle Windsor.

 

本章我们展示一下如何让EasyNetQ、TopShelf 和Windsor 一起使用。

 

IoC的口号是“任何使用了IoC容器的应用,都应该只把IoC库引用到2个地方”:

一是创建IoC容器的地方,容器存在于应用程序的整个生命周期里。二是从容器中解析根服务。所有其他依赖项都是由容器本身默默提供的。这就是为啥IoC容器这么拽这么必要的原因。

依照这个法则,在Main()方法中我们创建IoC容器,解析我们应用程序的根服务。

public class Program
{
    public static void Main(string[] args)
    {
//创建IoC容器
var container = new WindsorContainer().Install(FromAssembly.This()); HostFactory.Run(x => { x.Service<IVaultService>(s => { s.ConstructUsing(name => container.Resolve<IVaultService>()); s.WhenStarted(tc => tc.Start()); s.WhenStopped(tc => { tc.Stop(); container.Release(tc); container.Dispose(); }); }); x.RunAsLocalSystem(); x.SetDescription("Vault service."); x.SetDisplayName("Vault.Service"); x.SetServiceName("Vault.Service"); }); } }

Windsor的一个基本原则是,你必须释放你解析的任何组件。Windsor跟踪任何实现了IDisposable的组件,并确保不管组件在依赖关系图的什么地方被解析,Dispose方法都会被调用。但是你需要调用Release来正确地实现它。

“tc”变量是IVaultService的实例,它在“ConstructUsing”调用中得到解析,因此我们可以在Release调用中使用它。

EasyNetQ呢?EasyNetQ的口号是:你应该创建单个IBus的实例存在于整个应用程序的生命周期中。现在我们可以在Main() 创建IBus实例,然后是TopShelf 的配置,然后是创建IoC容器。但是由于我们希望IoC容器能够管理应用程序中的所有组件的生命周期。

首先,让我们创建一个简单的工厂方法,该方法获取EasyNetQ的连接字符串,并创建一个IBus的新实例

public class BusBuilder
{
    public static IBus CreateMessageBus()
    {
        var connectionString = ConfigurationManager.ConnectionStrings["easynetq"];
        if (connectionString == null || connectionString.ConnectionString == string.Empty)
        {
            throw new VaultServiceException("easynetq connection string is missing or empty");
        }

        return RabbitHutch.CreateBus(connectionString.ConnectionString);
    }
}

 

现在,我们可以编写 IWindsorInstaller 注册我们的服务:

public class Installer : IWindsorInstaller
{
    public void Install(IWindsorContainer container, IConfigurationStore store)
    {
        container.Register(
            Component.For<IVaultService>().ImplementedBy<VaultService>().LifestyleTransient(),
            Component.For<IBus>().UsingFactoryMethod(BusBuilder.CreateMessageBus).LifestyleSingleton()
            );
    }
}

请注意,我们告诉Windsor 使用我们的工厂方法UsingFactoryMethod,而不是“Instance”来创建IBus实例。Instance方法告诉Windsor ,我们自己将为服务的生命周期承担责任,但是我们想要的是让Windsor 在应用程序关闭时自动调用Dispose。

UsingFactoryMethod 告诉Windsor 应该去管理IBus的生命周期。我们将其声明为LifestyleSingleton ,因为我们希望在应用程序的整个生命周期中只使用一个IBus的实例。

 

现在,我们可以在我们的IVaultService实现中引用IBus:

public interface IVaultService   //我们的一个服务
{
    void Start();
    void Stop();
}

public class VaultService : IVaultService
{
    private readonly IBus bus;

    public VaultService(IBus bus)
    {
        this.bus = bus;
    }

    public void Start()
    {
        bus.SubscribeAsync<MyMessage>("vault_handler", msg => {  });

    }

    public void Stop()
    {
        // shutdown code
    }
}

在这里,我们只是在VaultService服务的开始方法中订阅MyMessage。我可能还会有一个IMyMessageHandler服务,由IVaultService引用,来完成消息处理。

所以你看到了,一个简单的方法,可以把这三个优秀的开源项目一起使用。

 

几个例子:

EasyNetQ例子包括:Request/Response、Autosubcriber,使用Windsor IoC

https://bitbucket.org/philipogorman/createrequestservice/src

 

posted on 2017-12-07 17:30  困兽斗  阅读(346)  评论(0编辑  收藏  举报

导航