MassTransit 探究初步

来源:互联网 发布:python split空格分割 编辑:程序博客网 时间:2024/05/21 09:33

1.  背景

作为SOA基础设施,企业服务总线(ESB)是一个具有高分布性、事件驱动服务的SOA架构,是当前企业集成的主流框架。

2.  简介

网站:http://masstransit-project.com/

 

MassTransit (MT) is a framework forcreating distributed applications on the .Net platform. MT provides the abilityto subscribe to messages by type and then connect different processing nodesthough message subscriptions building a cohesive mesh of services.

 

主要特性:

l  Bus architecture

l  Sagas

l  Exception management

l  Transactions

l  Serialization

l  Headers

l  Consumer lifecycle

l  Built on top of Rabbit Mq

l  IOC support

 

授权:

基于Apache 2.0,可以使用在任何环境中。

3.  架构

MassTransit在消息队列(MQ)之上构建了消息总线机制,封装了对消息队列的操作,以及其它的组件,比如序列化、日志、Saga、持久化等。

 

下图为传入消息处理管道模型:

 

4.  整体分析

MassTransit的目标是作为消息机制的抽象框架,因此,它本身并不具体实现MQ,而是通过集成其它MQ产品来作为其通信层。目前官方已集成的MQ产品有MSMQ、RabbitMQ。其它非官方补充了ActiveMQ。

MassTransit在MQ之上添加了Sagas、多线程、异常处理、事务、序列化、消息头(Header)、消息使用者生命周期管理、路由、Rx(Reactive Extension 反应式扩展)集成、NHibernate集成、调试、跟踪、日志输出、加密、定时服务等。

5.  特性分析

5.1.  声明式配置

MassTransit本身使用了许多优秀的设计,比如对MSMQ、RabbitMQ的使用,通过在Bus构造配置中调用UseMSMQ()或UseRabbitMQ()来声明式的决定。

5.2.  通过扩展方法隐藏具体实现

通过分析源码,可以发现UseXXX函数是通过定义在MassTransit.Transports.MSMQ.dll和MassTransit.Transports.RabbitMq.dll中的扩展方法实现的,这样当用户引用相应的DLL时,方法调用才会开放给用户。即用户选择具体使用哪种Transport时,是通过引用相应的DLL来决定的。DLL引用入项目后,具体MQ产品特定的配置,函数等方法也附加到了基类对象中。这样可以避免在基类中定义大量子类特定接口

MassTransit所有扩展组件均是采用这种方法,比如日志(Logging)组件。

6.  入门

首先需要定义消息:

public class YourMessage { public string Text { get; set; } }

消息总线的创建可以通过Bus类静态方法Initialize进行,传入相应的配置方法,如下所示:

 

Bus.Initialize(sbc =>{    sbc.UseMsmq();    sbc.VerifyMsmqConfiguration();    sbc.UseMulticastSubscriptionClient();    sbc.ReceiveFrom("msmq://localhost/test");    sbc.Subscribe(subs =>    {        subs.Handler<YourMessage>(msg => Console.WriteLine("From bus 1: " + msg.Text));    });});

初始化好Bus之后,就可以调用Publish方法发布消息了:

Bus.Instance.Publish(new YourMessage { Text = "Hi" });

在初始化中,通过sbc.Subscribe函数注册了一个Handler,其作用是在控制台中输出消息文本。

创建一个控制台应用程序,在main函数中完成初始化和发布,可以在随后的输出中看到消息文本。

7.  高级功能使用

7.1.  Subscription Service

参考: http://docs.masstransit-project.com/en/latest/overview/subscriptions.html

 

如果使用的消息队列不提供订阅共享功能,比如MSMQ,可以使用MT的SubscriptionService来实现此功能。在这种情况下,订阅信息的协调功能由一个中心管理器来完成。这个中心管理器就是运行在网路中的SubscriptionService的一个实例。每个消息总线(Bus)实例通过SubscriptionClient与其进行通信并交换订阅信息。

 

示例

 

创建Subscription Service:

var subscriptionBus = ServiceBusFactory.New(sbc =>{    sbc.UseStomp();    sbc.SetConcurrentConsumerLimit(1);    sbc.ReceiveFrom("stomp://localhost/mt_subscriptions");});var subscriptionSagas = new InMemorySagaRepository();var subscriptionClientSagas = new InMemorySagaRepository();var subscriptionService = new SubscriptionService(subscriptionBus, subscriptionSagas, subscriptionClientSagas);

创建Time Out Service:

var timeoutBus = ServiceBusFactory.New(sbc =>{    sbc.UseStomp();    sbc.UseControlBus();    sbc.ReceiveFrom("stomp://localhost/mt_timeouts");    sbc.UseSubscriptionService("stomp://localhost/mt_subscriptions");});var timeoutService = new TimeoutService(timeoutBus, new InMemorySagaRepository<TimeoutSaga>());timeoutService.Start();

创建应用Bus:

var bus = ServiceBusFactory.New(sbc{    sbc.UseStomp();    sbc.UseControlBus();    sbc.ReceiveFrom("stomp://localhost/your_awesome_application");    sbc.UseSubscriptionService("stomp://localhost/mt_subscriptions");});

说明:

当调用UseSubscriptionService时,隐式附加了一个SubscriptionClient到bus中。首先,SubscriptionClient会发送一个AddSubscriptionClient消息给SubscriptionService队列,然后开始监听订阅变更,随后发送AddScription/RemoveSubscription消息。通过这种方式,所有的更新将传播到应用中所有其它节点中。

 

7.2.  Request/Response

参考: http://docs.masstransit-project.com/en/latest/overview/request.html

 

通常使用Message Bus是一个应用发送一个请求(Request),另一个应用接收到这个请求后做出一些回应(Response)。比如工资进程请求税务进程执行某种所得税,在计算完成后,返回结果。

 

示例

 

消息定义:

请求和响应通过CorrelationId来进行关联。

public class BasicRequest : CorrelatedBy<Guid>{    public Guid CorrelationId { get;set; }    public string Text { get; set; }}public class BasicResponse : CorrelatedBy<Guid>{    public Guid CorrelationId { get; set; }    public string Text { get; set; }}

响应者:

简单的在请求信息上添加一个RESP前缀作为响应。注意BasicResponse.CorrelationId并没有设置,这个是由MT自动设置的。

public class Program{    public static void Main()    {        Bus.Initialize(sbc =>        {            sbc.UseMsmq();            sbc.VerifyMsmqConfiguration();            sbc.UseMulticastSubscriptionClient();            sbc.ReceiveFrom("msmq://localhost/message_responder");            sbc.Subscribe(subs=>            {                subs.Handler<BasicRequest>( (cxt, msg )=>                {                    cxt.Respond(new BasicResponse{Text = "RESP"+msg.Text});                });            });        });    }}


请求者:

提交请求并处理与原始请求相关联(通过CorrelationId)的响应,处理后取消对响应的订阅并结束这次请求。

public class Program{    public static void Main()    {        Bus.Initialize(sbc =>        {            sbc.UseMsmq();            sbc.VerifyMsmqConfiguration();            sbc.UseMulticastSubscriptionClient();            sbc.ReceiveFrom("msmq://localhost/message_requestor");        });        Bus.Instance.PublishRequest(new BasicRequest(), x =>        {            x.Handle<BasicResponse>(message => Console.WriteLine(message.Text));            x.SetTimeout(30.Seconds());        });    }}

上述代码会阻塞调用线程直到响应返回或超时。超时后会抛出RequestTimeoutException。如果响应处理发生异常,该异常会重新抛出给调用请求发出线程。

如果想使用异步处理,可以调用BeginPublishRequest,在处理结束后,调用EndRequest方法完成处理。

 

 

7.3.  Sagas

参考: http://docs.masstransit-project.com/en/latest/overview/saga.html

 

一个Saga是指由协调器(coordinator)管理的一个长事务。Sagas通常由一个事件(event)启动,Sagas对事件进行编排,并维护所有事务的状态。

 

7.3.1.  定义Sagas

在MT中有两种方法定义Saga:一种是用接口和类直接定义初始化、协调交互的各种消息,或可以被Saga示例观察到的各种消息;另一种是通过在类里面定义事件、状态、行为来定义一个状态机来实现。

 

7.3.2.  通过状态机实现Sagas

要通过状态机实现Sagas,必须从SagaStateMachine类继承:

public class AuctionSaga :    SagaStateMachine<AuctionSaga>,    ISaga{    static CombineSaga()    {        Define(() =>        {            // the state machine behavior is defined here        });    }    public Guid CorrelationId { get; set; }    public IServiceBus Bus { get; set; }}

然后定义各种状态:

public static State Initial { get; set; }public static State Completed { get; set; }public static State Open { get; set; }public static State Closed { get; set; }

定义与状态关联的事件:

public static Event<CreateAuction> Create { get; set; }public static Event<PlaceBid> Bid { get; set; }

定义消息:

public interface CreateAuction :    CorrelatedBy<Guid>{    string Title { get; }    string OwnerEmail { get; }    decimal OpeningBid { get; }}public interface PlaceBid{    Guid BidId { get; }    Guid AuctionId { get; }    decimal MaximumBid { get; }    string BidderEmail { get; }}

定义状态转换:

static AuctionSaga(){    Define(() =>    {        Initially(            When(Create));        During(Open,            When(Bid));    });}

定义事件发生时的行为:

static AuctionSaga(){    Define(() =>    {        Initially(            When(Create)                .Then((saga,message) =>                {                    saga.OpeningBid = message.OpeningBid;                    saga.OwnerEmail = message.OwnerEmail;                    saga.Title = message.Title;                })                .TransitionTo(Open));    });}//public decimal OpeningBid { get; set; }public string OwnerEmail { get; set; }public string Title { get; set; }

完整的定义:

static SupervisorSaga(){    Define(() =>    {        Initially(            When(Create)                .Then((saga,message) =>                {                    saga.PostalCode = message.PostalCode;                })                .Publish((saga,message) => new RequestPostalCodeDetails(saga.PostalCode))                .Publish((saga,message) => new RequestGeolocation(saga.PostalCode))                .TransitionTo(Waiting));        During(Waiting,            When(PostalCodeDetailsReceived)                .Then((saga,message) =>                {                    saga.City = message.City;                    saga.State = message.State;                }),            When(GeolocationReceived)                .Then((saga,message) =>                {                    saga.Latitude = message.Latitude;                    saga.Longitude = message.Longitude;                }));        Combine(PostalCodeDetailsReceived, GeolocationReceived)            .Into(ReadyToProceed, saga => saga.ReadyFlags);        During(Waiting,            When(ReadyToProceed)                .Then((saga,message) =>                {                    saga.Bus.Publish(new PostalCodeDetails(...));                })                .Complete());    });}//public int ReadyFlags { get; set; }public static Event<CreatePostalCodeDetailsRequest> Create { get; set; }public static Event<PostalCodeDetailsResponse> PostalCodeDetailsReceived { get; set; }public static Event<GeolocationResponse> GeolocationReceived { get; set; }public static Event ReadyToProceed { get; set; }

当Sagas定义好后,就可以在Bus中进行订阅:

public class Program{    public static void Main()    {        Bus.Initialize(sbc =>        {            sbc.ReceiveFrom("loopback://localhost/my_saga_bus");            sbc.Subscribe(subs =>            {                subs.Saga<AuctionSaga>(new InMemorySagaRepository<AuctionSaga>())                    .Permanent();            });        });    }}


原创粉丝点击