得物商品狀態體系介紹
來源:得物技術
目錄
一、得物的商品體系
1. 新品
2. 商品
3. 草稿
二、得物商品狀態流轉
三、狀態機圖
1. 核心元件
2. 狀態型別
四、狀態機選型介紹
1. Enum StateMachine
2. Spring StateMachine
3. Squirrel StateMachine
4. Cola StateMachine
五、狀態機效能評測
1. 準備測試程式碼
2. 準備基準測試程式碼
3. 測試結果彙總
六、基本功能評測
七、接入成本評測
八、擴充套件能力評測
九、狀態機對比總結
十、商品域對於狀態機的訴求
十一、狀態機選型總結
一
得物的商品體系
目前得物的商品分為三種型別,分別是:新品、商品、草稿。但是隻有商品是可售賣的,新品和草稿都不是可售賣的。
新品有很多種建立的渠道,商品可以由新品選品透過後由系統自動生成,也可以由運營直接建立。而商品草稿是在商品被編輯後建立而來,草稿在更新稽核透過後,會重新覆蓋已有的商品資訊。
新品
新品是企業賣家或個人賣家或者 ISV 等渠道申請的一種不可售賣的商品,需要運營選品稽核、商研稽核透過後,才會變成可售賣的商品,精簡後的新品申請的流程大致如下圖所示:
商品
新品在稽核透過後,就變成了商品,商品的狀態有上架、下架、待稽核等多種,只有上架狀態的商品是可售賣的。
未上架的商品有兩種情況,一種是待補全資訊,一種是待稽核,正常稽核透過的商品如果沒有特殊條件,會自動上架。
第 1 種待補全的商品,這種商品是新品在運營選品透過後,就進入了商品池,需要補全其他資訊,然後再由商研後置稽核透過後進行上架。
第 2 種待稽核的商品,這種商品是在原來上架狀態的商品被下架並且編輯之後,等待稽核。
待稽核的商品還未上架過,所以沒有草稿,如果編輯該商品會直接修改商品庫裡的資訊。
如果已經上架了,再來編輯商品,則會先生成草稿,草稿儲存在草稿庫中,此時草稿的修改不會影響原商品。
新品上新的流程在迭代過程中也發生了變化:
1、現有 SPU 管理可以跳過商研稽核,直接建立商品上架,流程上是將商研稽核後置了;
2、得物運營自挖品,需要運營先在新品中提報,經過選品和商研稽核後,再去 SPU 管理補全商品資料再稽核上架。新品提報和補全資料這兩個流程希望可以進行合併,節約上架時效和上架成本。
草稿
草稿是在商品的基礎上編輯之後,生成的一個副本,草稿可以反覆編輯。
在管理後臺的頁面上體現為,如果有“草稿”兩個字,則說明這條記錄是一個草稿,如果有“編輯”兩個字,則說明這條記錄是一個商品。
二
得物商品狀態流轉
目前得物的商品狀態共有:下架、上架、待補全、待稽核、稽核透過等等數十種狀態。
當對商品進行編輯時,會建立一條草稿記錄,記錄商品修改後的副本資訊,儲存在草稿庫中,其中草稿的狀態和商品原本的狀態是隔離的,草稿的狀態變更不會影響商品的狀態。
精簡後的各狀態之間的流轉如下圖所示:
從上圖可以看出,商品的狀態已經相當豐富,狀態之間的流轉也是錯綜複雜,並且還涉及到不同的商品型別之間的流轉。從系統後續的穩定性和可維護性來看,確實到了需要引入狀態機來維護商品狀態流轉的時機了。
三
狀態機圖
狀態機圖是一種行為圖,它透過有限的狀態轉換來表示系統中的某些行為。除此之外狀態機圖也可以用來表示系統的某種協議狀態。UML 2.4 中定義的兩種狀態機是:行為狀態機和協議狀態機。具體可以參考 UML State Machine 中的定義。
核心元件
大體上狀態機有以下幾個核心的元件:
狀態
事件
流轉
條件
動作
透過這些元件共同來形成一個完整的狀態機:
如下圖所示,表示有兩個狀態,StateA 和 StateB,兩個狀態之間透過 EventA 和 EventB 事件進行流轉。在狀態扭轉之前需要滿足一定的條件,條件滿足後即可執行狀態流轉,並可執行狀態流轉後的動作。
狀態型別
在 UML 的定義中,狀態有三種型別:簡單狀態、組合狀態、子狀態機狀態。
其中簡單狀態、組合狀態比較好理解,如下圖所示,組合狀態將多個簡單狀態進行組合封裝,形成一個新的複雜的狀態,內部狀態和它的內部內容被定義它們的狀態機所包含,如下圖所示:
但是子狀態機狀態(以下我們用子機狀態來表示)相對比較複雜,子機狀態在語義上等同於組合狀態。子機狀態機的區域是組合狀態的區域。
進入、退出和其他行為動作以及內部轉換被定義為狀態的一部分。子機狀態是一種分解機制,它允許對公共行為進行分解並複用。子狀態機是一個狀態機定義可以被多次複用的方式。它也需要將進入和離開遷移繫結到內部頂點上,這一點與封裝組合狀態類似。
子機狀態最重要的作用就是封裝和複用,概念理解起來比較晦澀難懂,下面我們用一張圖來描述:
雖然 UML 在狀態機的定義中定義了這麼多種狀態,但實際上我們只需要簡單狀態就夠用了。
四
狀態機選型介紹
開源的狀態機引擎有很多,目前在 Github 上的 Top 2 狀態機實現中,一個是 Spring StateMachine,一個是 Squirrel StateMachine。他們的優點是功能很完備,缺點也是功能很完備。
就我們的專案而言,不需要那麼多狀態機的高階玩法,其實大部分專案都是如此:比如狀態的巢狀(nested state),狀態的並行(parallel,fork,join)、子狀態機等等。
網上已經有非常多的狀態機選型的文章了,這裡不再長篇贅述,只做簡單的介紹。
Enum StateMachine
在看開源的狀態機引擎之前,我們先看一下,透過列舉實現一個狀態機的最簡單方式。
列舉型別因為自身的執行緒安全性保障和高可讀性特性,是簡單狀態機的首選。
首先我們定義一個列舉,表示商品的狀態,並在列舉中定義一個狀態流轉的方法,其中狀態流轉的抽象方法中接收 3 個引數:
期望流轉到的目標狀態
狀態流轉的條件
狀態流轉後的動作
public interface StateCondition {
// 檢查是否能流轉到目標狀態
boolean check(CommodityState target);
}
public interface StateAction {
void doAction();
}
狀態機的列舉定義如下:
public enum CommodityState { // 待稽核 TO_AUDIT { @Override StateCondition getCondition() {return new ToAuditStateCondition();} @Override StateAction getAction() {return new ToAuditStateAction();} }, // 已上架 ON_SHELF { @Override StateCondition getCondition() {return new OnShelfStateCondition();} @Override StateAction getAction() {return new OnShelfStateAction();} }, // 已下架 OFF_SHELF { @Override StateCondition getCondition() {return new OffShelfStateCondition();} @Override StateAction getAction() {return new OffShelfStateAction();} }; boolean transition(CommodityState target) { StateCondition condition = getCondition(); if (condition.check(target)) { StateAction action = getAction(); action.doAction(); return true; } throw new IllegalArgumentException("當前狀態不符合流轉條件"); } abstract StateCondition getCondition(); abstract StateAction getAction();}
具體的條件檢查和執行的動作,都定義到每個狀態具體的實現類中。
Spring StateMachine
Spring StateMachine 是 Spring 官方提供的狀態機實現。
先從狀態機的定義入手,StateMachine<States, Events>,其中:
StateMachine:狀態機模型
State:S-狀態,一般定義為一個列舉類,如建立、待風控稽核、待支付等狀態
Event:E-事件,同樣定義成一個列舉類,如訂單建立、訂單稽核、支付等,代表一個動作。一個狀態機的定義就由這兩個主要的元素組成,狀態及對對應的事件(動作)。
Spring StateMachine 中的相關概念:
Transition: 節點,是組成狀態機引擎的核心
Source:節點的當前狀態
Target:節點的目標狀態
Event:觸發節點從當前狀態到目標狀態的動作
Guard:起校驗功能,一般用於校驗是否可以執行後續 Action
Action:用於實現當前節點對應的業務邏輯處理
以下是一些核心元件:
Spring StateMachine 的核心實現:
對於節點配置,可以看個簡單的例子:
builder.configureTransitions() // 配置節點// 表示source target兩種狀態不同.withExternal() // 當前節點狀態.source(SOURCE) // 目標節點狀態.target(TARGET) // 導致當前變化的動作/事件.event(BizOrderStatusChangeEventEnum.EVT_CREATE) // 執行當前狀態變更導致的業務邏輯處理,以及出異常時的處理.action(orderCreateAction, errorHandlerAction);
其中有幾種可選的型別:
WithExternal 是當 Source 和 Target 不同時的寫法,如上例子。
WithInternal 當 Source 和 Target 相同時的串聯寫法,比如付款失敗時,付款前及付款後都是待付款狀態。
WithChoice 當執行一個動作,可能導致多種結果時,可以選擇使用 Choice+Guard 來跳轉。
更詳細的進行 Spring 狀態機的配置,可以參考這篇文章:
Squirrel StateMachine
Squirrel-Foundation 是一款很優秀的開源產品,推薦大家閱讀以下它的原始碼。相較於 Spring statemachine,Squirrel 的實現更為輕量,設計域也很清晰,對應的文件以及測試用例也很豐富。
核心元件:
Squirrel StateMachine 的核心實現:
Squirrel 的事件處理模型與 Spring-Statemachine 比較類似,Squirrel 的事件執行器的作用點粒度更細,透過預處理,將一個狀態遷移分解成 Exit Trasition Entry 這三個 Action Event,再遞交給執行器分別執行(這個設計挺不錯)。
怎樣配置並使用 Squirrel StateMachine,可以參考這篇文章:https://blog.csdn.net/footless_bird/article/details/115797710
Cola StateMachine
開源狀態機都是有狀態的(Stateful)的,有狀態意味著多執行緒併發情況下如果是單個例項就容易出現執行緒安全問題。
如今我們的系統普遍都是分散式部署,不得不考慮多執行緒的問題,因為每來一個請求就需要建立一個狀態機例項(per statemachine per request)。如果某些狀態機它的構建過程很複雜,並且當下 QPS 又很高的話,往往會造成系統的效能瓶頸。
為此阿里出了一個開源的狀態機:Cola-StateMachine
當時他們團隊也想搞個狀態機來減負,經過深思熟慮、不斷類比之後他們考慮自研。希望能設計出一款功能相對簡單、效能良好的開源狀態機;最後命名為 Cola-ComPonent-Statemachine。
Cola-StateMachine 最重要的特點是,狀態機的設計是無狀態的,並且內部實現了 DSL 語法,透過流式 API 限定了方法呼叫的順序。
分析一下市面上的開源狀態機引擎,不難發現,它們之所以有狀態,主要是在狀態機裡面維護了兩個狀態:初始狀態(Initial State)和當前狀態(Current State),如果我們能把這兩個例項變數去掉的話,就可以實現無狀態,從而實現一個狀態機只需要有一個 Instance 就夠了。
關鍵是這兩個狀態可以不要嗎?當然可以,唯一的副作用是,我們沒辦法獲取到狀態機 Instance 的 Current State。然而,我也不需要知道,因為我們使用狀態機,僅僅是接受一下 Source State,Check 一下 Condition,Execute 一下 Action,然後返回 Target State 而已。它只是實現了一個狀態流轉的 DSL 表達,僅此而已,全程操作完全可以是無狀態的。
具體舉例如下:
// 構建一個狀態機(生產場景下,生產場景可以直接初始化一個Bean)StateMachineBuilder<StateMachineTest.ApplyStates, StateMachineTest.ApplyEvents, Context> builder = StateMachineBuilderFactory.create();// 外部流轉(兩個不同狀態的流轉)builder.externalTransition().from(SOURCE)//原來狀態.to(TARGET)//目標狀態.on(EVENT1)//基於此事件觸發.when(checkCondition1())//前置過濾條件.perform(doAction());//滿足條件,最終觸發的動作
更詳細的介紹 Cola StateMachine 的資料,可以參考作者的介紹:https://blog.csdn.net/significantfrank/article/details/104996419
五
狀態機效能評測
本次對比的是 Spring StateMachine 和 Cola StateMachine 的效能,為了儘量避免其他邏輯的影響,我在 Action 和 Condition 的實現類中,均是空實現,保證只評測兩個框架本身的效能。
本次評測的兩個框架的版本如下:
<dependency> <groupId>com.alibaba.cola</groupId> <artifactId>cola-component-statemachine</artifactId> <version>4.0.1</version></dependency><dependency> <groupId>org.springframework.statemachine</groupId> <artifactId>spring-statemachine-core</artifactId> <version>3.2.1</version></dependency>
準備測試程式碼
Spring StateMachine
將主要的程式碼都封裝到兩個類中:SpuStateMachineConfig 和 SpuStateMachineService
首先是 SpuStateMachineConfig,主要是對 Spring StateMachine 進行配置。
/**
* 狀態機 核心配置
*/
public class SpuStateMachineConfig extends EnumStateMachineConfigurerAdapter<SpuStatesEnum, SpuEventsEnum> {
public final static String DEFAULT_MACHINEID = "spring/machine/default/machineid";
private final SpuStateMachinePersist spuStateMachinePersist = new SpuStateMachinePersist();
private final StateMachinePersister<SpuStatesEnum, SpuEventsEnum, SpuMessageContext> stateMachinePersister = new DefaultStateMachinePersister<>(spuStateMachinePersist);
private final DefaultSpuGuard defaultSpuGuard = new DefaultSpuGuard();
private final DefaultSpuErrorAction defaultSpuErrorAction = new DefaultSpuErrorAction();
private final DefaultSpuSuccessAction defaultSpuSuccessAction = new DefaultSpuSuccessAction();
private final SpuCreateDraftSuccessAction spuCreateDraftSuccessAction = new SpuCreateDraftSuccessAction();
private final SpuCancelDraftSuccessAction spuCancelDraftSuccessAction = new SpuCancelDraftSuccessAction();
public StateMachinePersister<SpuStatesEnum, SpuEventsEnum, SpuMessageContext> getSpuMachinePersister() {
return stateMachinePersister;
}
public StateMachinePersister<SpuStatesEnum, SpuEventsEnum, SpuMessageContext> spuMachinePersister() {
return getSpuMachinePersister();
}
public void configure(StateMachineConfigurationConfigurer<SpuStatesEnum, SpuEventsEnum> config) throws Exception {
configMachineId(config, DEFAULT_MACHINEID);
}
public void configure(StateMachineStateConfigurer<SpuStatesEnum, SpuEventsEnum> config) throws Exception {
configureStates(config);
}
public void configure(StateMachineTransitionConfigurer<SpuStatesEnum, SpuEventsEnum> transitions) throws Exception {
configureTransitions(transitions);
}
"spuStateMachineFactory") (name =
public StateMachineFactory<SpuStatesEnum, SpuEventsEnum> spuStateMachineFactory() throws Exception {
StateMachineConfigBuilder<SpuStatesEnum, SpuEventsEnum> configBuilder = new StateMachineConfigBuilder<SpuStatesEnum, SpuEventsEnum>();
// 透過apply方法將Configurer設定進去,this正好實現了
// 也可以自定義實現configurer,比如:new BuilderStateMachineConfigurerAdapter<>();
configBuilder.apply(this);
StateMachineConfig<SpuStatesEnum, SpuEventsEnum> stateMachineConfig = configBuilder.getOrBuild();
StateMachineModel<SpuStatesEnum, SpuEventsEnum> machineModel = getMachineModel(stateMachineConfig);
StateMachineModelFactory<SpuStatesEnum, SpuEventsEnum> factory = stateMachineConfig.getModel().getFactory();
return new ObjectStateMachineFactory<>(machineModel, factory);
}
private static StateMachineModel<SpuStatesEnum, SpuEventsEnum> getMachineModel(StateMachineConfig<SpuStatesEnum, SpuEventsEnum> stateMachineConfig) {
StatesData<SpuStatesEnum, SpuEventsEnum> stateMachineStates = stateMachineConfig.getStates();
TransitionsData<SpuStatesEnum, SpuEventsEnum> stateMachineTransitions = stateMachineConfig.getTransitions();
ConfigurationData<SpuStatesEnum, SpuEventsEnum> stateMachineConfigurationConfig = stateMachineConfig.getStateMachineConfigurationConfig();
// 設定StateMachineModel
return new DefaultStateMachineModel<>(stateMachineConfigurationConfig, stateMachineStates, stateMachineTransitions);
}
}
主要執行的核心配置如下,包括配置狀態機,新增所有支援的狀態,新增狀態的變遷。
{
然後是在 SpuStateMachineService 中封裝狀態機的呼叫入口,並且在 SpuStateMachineService 中會啟動 Spring 容器。
/**
* 狀態機 核心處理 類
*/
public class SpuStateMachineService {
private final ApplicationContext applicationContext;
private final StateMachineFactory<SpuStatesEnum, SpuEventsEnum> spuStateMachineFactory;
private final StateMachinePersister<SpuStatesEnum, SpuEventsEnum, SpuMessageContext> spuStateMachinePersister;
public SpuStateMachineService(String machineId) {
// 啟動Spring容器,獲取 ApplicationContext 物件
applicationContext = new AnnotationConfigApplicationContext(SpuStateMachineConfig.class);
spuStateMachineFactory = applicationContext.getBean(StateMachineFactory.class);
spuStateMachinePersister = applicationContext.getBean(StateMachinePersister.class);
}
/**
* 傳送事件
*
* @param event
* @param context
* @return
*/
public boolean sendEvent(SpuEventsEnum event, SpuMessageContext context) {
// 利用隨記ID建立狀態機,建立時沒有與具體定義狀態機繫結
StateMachine<SpuStatesEnum, SpuEventsEnum> stateMachine = spuStateMachineFactory.getStateMachine(SpuStateMachineConfig.DEFAULT_MACHINEID);
try {
// restore
spuStateMachinePersister.restore(stateMachine, context);
// 構建 mesage
Message<SpuEventsEnum> message = MessageBuilder.withPayload(event)
.setHeader("request", context)
.build();
// 傳送事件,返回是否執行成功
boolean success = stateMachine.sendEvent(message);
if (success) {
spuStateMachinePersister.persist(stateMachine, context);
}
return success;
} catch (RuntimeException e) {
throw e;
} catch (Exception e) {
throw new RuntimeException("狀態機處理未執行成功", e);
} finally {
stateMachine.stop();
}
}
}
Cola StateMachine
將主要的程式碼也都封裝到兩個類中:ColaStateMachineConfig 和 ColaStateMachineService。
首先是 ColaStateMachineConfig,主要負責 Cola StateMachine 的配置:
public class ColaStateMachineConfig<Context> {
public StateMachineBuilder<SpuStateEnum, SpuEventEnum, Context> createBuilder() {
StateMachineBuilder<SpuStateEnum, SpuEventEnum, Context> builder = StateMachineBuilderFactory.create();
// 建立草稿
builder.externalTransition()
.from(SpuStateEnum.INIT)
.to(SpuStateEnum.DRAFT)
.on(SpuEventEnum.CREATE_DRAFT)
.when(SpuEventEnum.CREATE_DRAFT.getCondition())
.perform(SpuEventEnum.CREATE_DRAFT.getAction());
// 建立SPU
builder.externalTransition()
.from(SpuStateEnum.INIT)
.to(SpuStateEnum.NEW)
.on(SpuEventEnum.CREATE_SPU)
.when(SpuEventEnum.CREATE_SPU.getCondition())
.perform(SpuEventEnum.CREATE_SPU.getAction());
// 建立SPU(基於草稿)
builder.externalTransition()
.from(SpuStateEnum.DRAFT)
.to(SpuStateEnum.NEW)
.on(SpuEventEnum.CREATE_SPU)
.when(SpuEventEnum.CREATE_SPU.getCondition())
.perform(SpuEventEnum.CREATE_SPU.getAction());
// 提交稽核
builder.externalTransition()
.from(SpuStateEnum.NEW)
.to(SpuStateEnum.PENDING_REVIEW)
.on(SpuEventEnum.INITIATE_AUDIT)
.when(SpuEventEnum.INITIATE_AUDIT.getCondition())
.perform(SpuEventEnum.INITIATE_AUDIT.getAction());
// 稽核透過
builder.externalTransition()
.from(SpuStateEnum.PENDING_REVIEW)
.to(SpuStateEnum.CM_APPROVED_PASS)
.on(SpuEventEnum.REVIEW_PASS)
.when(SpuEventEnum.REVIEW_PASS.getCondition())
.perform(SpuEventEnum.REVIEW_PASS.getAction());
// 稽核拒絕
builder.externalTransition()
.from(SpuStateEnum.PENDING_REVIEW)
.to(SpuStateEnum.CM_APPROVED_REJECTION)
.on(SpuEventEnum.REVIEW_REJECTION)
.when(SpuEventEnum.REVIEW_REJECTION.getCondition())
.perform(SpuEventEnum.REVIEW_REJECTION.getAction());
// 刪除SPU
builder.externalTransition()
.from(SpuStateEnum.DRAFT)
.to(SpuStateEnum.CANCEL)
.on(SpuEventEnum.CANCEL)
.when(SpuEventEnum.CANCEL.getCondition())
.perform(SpuEventEnum.CANCEL.getAction());
return builder;
}
}
然後是 ColaStateMachineService,主要是封裝了狀態機的呼叫入口:
{
準備基準測試程式碼
基準測試是從吞吐量的維度做評測,預熱 2 輪,使用 2 個程式,每個程式中有 8 個執行緒進行測試。
Spring StateMachine
/**
* 基準測試
*
* @auther houyi.wh
* @date 2023-10-18 14:10:18
* @since 0.0.1
*/
@Warmup(iterations = 2)
@BenchmarkMode({Mode.Throughput})
@Measurement(iterations = 2, time = 1)
@OutputTimeUnit(TimeUnit.MILLISECONDS)
@Fork(value = 2)
@Threads(8)
@State(Scope.Benchmark)
public class SpringStateMachineBench {
private SpuStateMachineService stateMachineService;
@Setup
public void prepare() {
stateMachineService = new SpuStateMachineService("commodity-machine");
}
@Benchmark
public void test_sendEvent() {
SpuMessageContext entity = new SpuMessageContext("122312", "spu-1222", ");
// 建立SPU,從INIT --> CREATE_SPU,如果符合條件則會執行doAction,並返回CREATE_SPU的狀態,否則返回INIT
boolean isSuccess = stateMachineService.sendEvent(SpuEventsEnum.CREATE_SPU, entity);
}
/**
* 執行基準測試
*
* @param args
* @throws RunnerException
*/
public static void main(String[] args) throws RunnerException {
Options opt = new OptionsBuilder()
.include(SpringStateMachineBench.class.getSimpleName())
.output("/Users/admin/Downloads/benchmark/spring-state-machine-benchmark.txt")
.build();
new Runner(opt).run();
}
}
Cola StateMachine
/**
* 基準測試
*
* @auther houyi.wh
* @date 2023-10-18 14:10:18
* @since 0.0.1
*/
@Warmup(iterations = 2)
@BenchmarkMode({Mode.Throughput})
@Measurement(iterations = 2, time = 1)
@OutputTimeUnit(TimeUnit.MILLISECONDS)
@Fork(value = 2)
@Threads(8)
@State(Scope.Benchmark)
public class ColaStateMachineBench {
private ColaStateMachineService<SpuEntity> stateMachineService;
@Setup
public void prepare() {
stateMachineService = new ColaStateMachineService<>("commodity-machine");
}
@Benchmark
public void test_sendEvent() {
SpuEntity entity = new SpuEntity("122312", "spu-1222", ");
// 建立SPU,從INIT --> CREATE_SPU,如果符合條件則會執行doAction,並返回CREATE_SPU的狀態,否則返回INIT
SpuStateEnum spuStateEnum = stateMachineService.sendEvent(SpuStateEnum.INIT, SpuEventEnum.CREATE_SPU, entity);
}
/**
* 執行基準測試
*
* @param args
* @throws RunnerException
*/
public static void main(String[] args) throws RunnerException {
Options opt = new OptionsBuilder()
.include(ColaStateMachineBench.class.getSimpleName())
.output("/Users/admin/Downloads/benchmark/cola-state-machine-benchmark.txt")
.build();
new Runner(opt).run();
}
}
測試結果彙總
測試結果對比如下,單位是每毫秒執行的次數,可以看到 Cola 是 Spring 的 1449 倍。
PS:由於這裡測試的是框架本身的效能,doAction 中都是空實現,如果 doAction 使用實際的業務場景,根據木桶原理,最低的木板將決定木桶水位的高低,所以當 doAction 中有實際的 IO 操作時,兩個框架的效能將會被 IO 操作的 RT 所拉齊。
具體的測試結果如下:
Spring StateMachine
JMH version: 1.35
# VM version: JDK 1.8.0_181, Java HotSpot(TM) 64-Bit Server VM, 25.181-b13
# VM invoker: /Library/Java/JavaVirtualMachines/jdk1.8.0_181.jdk/Contents/Home/jre/bin/java
# VM options: -javaagent:/Applications/IntelliJ IDEA.app/Contents/lib/idea_rt.jar=49634:/Applications/IntelliJ IDEA.app/Contents/bin -Dfile.encoding=UTF-8
# Blackhole mode: full + dont-inline hint (auto-detected, use -Djmh.blackhole.autoDetect=false to disable)
# Warmup: 2 iterations, 10 s each
# Measurement: 2 iterations, 1 s each
# Timeout: 10 min per iteration
# Threads: 8 threads, will synchronize iterations
# Benchmark mode: Throughput, ops/time
# Benchmark: com.shizhuang.duapp.statemachine.benchmark.SpringStateMachineBench.test_sendEvent
# Run progress: 0.00% complete, ETA 00:00:44
# Fork: 1 of 2
# Warmup Iteration 1: 17.855 ops/ms
# Warmup Iteration 2: 39.979 ops/ms
Iteration 1: 32.060 ops/ms
Iteration 2: 31.712 ops/ms
# Run progress: 50.00% complete, ETA 00:00:29
# Fork: 2 of 2
# Warmup Iteration 1: 16.947 ops/ms
# Warmup Iteration 2: 41.405 ops/ms
Iteration 1: 38.253 ops/ms
Iteration 2: 40.171 ops/ms
Result "com.shizhuang.duapp.statemachine.benchmark.SpringStateMachineBench.test_sendEvent":
35.549 ±(99.9%) 27.813 ops/ms [Average]
(min, avg, max) = (31.712, 35.549, 40.171), stdev = 4.304
CI (99.9%): [7.736, 63.362] (assumes normal distribution)
# Run complete. Total time: 00:00:58
REMEMBER: The numbers below are just data. To gain reusable insights, you need to follow up on
why the numbers are the way they are. Use profilers (see -prof, -lprof), design factorial
experiments, perform baseline and negative tests that provide experimental control, make sure
the benchmarking environment is safe on JVM/OS/HW level, ask for reviews from the domain experts.
Do not assume the numbers tell you what you want them to tell.
Benchmark Mode Cnt Score Error Units
SpringStateMachineBench.test_sendEvent thrpt 4 35.549 ± 27.813 ops/ms
Cola StateMachine
# JMH version: 1.35
# VM version: JDK 1.8.0_181, Java HotSpot(TM) 64-Bit Server VM, 25.181-b13
# VM invoker: /Library/Java/JavaVirtualMachines/jdk1.8.0_181.jdk/Contents/Home/jre/bin/java
# VM options: -javaagent:/Applications/IntelliJ IDEA.app/Contents/lib/idea_rt.jar=64954:/Applications/IntelliJ IDEA.app/Contents/bin -Dfile.encoding=UTF-8
# Blackhole mode: full + dont-inline hint (auto-detected, use -Djmh.blackhole.autoDetect=false to disable)
# Warmup: 2 iterations, 10 s each
# Measurement: 2 iterations, 1 s each
# Timeout: 10 min per iteration
# Threads: 8 threads, will synchronize iterations
# Benchmark mode: Throughput, ops/time
# Benchmark: com.shizhuang.duapp.statemachine.benchmark.ColaStateMachineBench.test_sendEvent
# Run progress: 0.00% complete, ETA 00:00:44
# Fork: 1 of 2
# Warmup Iteration 1: 46019.377 ops/ms
# Warmup Iteration 2: 48460.086 ops/ms
Iteration 1: 53695.798 ops/ms
Iteration 2: 44157.738 ops/ms
# Run progress: 50.00% complete, ETA 00:00:28
# Fork: 2 of 2
# Warmup Iteration 1: 46100.542 ops/ms
# Warmup Iteration 2: 54736.678 ops/ms
Iteration 1: 49838.690 ops/ms
Iteration 2: 55205.836 ops/ms
Result "com.shizhuang.duapp.statemachine.benchmark.ColaStateMachineBench.test_sendEvent":
50724.516 ±(99.9%) 31836.478 ops/ms [Average]
(min, avg, max) = (44157.738, 50724.516, 55205.836), stdev = 4926.730
CI (99.9%): [18888.038, 82560.993] (assumes normal distribution)
# Run complete. Total time: 00:00:56
REMEMBER: The numbers below are just data. To gain reusable insights, you need to follow up on
why the numbers are the way they are. Use profilers (see -prof, -lprof), design factorial
experiments, perform baseline and negative tests that provide experimental control, make sure
the benchmarking environment is safe on JVM/OS/HW level, ask for reviews from the domain experts.
Do not assume the numbers tell you what you want them to tell.
Benchmark Mode Cnt Score Error Units
ColaStateMachineBench.test_sendEvent thrpt 4 50724.516 ± 31836.478 ops/ms
六
基本功能評測
持久化一方面是好事,另一方面是壞事。
好事是因為持久化策略可以應對分散式系統的故障,每個實體物件在做狀態變遷之前,可以從持久化的儲存中獲取該實體之前的狀態,不用擔心狀態的丟失。
壞事是因為為了要保證狀態機的狀態,每次狀態變遷之前都需要先恢復當前的狀態,這個操作是非常消耗效能的。
Cola StateMachine 將 StateMachine 的例項定義為無狀態(Stateless)的,狀態的變遷不依賴當前 StateMachine 例項的狀態,所以也就不需要持久化的問題。系統發生故障時,StateMachine 的例項也不需要重建,只需要對狀態變遷做重試即可,狀態是否能夠變遷是在 Condition 中定義的,跟 StateMachine 的例項沒有直接的關係。
七
接入成本評測
八
擴充套件能力評測
九
狀態機對比總結
下面是一份詳細的 Spring StateMachine、Squirrel StateMachine、Cola StateMachine 對比:
綜合來看,三個狀態機框架都有自己的優勢和適用場景。Spring StateMachine 更適合應用於複雜業務場景,適合 Spring 生態中的應用;Squirrel StateMachine 更適合快速建模、輕量級場景;Cola StateMachine 更偏向於分散式系統和領域驅動設計。根據實際需求和專案特點選擇適合的狀態機框架更為重要。
十
商品域對於狀態機的訴求
商品域對於狀態機的訴求主要是希望:
能夠更清晰、更合理的管理和維護商品的狀態。
保證狀態的變遷是符合業務場景的,不能產生錯誤的狀態變遷。
解決商品狀態流轉的過程管控的問題,將複雜的狀態流轉從耦合的業務中提取出來,讓狀態變遷的邏輯單獨實現,便於後續狀態的維護和擴充套件。
商品域對於狀態機有以下這些使用場景:
商品狀態的狀態值較多,狀態之間的變遷較混亂,需要將散落在各個程式碼裡維護不清晰的狀態變遷統一維護。
SPU 的有多個表示狀態的欄位,需要在一個場景中同時維護多個狀態欄位的狀態變遷。
十一
狀態機選型總結
根據各狀態機的功能、效能、接入成本、擴充套件能力,並結合商品領域對於狀態機的使用訴求,主要是希望:
能夠更清晰、更合理的管理和維護商品的狀態。
保證狀態的變遷是符合業務場景的,不能產生錯誤的狀態變遷。
解決商品狀態流轉的過程管控的問題,將複雜的狀態流轉從耦合的業務中提取出來,讓狀態變遷的邏輯單獨實現,便於後續狀態的維護和擴充套件。
參考資料
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/70027824/viewspace-2996450/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- 常見的四類HTTP狀態碼介紹HTTP
- Flink狀態管理和容錯機制介紹
- 物聯網時代,智慧家居系統的具體介紹
- Go中的有限狀態機FSM的詳細介紹Go
- 各種HTTP 3xx重定向狀態碼介紹HTTP
- 動態惡意軟體分析工具介紹
- 修復HTTP 304錯誤狀態碼5種方法介紹HTTP
- 軟體系統介紹文件模板
- WWDC第一天內容彙總,keynote+各平臺整體狀態介紹
- EOS系統合約總體介紹
- Linux作業系統總體介紹!Linux作業系統
- SMP、NUMA、MPP體系結構介紹
- Spring Cloud Stream 體系及原理介紹SpringCloud
- 中科三方域名管理專題:各種域名狀態介紹
- QuickTask動態指令碼支援框架整體介紹篇UI指令碼框架
- 深入iOS系統底層之靜態庫介紹iOS
- Zustand 讓 React 狀態變得太簡單React
- 簡單介紹一個用於 Vue.js 的狀態管理庫:PiniaVue.js
- 複製狀態與變數記錄表 | performance_schema全方位介紹變數ORM
- 簡單介紹靜態路由路由
- Mybatis介紹之 動態SQLMyBatisSQL
- VSCode軟體介紹VSCode
- 半導體制造業sap系統功能介紹
- 物聯網學習教程—Linux系統程式設計之程式介紹Linux程式設計
- 系統設計架構:有狀態與無狀態架構
- MySQL執行狀況查詢方式介紹MySql
- 【AWR】Oracle awr相關檢視及體系介紹Oracle
- 得物直播低延遲探索 | 得物技術
- Altium designer軟體介紹
- CUDA記憶體介紹記憶體
- django中介軟體介紹Django
- 記憶體回收介紹記憶體
- 元宇宙系統介紹元宇宙
- APM系統SkyWalking介紹
- 系統SDK介紹-02
- 系統SDK介紹-01
- 【Oracle體系結構】 Oracle19C 系統結構介紹Oracle
- 【塗鴉物聯網足跡】物聯網基礎介紹篇