MassTransit和服务结构状态服务?

时间:2016-12-22 16:33:47

标签: masstransit service-fabric-stateful

我一直试图想出一个网站的演示,该网站使用MassTransit和RabbitMQ将消息发布到Service Fabric上作为有状态服务运行的服务。

一切都很顺利,我的客户会发一条消息:

IBusControl bus = BusConfigurator.ConfigureBus();
Uri sendToUri = new Uri($"{RabbitMqConstants.RabbitMqUri}" + $"{RabbitMqConstants.PeopleServiceQueue}");
ISendEndpoint endPoint = await bus.GetSendEndpoint(sendToUri);

await endPoint.Send<ICompanyRequest>(new {CompanyId = id });

我的服务架构服务中的消费者定义如下:

        IBusControl busControl = Bus.Factory.CreateUsingRabbitMq(cfg =>
        {
            IRabbitMqHost host = cfg.Host(new Uri(RabbitMqConstants.RabbitMqUri), h =>
            {
                h.Username(RabbitMqConstants.UserName);
                h.Password(RabbitMqConstants.Password);
            });

            cfg.ReceiveEndpoint(host, RabbitMqConstants.PeopleServiceQueue, e =>
            {
                e.Consumer<PersonInformationConsumer>();
            });

        });

        busControl.Start();

这允许我在我的课程中使用该消息,我可以很好地处理它。当我们想要使用IReliableDictonary或IReliableQueue或任何需要引用从服务结构服务中的RunAsync函数运行的上下文时,就会出现问题。

所以我的问题是,我如何配置(可能)MassTransit在状态服务结构服务中工作哪些服务上下文本身的知识?

非常感谢提前。 麦克

更新 好的,如果我将注册例程指向我的消息使用者类(例如),我在这方面取得了一些进展:

ServiceRuntime.RegisterServiceAsync("ServiceType", context => new PersonInformationConsumer(context)).GetAwaiter().GetResult();
                ServiceEventSource.Current.ServiceTypeRegistered(Process.GetCurrentProcess().Id, typeof(PersonInformationConsumer).Name);

然后在我的消费者课程中,我可以执行以下操作:

    internal sealed class PersonInformationConsumer : StatefulService, IConsumer<ICompanyRequest>
{
    private static StatefulServiceContext _currentContext;

    #region Constructors

    public PersonInformationConsumer(StatefulServiceContext serviceContext) : base(serviceContext)
    {
        _currentContext = serviceContext;
    }

    public PersonInformationConsumer() : base(_currentContext)
    {
    }

我现在可以成功拨打服务信息:

ServiceEventSource.Current.ServiceMessage(this.Context, "Message has been consumed, request Id: {0}", context.Message.CompanyId);

我现在遇到的问题是尝试在IReliableDictionary上存储一些东西,这样做导致&#34;对象引用未设置为对象的实例&#34;错误:( ......任何想法都会受到赞赏(虽然现在可能不会读到新年!)

        public async Task Consume(ConsumeContext<ICompanyRequest> context)
    {

        ServiceEventSource.Current.ServiceMessage(this.Context, "Message has been consumed, request Id: {0}", context.Message.CompanyId);

        using (ITransaction tx = StateManager.CreateTransaction())
        {

            try
            {

                var myDictionary = await StateManager.GetOrAddAsync<IReliableDictionary<string, long>>("myDictionary");

这导致错误....帮助! :)

1 个答案:

答案 0 :(得分:1)

您需要做更多工作才能让MassTransit和有状态服务协同工作,这里有一些问题需要关注。

只有状态分区中的主服务器(n个分区中的n个主服务器)才能写入/更新有状态服务,所有副本在尝试写回任何状态时都会抛出异常。所以你需要处理这个问题,表面看起来很容易,直到你考虑到由于重新平衡集群,master可以在集群中移动,一般服务结构应用程序的默认设置就是关闭副本上的处理并仅在主服务器上运行工作。这一切都是通过RunAsync方法完成的(尝试一下,在RunAsync方法中使用noddy运行3个有状态服务,然后终止主服务器。)

还要考虑对数据进行分区,因为有状态服务可以扩展分区,您需要创建一种方法将数据分发到服务总线上的单独端点,可能有一个单独的队列只能侦听到给定的分区范围?假设您有UserCreated消息,您可以将其拆分为country UK转到分区1,US转到分区2等...

如果您只是希望获得基本启动和运行的功能,我将其限制为一个分区,并尝试将您的总线创建放在RunAsync中,并在取消令牌请求取消后关闭总线。

protected override async Task RunAsync(CancellationToken cancellationToken)
{
    var busControl = Bus.Factory.CreateUsingRabbitMq(cfg =>
    {
        IRabbitMqHost host = cfg.Host(new Uri(RabbitMqConstants.RabbitMqUri), h =>
        {
            h.Username(RabbitMqConstants.UserName);
            h.Password(RabbitMqConstants.Password);
        });

        cfg.ReceiveEndpoint(host, RabbitMqConstants.PeopleServiceQueue, e =>
        {
            // Pass in the stateful service context
            e.Consumer(c => new PersonInformationConsumer(Context));
        });

    });

    busControl.Start();

    while (true)
    {
        if(cancellationToken.CancellationRequested)
        {
            //Service Fabric wants us to stop
            busControl.Stop();

            cancellationToken.ThrowIfCancellationRequested();
        }

        await Task.Delay(TimeSpan.FromSeconds(1));
    }
}