中文字幕精品亚洲无线码二区,国产黄a三级三级三级看三级,亚洲七七久久桃花影院,丰满少妇被猛烈进入,国产小视频在线观看网站

eShopOnContainers 知多少(shao)[6]:持久化(hua)事件日志

1. 引言

事件總線解決了微服務間如何基于集成事件進行異步通信的問題。然而只有事件總線正常運行,微服務之間基于事件的通信才得以運轉。
而現實情況是,總有這樣或那樣的問題,導致事件總線不穩定或不可用,比如:網絡中斷,系統斷電等等,這都可能導致微服務間的不一致性問題。
那如(ru)何(he)解決事件(jian)總線故障導致的不一致問題呢?

  1. 事件溯源
  2. 事件日志挖掘
  3. 發件箱模式

2. 問題

既然(ran)上(shang)面(mian)提(ti)到了(le)一致性問(wen)題,那具體的(de)問(wen)題是什(shen)么(me)(me)呢,在(zai)什(shen)么(me)(me)情況才會發生呢?我(wo)想我(wo)有(you)必要簡單舉例(li)。上(shang)代碼:

var oldPrice = item.Price;
item.Price = product.Price;
_context.CatalogItems.Update(item);
var @event = new ProductPriceChangedIntegrationEvent(item.Id, item.Price, oldPrice);
// Commit changes in original transaction
await _context.SaveChangesAsync();

// Publish integration event to the event bus
// (RabbitMQ or a service bus underneath)
_eventBus.Publish(@event);

當產品價格更改后,代碼將數據提交給數據庫,然后發布ProductPriceChangedIntegrationEvent 事件。
如果服務在數據庫更新后崩潰(奔潰發生在_context.SaveChangesAsync()代碼執行之后,但又發生在(zai)集成事(shi)件(jian)成功發布前),就會導(dao)致(zhi)本地(di)微服(fu)(fu)務價格已成功更新,但集成事(shi)件(jian)未發布的(de)問題。就會導(dao)致(zhi)目錄微服(fu)(fu)務中定義(yi)的(de)價格和顧(gu)客(ke)購物(wu)車(che)中緩存的(de)價格不一致(zhi)。

3. 分析問題

以上問題的關鍵在于是如何確保兩個獨立的操作的原子性。如果單從單體應用的角度來處理的話,我們完全是可以將他們放到同一個事務中去保證。然而在微服務中,就違背了其高可用的基本要求。因為一旦事件總線(xian)處于癱瘓(huan)狀態,那么整(zheng)個目錄微服(fu)務(wu)就不可(ke)用了(le)。這(zhe)種強制通過事務(wu)保證的一致(zhi)性,就引入了(le)太多的問題依(yi)賴。

如果從微(wei)服(fu)(fu)務(wu)的(de)角度來(lai)看(kan),每個微(wei)服(fu)(fu)務(wu)負責各自的(de)業務(wu)邏輯,對于目錄微(wei)服(fu)(fu)務(wu)來(lai)說,它的(de)關注(zhu)點(dian)是產(chan)品的(de)更新是否成功。至于借助事件總(zong)線通過異步事件實(shi)現(xian)微(wei)服(fu)(fu)務(wu)間的(de)通信,并(bing)不(bu)(bu)是其關注(zhu)點(dian)。這也(ye)就是關注(zhu)點(dian)分(fen)離。換句話說,產(chan)品的(de)更新不(bu)(bu)應該(gai)依賴外部狀態。在這里,外部狀態就是事件總(zong)線的(de)可用性(xing)。

你可能會說了(le),既然不允(yun)許通過強事務保證一致性,那么(me)如何解決一致性問題呢(好像繞了(le)半(ban)天又(you)回到(dao)了(le)原點)?

這里就要引入強一致性和最終一致性的概念了。
強一致性:也就是事務一致性,將多個操作放到單一事務處理。要么全部成功,要么全部失敗。
事務一致性
最終一致性:通過將某些操作的執行延遲到稍后的時間來執行。若前面的操作執行成功,后續操作將延后執行。若前面的操作失敗,后續的操作就不會執行。
最終一致性

到這里,我們實際要解決的問題就明確了:如何確保事件總線能夠正確進行事件轉發?

換句話說:事(shi)(shi)件(jian)(jian)總線掛了,但(dan)是(shi)事(shi)(shi)件(jian)(jian)消息不能丟(diu)失。只要事(shi)(shi)件(jian)(jian)消息不丟(diu),后(hou)面我們還有(you)機會挽救(重(zhong)新發布消息)。

如何保證(zheng)事件消息(xi)不丟失呢?當(dang)然是持久化了。

4. 持久化事件源

eShopOnContainers已經考慮了這一點,集成了事件日志用于持久化。我們直接來看類圖:
事件日志
從類圖中看其實現邏輯也很簡單,主要是定義了一個IntegrationEventLogEntry實體、EventStateEnum事件狀態枚舉和IntegrationEventLogContextEF上下文用于事件日志持久化。暴露IIntegrationEventLogService用于事件狀態的更(geng)新。

其他微服務通過在啟動類中注冊IntegrationEventLogContext即可(ke)完成事件日志(zhi)的集成。

services.AddDbContext<IntegrationEventLogContext>(options =>
{
    options.UseSqlServer(configuration["ConnectionString"],
        sqlServerOptionsAction: sqlOptions =>
        {
            sqlOptions.MigrationsAssembly(typeof(Startup)
                .GetTypeInfo().Assembly.GetName().Name);
            sqlOptions.EnableRetryOnFailure(maxRetryCount: 10,
                maxRetryDelay: TimeSpan.FromSeconds(30), 
                errorNumbersToAdd: null);
        });
});

使用EF進行數據庫遷移后,就會生成IntergrationEventLog表。如下圖所示:

5. 如何借助事件日志確保高可用

主要分兩步走:

  1. 應用程序開始本地數據庫事務,然后更新領域實體狀態,并將集成事件插入集成事件日志表中,最后提交事務來確保領域實體更新和保存事件日志所需的原子性。
  2. 發布事件

第一步(bu)毋庸置疑,第二步(bu)發布事件,我們又有多種實現方式:

  1. 在提交事務后立即發布集成事件,并將其標記為已發布。當微服務發生故障時,可以通過遍歷存儲的集成事件(未發布)執行補救措施。
  2. 將事件日志表用作一種隊列。使用單獨的線程或進程查詢事件日志表,將事件發布到事件總
    線,然后將事件標記為已發布。

通過單獨的進程,將事件日志表作為隊列進行事件發布

這里很顯然第二種方式更為(wei)穩妥(tuo)。而eShopOnContainers出(chu)于簡單考(kao)慮(lv),采用了第一種方案,具體(ti)代碼(ma)如下(xia):

using (var transaction = _catalogContext.Database.BeginTransaction())
{
 _catalogContext.CatalogItems.Update(catalogItem);
 await _catalogContext.SaveChangesAsync();
 // Save to EventLog only if product price changed
 if(raiseProductPriceChangedEvent)
 await
_integrationEventLogService.SaveEventAsync(priceChangedEvent);
 transaction.Commit();
}
// Publish the intergation event through the event bus
_eventBus.Publish(priceChangedEvent);
integrationEventLogService.MarkEventAsPublishedAsync( priceChangedEvent); 

至此,eShopOnContainers確(que)保事件(jian)總線能夠正確(que)轉發消息的解決方案闡(chan)述完畢(bi)。你可能會問,這(zhe)對應的是引言中(zhong)的哪一(yi)種方案?都不是,你可以看作(zuo)其是基于事件(jian)日志的簡(jian)化版的事件(jian)溯源。

6. 僅此而已?

通過(guo)(guo)持久化事件日(ri)志(zhi)來避免事件發布失敗導致(zhi)的一(yi)致(zhi)性問題,是(shi)一(yi)種有(you)效(xiao)措施。然而(er)消(xiao)(xiao)息從發送(song)(song)到接收再到正常消(xiao)(xiao)費的過(guo)(guo)程中,每一(yi)個環節都可(ke)能故障,所以(yi)僅僅在(zai)消(xiao)(xiao)息發送(song)(song)端(duan)使用事件日(ri)志(zhi)只是(shi)確保最終(zhong)一(yi)致(zhi)性的一(yi)小步。還(huan)有(you)很多問題有(you)待完善:

  1. 消息發送成功了,但未被成功接收
  2. 消息發送且成功接收,但未被正確消費
  3. 消息重復發送,導致多次消費問題
  4. 消息被多個微服務訂閱,如何確保每個微服務都成功接收并消費
  5. 等等

而這些問題就(jiu)留給大家(jia)思(si)考吧(ba)。

posted @ 2018-12-11 12:15  「圣杰」  閱讀(4246)  評論(5)    收藏  舉報