如何控制專案中的事務和併發操作?非同步操作和排他任務使用指南

如何控制專案中的事務和併發操作

事務和併發

非同步操作

排他任務

排他任務的產生背景

排他Job

流程例項授權

資料物件

事務和併發

非同步操作

Activiti透過事務方式執行流程,可以根據需求定製

Activiti處理事務:

如果觸發了Activiti的操作(開始流程,完成任務,觸發流程繼續執行),activiti會推進流程,直到每個分支都進入等待狀態

抽象的說,會從流程圖執行深度優先搜尋,如果每個分支都遇到等待狀態,就會返回

等待狀態是稍後需要執行任務,Activiti會把當前狀態儲存到資料庫中,然後等待下一次觸發

觸發可能來自外部,比如使用者任務或接收到一個訊息,也可能來自Activiti本身(定時器事件)

流程包含使用者任務,服務任務和定時器事件完成使用者任務和校驗地址是在同一個工作單元中,兩者的成功和失敗是原子性的。意味著如果服務任務丟擲異常,要回滾當前事務,這樣流程會退回到使用者任務,使用者任務就依然在資料庫裡這就是activiti預設的行為。在(1)中應用或客戶端執行緒完成任務。這會執行服務,流程推進,直到遇到一個等待狀態,就是定時器(2),然後它會返回給呼叫者(3),並提交事務(如果事務是由Activiti開啟的)

有時需要自定義控制流程中事務的邊界,把業務邏輯包裹在一起。這就需要使用

非同步執行:

完成了使用者任務,生成一個發票,把發票傳送給客戶生成發票不在同一個工作單元內了。如果生成發票出錯不需要對使用者任務進行回滾Activiti實現的是完成使用者任務(1),提交事務,返回給呼叫者應用。然後在後臺的執行緒中,非同步執行生成發票。後臺執行緒就是Activiti的Job執行器(一個執行緒池)週期對資料庫的Job進行掃描:當到達“generate invoice”任務,為Activiti建立一個稍後執行的Job“訊息”,並儲存到資料庫。Job會被Job執行器獲取並執行。也會給本地Job執行器一個提醒,告訴有一個新Job,來增加效能

要想使用這個特性,要使用

activiti:async=“true”

擴充套件

activiti:async

可以使用到以下BPMN任務型別中:taskserviceTask,scriptTaskbusinessRuleTasksendTaskreceiveTaskuserTasksubProcesscallActivity

對於userTask,receiveTask和其他等待狀態,非同步執行的作用是讓開始流程監聽器執行在一個單獨的執行緒或者事務中

排他任務

從Activiti 5。9開始

,JobExecutor

能保證

同一個流程例項中的Job不會併發執行

排他任務的產生背景

一個並行閘道器,後面有三個服務任務,都設定為非同步執行:

這樣會新增三個job到資料庫裡。一旦job進入資料庫,就可以被jobExecutor執行了。JobExecutor會獲取job,代理到工作執行緒的執行緒池中,在那裡真正執行job就是說,使用非同步執行,可以把任務分配給這個執行緒池(在叢集環境,可能會使用多個執行緒池)

產生一致性問題:

考慮一下服務任務後的匯聚:當服務任務完成後,到達併發匯聚節點,需要決定是等待其他分支,還是繼續向下執行就是說,對每個到達並行匯聚的分支,都需要判斷是繼續還是等待其他分支的一個或多個分支

為什麼會產生這樣的問題:

因為服務任務配置成使用非同步執行,可能相關的job都在同一時間被獲取,被JobExecutor分配給不同的工作執行緒執行結果是,三個單獨的服務執行使用的事務在到達併發匯聚時可能重疊:如果出現了這個問題,這些事務是互相不可見的,其他事務同時到達了相同的併發匯聚,假設都在等待其他分支然而,每個事務都假設在等待其他分支,所以沒有分支會越過併發匯聚繼續執行,流程例項會一直在等待狀態,無法繼續執行

Activiti解決這個問題方式:Activiti使用了樂觀鎖:

當基於判斷的資料看起來不是最新的時候 (因為其他事務可能在提交之前進行了修改,會在每個事務裡增加資料庫同一行的版本),這個時候,第一個提交的事務會成功,其他會因為樂觀鎖異常導致失敗

這就解決了上面流程的問題:

如果多個分支同步到達並行匯聚,會假設都在登入,並增加父流程的版本號(流程例項)然後嘗試提交第一個分支會成功提交,其他分支會因為樂觀鎖導致失敗因為流程是被job觸發的,Activiti會嘗試在等待一段時間後嘗試執行同一個job,這段時間可以同步閘道器的狀態

Activiti樂觀鎖是一個很好的解決方案嗎?樂觀鎖允許Activiti避免非一致性,確定流程不會“堵在匯聚閘道器”:

或者所有分支都透過閘道器,或者資料庫中的job正在嘗試透過雖然這是一個對於永續性和一致性的完美解決方案,但對於上層來說不一定是期望的行為:

Activiti只會對同一個job重試估計次數(預設配置為3)。之後,job還會在資料庫裡,但是不會再重試了。意味著這個操作必須手工執行job的觸發如果job有非事務方面的效果,不會因為失敗的事務回滾:如果“預定演唱會門票”服務沒有與Activiti共享事務,重試job可能導致我們預定了過多門票

針對這些問題,在Activiti中推出了新的概念:排他job

排他Job

對於一個流程例項,排他任務不能同時執行兩個

考慮上面的流程:如果我們把服務任務申請為排他任務,JobExecutor會保證對應的job不會併發執行。

會保證無論什麼時候獲取一個流程例項的排他任務,都會把同一個流程例項的其他任務都取出來,放在同一個工作執行緒中執行。保證job是順序執行的

從activiti 5。9開始,排他任務已經是預設配置。所以非同步執行和定時器事件預設都是排他任務

如果你想把job設定為非排他,可以使用

activiti:exclusive=“false”

進行配置

排他任務沒有效能問題:

在高負載的情況下效能是個問題,高負載意味著

JobExecutor

的所有工作執行緒都一直在忙碌著使用排他任務,Activiti可以簡單的分佈不同的負載。排他任務意味著同一個流程例項的非同步執行會由相同的執行緒順序執行但是要考慮:如果有多個流程例項時。所有其他流程例項的job也會分配給其他執行緒同步執行意味著雖然Activiti不會同時執行一個流程例項的排他job,但是還會同步執行多個流程例項的非同步執行透過一個總體的預測,在大多數場景下,排他任務都會讓單獨的例項執行的更迅速。而且,對於同一流程例項中的job,需要用到的資料也會利用執行的叢集節點的快取。如果任務沒有在同一個節點執行,資料就必須每次從資料庫重新讀取了

流程例項授權

預設所有人在部署的流程定義上啟動一個新流程例項,透過流程初始化授權功能定義的使用者和組,web客戶端可以限制哪些使用者可以啟動一個新流程例項

Activiti引擎不會校驗授權定義:

這個功能只是為減輕web客戶端開發者實現校驗規則的難度

設定方法與使用者任務使用者分配類似,使用者或組可以使用

activiti:potentialStarter

標籤分配為流程的預設啟動者

user(user3)是直接引用了使用者user3,group(group3)是引用了組group3。如果沒顯示設定,默為群組

也可以使用

process

標籤的屬性

activiti:candidateStarterUsers

activiti:candidateStarterGroups

可以同時使用這兩個屬性

定義流程初始化授權後,開發者可以使用如下方法獲得授權定義。可以獲得給定的使用者能夠啟動哪些流程定義

可以獲得指定流程定義設定的潛在啟動者對應的

IdentityLink

獲得可以啟動給定流程的使用者列表的示例

獲得可以啟動給定流程配置的群組的示例

資料物件

BPMN提供了一種功能,可以在流程定義或子流程中定義資料物件

根據BPMN規範,流程定義可以包含複雜XML結構,可以匯入XSD定義

對於Activiti來說

,作為Activiti首次支援的資料物件,

可以支援XSD型別

資料物件定義會自動轉換為流程變數,名稱與name屬性對應

除了資料物件的定義之外,Activiti支援使用擴充套件元素來為這個變數賦予預設值

相關文章