项目终于用上了Spring状态机,非常优雅!
3.3 测试验证
1)验证业务
- 新增一个订单
http://localhost:8084/order/create - 对订单进行支付
http://localhost:8084/order/pay?id=2 - 对订单进行发货
http://localhost:8084/order/deliver?id=2 - 对订单进行确认收货
http://localhost:8084/order/receive?id=2
正常流程结束。如果对一个订单进行支付了,再次进行支付,则会报错:http://localhost:8084/order/pay?id=2
报错如下:
2)验证持久化
内存
使用内存持久化类持久化:
/**
* author:公众号:码猿技术专栏
*/
@Resource
private StateMachinePersister<OrderStatus, OrderStatusChangeEvent, String> stateMachineMemPersister;
/**
* 发送订单状态转换事件
* synchronized修饰保证这个方法是线程安全的
*
* @param changeEvent
* @param order
* @return
*/
private synchronized boolean sendEvent(OrderStatusChangeEvent changeEvent, Order order) {
boolean result = false;
try {
//启动状态机
orderStateMachine.start();
//尝试恢复状态机状态
stateMachineMemPersister.restore(orderStateMachine, String.valueOf(order.getId()));
Message message = MessageBuilder.withPayload(changeEvent).setHeader("order", order).build();
result = orderStateMachine.sendEvent(message);
//持久化状态机状态
stateMachineMemPersister.persist(orderStateMachine, String.valueOf(order.getId()));
} catch (Exception e) {
log.error("订单操作失败:{}", e);
} finally {
orderStateMachine.stop();
}
return result;
}
redis持久化
引入依赖:
<!-- redis持久化状态机 -->
<dependency>
<groupId>org.springframework.statemachine</groupId>
<artifactId>spring-statemachine-redis</artifactId>
<version>1.2.9.RELEASE</version>
</dependency>
配置yaml:
spring:
redis:
database: 0
host: localhost
jedis:
pool:
max-active: 8
max-idle: 8
max-wait: ''
min-idle: 0
password: ''
port: 6379
timeout: 0
使用redis持久化类持久化:
/**
* author:公众号:码猿技术专栏
*/
@Resource
private StateMachinePersister<OrderStatus, OrderStatusChangeEvent, String> stateMachineRedisPersister;
/**
* 发送订单状态转换事件
* synchronized修饰保证这个方法是线程安全的
*
* @param changeEvent
* @param order
* @return
*/
private synchronized boolean sendEvent(OrderStatusChangeEvent changeEvent, Order order) {
boolean result = false;
try {
//启动状态机
orderStateMachine.start();
//尝试恢复状态机状态
stateMachineRedisPersister.restore(orderStateMachine, String.valueOf(order.getId()));
Message message = MessageBuilder.withPayload(changeEvent).setHeader("order", order).build();
result = orderStateMachine.sendEvent(message);
//持久化状态机状态
stateMachineRedisPersister.persist(orderStateMachine, String.valueOf(order.getId()));
} catch (Exception e) {
log.error("订单操作失败:{}", e);
} finally {
orderStateMachine.stop();
}
return result;
}
3.4 状态机存在的问题
1)stateMachine无法抛出异常,异常会被状态机给消化掉
问题现象
从orderStateMachine.sendEvent(message);获取的结果无法感知到。无论执行正常还是抛出异常,都返回true。
@Resource
private OrderMapper orderMapper;
@Resource
private StateMachine<OrderStatus, OrderStatusChangeEvent> orderStateMachine;
@OnTransition(source = "WAIT_PAYMENT", target = "WAIT_DELIVER")
@Transactional(rollbackFor = Exception.class)
public void payTransition(Message<OrderStatusChangeEvent> message) {
Order order = (Order) message.getHeaders().get("order");
log.info("支付,状态机反馈信息:{}", message.getHeaders().toString());
try {
//更新订单
order.setStatus(OrderStatus.WAIT_DELIVER.getKey());
orderMapper.updateById(order);
//TODO 其他业务
//模拟异常
if(Objects.equals(order.getName(),"A")){
throw new RuntimeException("执行业务异常");
}
} catch (Exception e) {
//如果出现异常,记录异常信息,抛出异常信息进行回滚
log.error("payTransition 出现异常:{}",e);
throw e;
}
}
监听事件抛出异常,在发送事件中无法感知:
private synchronized boolean sendEvent(OrderStatusChangeEvent changeEvent, Order order) {
boolean result = false;
try {
//启动状态机
orderStateMachine.start();
//尝试恢复状态机状态
stateMachineMemPersister.restore(orderStateMachine, String.valueOf(order.getId()));
Message message = MessageBuilder.withPayload(changeEvent).setHeader("order", order).build();
//事件执行异常了,依然返回true,无法感知异常
result = orderStateMachine.sendEvent(message);
if(result){
//持久化状态机状态,如果根据true持久化,则会出现问题
stateMachineMemPersister.persist(orderStateMachine, String.valueOf(order.getId()));
}
} catch (Exception e) {
log.error("订单操作失败:{}", e);
} finally {
orderStateMachine.stop();
}
return result;
}
调试发现:发送事件和监听事件是一个线程,发送事件的结果是在监听操作执行完之后才返回
监听线程:
解决方案:自己保存异常到数据库或者内存中,进行判断
也可以通过接口:org.springframework.statemachine.StateMachine##getExtendedState
方法把执行状态放入这个变量中
public interface ExtendedState {
Map<Object, Object> getVariables();
<T> T get(Object var1, Class<T> var2);
void setExtendedStateChangeListener(ExtendedState.ExtendedStateChangeListener var1);
public interface ExtendedStateChangeListener {
void changed(Object var1, Object var2);
}
}
org.springframework.statemachine.support.DefaultExtendedState##getVariables
private final Map<Object, Object> variables;
public DefaultExtendedState() {
this.variables = new ObservableMap(new ConcurrentHashMap(), new DefaultExtendedState.LocalMapChangeListener());
}
public Map<Object, Object> getVariables() {
return this.variables;
}
改造监听状态:把业务的执行结果进行保存,1成功,0失败
@Resource
private OrderMapper orderMapper;
@Resource
private StateMachine<OrderStatus, OrderStatusChangeEvent> orderStateMachine;
@OnTransition(source = "WAIT_PAYMENT", target = "WAIT_DELIVER")
@Transactional(rollbackFor = Exception.class)
public void payTransition(Message<OrderStatusChangeEvent> message) {
Order order = (Order) message.getHeaders().get("order");
log.info("支付,状态机反馈信息:{}", message.getHeaders().toString());
try {
//更新订单
order.setStatus(OrderStatus.WAIT_DELIVER.getKey());
orderMapper.updateById(order);
//TODO 其他业务
//模拟异常
if(Objects.equals(order.getName(),"A")){
throw new RuntimeException("执行业务异常");
}
//成功 则为1
orderStateMachine.getExtendedState().getVariables().put(CommonConstants.payTransition+order.getId(),1);
} catch (Exception e) {
//如果出现异常,则进行回滚
log.error("payTransition 出现异常:{}",e);
//将异常信息变量信息中,失败则为0
orderStateMachine.getExtendedState().getVariables().put(CommonConstants.payTransition+order.getId(), 0);
throw e;
}
}
发送事件改造:如果获取到业务执行异常,则返回失败,不进行状态机持久化 com.zengqingfa.springboot.state.demo.service.impl.OrderServiceImpl##sendEvent
@Resource
private StateMachine<OrderStatus, OrderStatusChangeEvent> orderStateMachine;
@Resource
private StateMachinePersister<OrderStatus, OrderStatusChangeEvent, String> stateMachineMemPersister;
/**
* 发送订单状态转换事件
* synchronized修饰保证这个方法是线程安全的
*
* @param changeEvent
* @param order
* @return
*/
private synchronized boolean sendEvent(OrderStatusChangeEvent changeEvent, Order order){
boolean result = false;
try {
//启动状态机
orderStateMachine.start();
//尝试恢复状态机状态
stateMachineMemPersister.restore(orderStateMachine, String.valueOf(order.getId()));
Message message = MessageBuilder.withPayload(changeEvent).setHeader("order", order).build();
result = orderStateMachine.sendEvent(message);
if(!result){
return false;
}
//获取到监听的结果信息
Integer o = (Integer) orderStateMachine.getExtendedState().getVariables().get(CommonConstants.payTransition + order.getId());
//操作完成之后,删除本次对应的key信息
orderStateMachine.getExtendedState().getVariables().remove(CommonConstants.payTransition+order.getId());
//如果事务执行成功,则持久化状态机
if(Objects.equals(1,Integer.valueOf(o))){
//持久化状态机状态
stateMachineMemPersister.persist(orderStateMachine, String.valueOf(order.getId()));
}else {
//订单执行业务异常
return false;
}
} catch (Exception e) {
log.error("订单操作失败:{}", e);
} finally {
orderStateMachine.stop();
}
return result;
}
代码优化
- 发送事件只针对了支付,如果是非支付事件呢?
//获取到监听的结果信息
Integer o = (Integer) orderStateMachine.getExtendedState().getVariables().get(CommonConstants.payTransition + order.getId());
- 监听设置状态的代码有重复代码,需要进行优化,可使用aop
try {
//TODO 其他业务
//成功 则为1
orderStateMachine.getExtendedState().getVariables().put(CommonConstants.payTransition+order.getId(),1);
} catch (Exception e) {
//如果出现异常,则进行回滚
log.error("payTransition 出现异常:{}",e);
//将异常信息变量信息中,失败则为0
orderStateMachine.getExtendedState().getVariables().put(CommonConstants.payTransition+order.getId(), 0);
throw e;
}
常量类:
public interface CommonConstants {
String orderHeader="order";
String payTransition="payTransition";
String deliverTransition="deliverTransition";
String receiveTransition="receiveTransition";
}
支付发送事件:com.zengqingfa.springboot.state.demo.service.impl.OrderServiceImpl##pay
@Resource
private StateMachine<OrderStatus, OrderStatusChangeEvent> orderStateMachine;
@Resource
private StateMachinePersister<OrderStatus, OrderStatusChangeEvent, String> stateMachineMemPersister;
@Resource
private OrderMapper orderMapper;
/**
* 对订单进行支付
*
* @param id
* @return
*/
public Order pay(Long id) {
Order order = orderMapper.selectById(id);
log.info("线程名称:{},尝试支付,订单号:{}" ,Thread.currentThread().getName() , id);
if (!sendEvent(OrderStatusChangeEvent.PAYED, order,CommonConstants.payTransition)) {
log.error("线程名称:{},支付失败, 状态异常,订单信息:{}", Thread.currentThread().getName(), order);
throw new RuntimeException("支付失败, 订单状态异常");
}
return order;
}
/**
* 发送订单状态转换事件
* synchronized修饰保证这个方法是线程安全的
*
* @param changeEvent
* @param order
* @return
*/
private synchronized boolean sendEvent(OrderStatusChangeEvent changeEvent, Order order,String key){
boolean result = false;
try {
//启动状态机
orderStateMachine.start();
//尝试恢复状态机状态
stateMachineMemPersister.restore(orderStateMachine, String.valueOf(order.getId()));
Message message = MessageBuilder.withPayload(changeEvent).setHeader("order", order).build();
result = orderStateMachine.sendEvent(message);
if(!result){
return false;
}
//获取到监听的结果信息
Integer o = (Integer) orderStateMachine.getExtendedState().getVariables().get(key + order.getId());
//操作完成之后,删除本次对应的key信息
orderStateMachine.getExtendedState().getVariables().remove(key+order.getId());
//如果事务执行成功,则持久化状态机
if(Objects.equals(1,Integer.valueOf(o))){
//持久化状态机状态
stateMachineMemPersister.persist(orderStateMachine, String.valueOf(order.getId()));
}else {
//订单执行业务异常
return false;
}
} catch (Exception e) {
log.error("订单操作失败:{}", e);
} finally {
orderStateMachine.stop();
}
return result;
}
使用aop对监听事件切面,把业务执行结果封装到状态机的变量中,注解:
@Retention(RetentionPolicy.RUNTIME)
public @interface LogResult {
/**
*执行的业务key
*
* @return String
*/
String key();
}
切面:
@Component
@Aspect
@Slf4j
public class LogResultAspect {
//拦截 LogHistory注解
@Pointcut("@annotation(com.zengqingfa.springboot.state.demo.aop.annotation.LogResult)")
private void logResultPointCut() {
//logResultPointCut 日志注解切点
}
@Resource
private StateMachine<OrderStatus, OrderStatusChangeEvent> orderStateMachine;
@Around("logResultPointCut()")
public Object logResultAround(ProceedingJoinPoint pjp) throws Throwable {
//获取参数
Object[] args = pjp.getArgs();
log.info("参数args:{}", args);
Message message = (Message) args[0];
Order order = (Order) message.getHeaders().get("order");
//获取方法
Method method = ((MethodSignature) pjp.getSignature()).getMethod();
// 获取LogHistory注解
LogResult logResult = method.getAnnotation(LogResult.class);
String key = logResult.key();
Object returnVal = null;
try {
//执行方法
returnVal = pjp.proceed();
//如果业务执行正常,则保存信息
//成功 则为1
orderStateMachine.getExtendedState().getVariables().put(key + order.getId(), 1);
} catch (Throwable e) {
log.error("e:{}", e.getMessage());
//如果业务执行异常,则保存信息
//将异常信息变量信息中,失败则为0
orderStateMachine.getExtendedState().getVariables().put(key + order.getId(), 0);
throw e;
}
return returnVal;
}
}
监听类使用注解:
@Component("orderStateListener")
@WithStateMachine(name = "orderStateMachine")
@Slf4j
public class OrderStateListenerImpl {
@Resource
private OrderMapper orderMapper;
@OnTransition(source = "WAIT_PAYMENT", target = "WAIT_DELIVER")
@Transactional(rollbackFor = Exception.class)
@LogResult(key = CommonConstants.payTransition)
public void payTransition(Message<OrderStatusChangeEvent> message) {
Order order = (Order) message.getHeaders().get("order");
log.info("支付,状态机反馈信息:{}", message.getHeaders().toString());
//更新订单
order.setStatus(OrderStatus.WAIT_DELIVER.getKey());
orderMapper.updateById(order);
//TODO 其他业务
//模拟异常
if (Objects.equals(order.getName(), "A")) {
throw new RuntimeException("执行业务异常");
}
}
@OnTransition(source = "WAIT_DELIVER", target = "WAIT_RECEIVE")
@LogResult(key = CommonConstants.deliverTransition)
public void deliverTransition(Message<OrderStatusChangeEvent> message) {
Order order = (Order) message.getHeaders().get("order");
log.info("发货,状态机反馈信息:{}", message.getHeaders().toString());
//更新订单
order.setStatus(OrderStatus.WAIT_RECEIVE.getKey());
orderMapper.updateById(order);
//TODO 其他业务
}
@OnTransition(source = "WAIT_RECEIVE", target = "FINISH")
@LogResult(key = CommonConstants.receiveTransition)
public void receiveTransition(Message<OrderStatusChangeEvent> message) {
Order order = (Order) message.getHeaders().get("order");
log.info("确认收货,状态机反馈信息:{}", message.getHeaders().toString());
//更新订单
order.setStatus(OrderStatus.FINISH.getKey());
orderMapper.updateById(order);
//TODO 其他业务
}
}
文章转载自公众号: 码猿技术专栏