幸福框架:使用 “離線事件” 處理 “長事務”


背景

事件有兩種使用方式:一、作為傳統的監聽者模式以達到程序結構的解耦;二、作為消息機制以達到時間和空間上的解耦,如發送到遠程服務器、持久化到隊列等待。今天介紹如何使用“離線事件”處理“長事務”,這就需要把事件當做消息對待。

我理解的長事務是“執行時間長的任務,具體多少沒有標准”,如果希望在一個數據庫事務中完成這些長事務是不現實的,之前我的做法是換成存儲過程以降低事務的執行時間,以后我會采用“離線事件”。

離線事件:事件的一部分是同步執行,另外一部分會被異步的離線的在另外一台機器執行。

簡單示例

下載地址:OfflineEventStudy

項目結構

  1. Common:類庫,包含了事件和事件監聽者(同步事件監聽者和離線事件監聽者)。
  2. Console:離線事件執行程序,定時從隊列取事件並執行離線事件監聽者。
  3. Web:事件生成程序,發布事件並執行同步事件監聽者。

Common中的代碼

 1 using System;
 2 using System.Collections.Generic;
 3 using System.Linq;
 4 using System.Text;
 5 using System.Threading.Tasks;
 6 
 7 using Happy.Event;
 8 using Happy.Event.Offline;
 9 
10 namespace OfflineEventStudy.Common
11 {
12     [Persistable(1)]
13     public sealed class TestEvent : IEvent
14     {
15         public const string FilePath = @"E:\log.txt";
16 
17         public string Info { get; set; }
18     }
19 }

注意:Persistable特性會導致此事件支持離線訂閱。

 1 using System;
 2 using System.Collections.Generic;
 3 using System.Linq;
 4 using System.Text;
 5 using System.Threading.Tasks;
 6 using System.IO;
 7 using System.Threading;
 8 
 9 using Happy.Event;
10 using Happy.Event.Offline;
11 
12 namespace OfflineEventStudy.Common
13 {
14     public sealed class TestEventSubscriber : ISyncEventSubscriber<TestEvent>, IOfflineEventSubscriber<TestEvent>
15     {
16         void ISyncEventSubscriber<TestEvent>.Handle(TestEvent @event)
17         {
18             File.WriteAllText(TestEvent.FilePath, @event.Info + ":任務正在執行中!");
19         }
20 
21         void IOfflineEventSubscriber<TestEvent>.Handle(TestEvent @event)
22         {
23             Thread.Sleep(5000);
24 
25             File.WriteAllText(TestEvent.FilePath, @event.Info + ":任務完成!");
26         }
27     }
28 }

注意:同步接口會在Web中執行,離線接口會在Console中執行。

Console中的代碼

 1 using System;
 2 using System.Collections.Generic;
 3 using System.Linq;
 4 using System.Text;
 5 using System.Threading.Tasks;
 6 using System.Threading;
 7 
 8 using Happy.Event;
 9 using Happy.Event.Offline;
10 using Happy.Bootstrap;
11 using Happy.Infrastructure.Unity;
12 
13 namespace OfflineEventStudy.Console
14 {
15     class Program
16     {
17         static void Main(string[] args)
18         {
19             /****************************************啟動過程配置****************************************/
20 
21             BootstrapService
22              .Current
23              .IntegrateWithUnity() //使用Unity作為Ioc容器。
24              .UseRegisterServiceByConventionPlug() //使用按照約定注冊服務插件,會自動幫你執行注冊。
25                 .UseEventSubscriberRegister() //注冊所有的EventListener。
26                 .Done() //完成配置。
27              .Start(); //啟動。
28 
29             /****************************************啟動過程配置****************************************/
30 
31             var processor = new OfflineEventProcessor(OfflineEventQueueFactory.CreateMSMQOfflineEventQueue("OfflineEventStudy"));
32 
33             ThreadPool.QueueUserWorkItem((state) =>
34             {
35                 processor.Start();
36             }, null);
37 
38             System.Console.WriteLine("正在監聽任務");
39             System.Console.Read();
40         }
41     }
42 }

注意:這里會不斷的從隊列取事件並執行。

Web中的代碼

 1 using System;
 2 using System.Collections.Generic;
 3 using System.Linq;
 4 using System.Web;
 5 using System.Web.UI;
 6 using System.Web.UI.WebControls;
 7 using System.IO;
 8 
 9 using Happy.Event;
10 using OfflineEventStudy.Common;
11 
12 namespace OfflineEventStudy.Web
13 {
14     public partial class Default : System.Web.UI.Page
15     {
16         protected void Page_Load(object sender, EventArgs e)
17         {
18             if (this.IsPostBack)
19             {
20                 return;
21             }
22 
23             this.Display();
24         }
25 
26         protected void Button1_Click(object sender, EventArgs e)
27         {
28             EventPublisher.Current.Publish(new TestEvent { Info = "測試事件" });
29 
30             this.Display();
31         }
32 
33         protected void Button2_Click(object sender, EventArgs e)
34         {
35             this.Display();
36         }
37 
38         private void Display()
39         {
40             if (!File.Exists(TestEvent.FilePath))
41             {
42                 return;
43             }
44 
45             this.Label1.Text = File.ReadAllText(TestEvent.FilePath);
46         }
47     }
48 }

運行效果

備注

因為事件的一部分是離線執行的,所以不能保證實時一致性,關於最終一致性的問題這里不好多說,有興趣的朋友可以找http://www.cnblogs.com/netfocus/聊聊,netfocus最新力作enode完全放棄實時一致性,換來的是高並發。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM