03.03 使用EventBus + Redis發佈訂閱模式提升業務執行性能

前言

最近一直奔波於面試,面了幾家公司的研發。有讓我受益頗多的面試經驗,也有讓我感覺浪費時間的面試經歷~因為疫情原因,最近宅在家裡也沒事,就想著使用Redis配合事件總線去實現下具體的業務。

  • 需求一個簡單的電商,有幾個重要的需求點商品下單後TODO存儲訂單信息鎖定商品庫存消息推送商家端訂單支付後TODO存儲訂單支付信息商品庫存減少消息推送商家端會員積分調整

技術思路

這裡用控制檯實現上面的業務功能外,自行編寫一個基於C#反射特性的事件總線,方便具體業務事件的後續擴展,比如訂單支付後後續還要加會員消息推送啥的。使用Redis的發佈訂閱模式對事件處理進行異步化,提升執行性能。所以最終技術架構就是 事件總線+Redis發佈訂閱。

完成事件總線

這裡先不急著將上面的訂單、支付、會員 等進行建模。先將事件總線的架子搭好。首先需要理解事件總線在業務系統的目的是什麼。事件總線存在目的最重要的就是解耦

。我們需要實現的效果就是針對指定事件源對象觸發事件後,但凡註冊了該事件參數的事件處理類則開始執行相關代碼。

下圖可以看出我們的事件處理類均需要引用事件參數,所有事件處理類都是基於對事件參數處理的需求上來的。

使用EventBus + Redis發佈訂閱模式提升業務執行性能

但是!並不是意味創建了事件處理類就一定會去執行!能否執行除了取決於事件源的觸發外就是必須有一層註冊(也可稱映射)。在WinForm程序裡處處可見事件的綁定,如 this.button1.OnClick+=button1OnClick;那麼在這裡我將綁定事件放置到一個字典裡。C#的字典Dictionary是個key value的鍵值對數據集合,鍵和值都可以是任意數據類型。我們可以將事件處理類EventHandle和事件參數EventData作為鍵和值存儲到字典裡。在事件源觸發時根據EventData反向找出所有的EventHandle

思路就是這樣,開始編碼了。定義事件參數接口,後續具體業務的事件參數接口均要繼承它。

<code>+ View Code/<code>

需要一個事件處理接口,後續具體業務的事件處理接口均需要繼承它

<code>/// <summary>
/// 事件實現接口
/// /<summary>
public interface IEventHandle where T : IEventData
{
/// <summary>
/// 處理等級
/// 方便事件總線觸發時候可以有序的執行相應
/// /<summary>
/// <returns>
int ExecuteLevel { get; }

/// <summary>

/// 事件執行
/// /<summary>
/// <param>事件參數
void Execute(T eventData);
}
/<code>

現在已經將事件參數和事件處理都抽象出來了,接下來是要實現上面說的註冊容器的實現了。

<code>+ View Code/<code>

實現上面的接口

<code>+ View Code/<code>

根據上面代碼可以看出來,我們存儲到Dictionary內的是Type類型,GetEventHandleList方法最終獲取的是一個List<type>的集合。我們需要在下面創建的EventBus類裡循環List<type>並且執行這個事件處理類的Execute方法。/<type>/<type>

實現EventBus

<code>+ View Code/<code>

上面可以看到,我們的事件總線是支持對綁定的事件處理對象進行有序處理,需要依賴下面這個枚舉

<code>/// <summary>
/// 排序類型
/// /<summary>
public enum SortType
{
/// <summary>
/// 升序
/// /<summary>
Asc = 1,
/// <summary>

/// 降序
/// /<summary>
Desc = 2
}/<code>

好了,至此,我們的簡易版的事件總線就出來了~ 接下來就是去建模、實現相應的事件參數和事件處理類了。創建訂單模型:

<code>+ View Code/<code>

創建訂單下單事件參數

<code>+ View Code/<code>

OK~接下來就是實現我們上面需求上的那些功能了。

  • 存儲訂單信息
  • 鎖定商品庫存
  • 消息推送商家端
    這裡我不實現存儲訂單信息的事件處理對象,我默認此業務必須同步處理,至於後面兩個則可以採取異步處理。通過下面代碼創建相應的事件處理類。
    訂單創建事件之消息推送商家端處理類。
<code>+ View Code/<code>

訂單創建消息之鎖定庫存處理類

<code>/// <summary> 

/// 訂單創建事件 鎖定庫存 處理類
/// /<summary>
public class OrderCreateEventStockLockHandle : IEventHandle<iordercreateeventdata>
{
public int ExecuteLevel { get; private set; }

public OrderCreateEventStockLockHandle()
{
Console.WriteLine($"創建OrderCreateEventStockLockHandle對象");
this.ExecuteLevel = 1;
}


public void Execute(IOrderCreateEventData eventData)
{
Thread.Sleep(1000);
Console.WriteLine($"執行訂單創建事件之庫存鎖定!訂單ID:{eventData.Order.Id.ToString()},商品名稱:{eventData.Order.ProductName}");
}
}/<iordercreateeventdata>/<code>

OK~ 到main方法下開始執行訂單創建相關代碼。

<code>static void Main(string[] args)
{

Guid userId = Guid.NewGuid();
Stopwatch stopwatch = new Stopwatch();
stopwatch.Start();
EventBus eventBus = new EventBus();
eventBus.EventRegister(typeof(OrderCreateEventNotifyHandle), typeof(OrderCreateEventData));
eventBus.EventRegister(typeof(OrderCreateEventStockLockHandle), typeof(OrderCreateEventData));
var order = new Order.OrderModel()
{
CreateTime = DateTime.Now,
Id = Guid.NewGuid(),
Money = (decimal)300.00,
Number = 1,
ProductName = "鮮花一束",
UserId = userId
};
Console.WriteLine($"模擬存儲訂單");
Thread.Sleep(1000);
eventBus.Trigger(new OrderCreateEventData()
{

Order = order
});
stopwatch.Stop();
Console.WriteLine($"下單總耗時:{stopwatch.ElapsedMilliseconds}毫秒");
Console.ReadLine();
}/<code>

至此,我們採取事件總線的方式成功將需求實現了,執行後結果如下:

使用EventBus + Redis發佈訂閱模式提升業務執行性能

可以看到我們的下單總耗時是3038毫秒,如您所見,我們解決了代碼的耦合性但是沒有解決代碼的執行效率。下一章,將我們的Redis的發佈訂閱模式再加入進來,看是否能改善我們的代碼執行效率~~


分享到:


相關文章: