03. 履约链路业务流程分析

792 阅读2分钟

订单履约状态通知乱序问题分析

  1. 第一步,调度出库,仓库操作员根据订单进行拣货出库;
  2. 第二步,物流配送,快递员将商品打包邮寄配送;
  3. 第三步,完成签收,消费者最终完成签收;

都会产生一个订单履约状态的消息,投递到RocketMQ,订单服务专门的线程负责消费处理,更新订单状态;

012_订单系统订单履约链路.png

分析一下订单履约状态通知乱序的问题,可能导致订单状态数据错乱;

通过将同一个订单的履约状态消息投递到同一个ConsumerQueue;

另外,思考一下,履约状态消息消费过程中,怎么保证幂等消费(分布式锁),同时,如果消费出现异常,又要怎么保证履约状态消息的顺序性(SUSPEND_CURRENT_QUEUE_A_MOMENT,消息挂起);

SendResult sendResult = defaultMQProducer.send(message, new MessageQueueSelector() {
	@Override
	public MessageQueue select(List<MessageQueue> mqs, Message message, Object arg) {
		//根据订单id选择发送queue
		String orderId = (String) arg;
		long index = hash(orderId) % mqs.size();
		return mqs.get((int) index);
	}
}, orderId);

013_订单履约链路(履约状态乱序问题)分析.png

需要关注一下这里设计模式的一些应用实战;

思考一下,如何保证 1. 履约服务,创建履约单;2. 通知仓储服务,调度出库发货;3. 通知物流服务,物流配送;强一致性问题;

引入 Seata Saga长事务机制,缺点,没有全局锁概念,没有写隔离机制;

适用场景,业务链路比较长,耗时较长的特定业务场景;

{
    "Name": "order_fulfill",
    "Comment": "订单履约流程",
    "StartState": "FulfillService",
    "Version": "1.0.0",
    "States": {
        "FulfillService": {
            "Type": "ServiceTask",
            "ServiceName": "fulfillSagaService",
            "ServiceMethod": "createFulfillOrder",
            "CompensateState": "CreateFulfillOrderCompensate",
            "Next": "ChoiceWmsState",
            "Input": [
                "$.[receiveFulfillRequest]"
            ],
            "Output": {
                "CreateFulfillOrderResult": "$.#root"
            },
            "Status": {
                "#root == true": "SU",
                "#root == false": "FA",
                "$Exception{java.lang.Throwable}": "UN"
            },
            "Catch": [
                {
                    "Exceptions": [
                        "java.lang.Throwable"
                    ],
                    "Next": "CompensationTrigger"
                }
            ]
        },
        "ChoiceWmsState":{
            "Type": "Choice",
            "Choices":[
                {
                    "Expression":"[CreateFulfillOrderResult] == true",
                    "Next":"WmsService"
                }
            ],
            "Default":"Fail"
        },
        "WmsService": {
            "Type": "ServiceTask",
            "ServiceName": "wmsSageService",
            "ServiceMethod": "pickGoods",
            "CompensateState": "WmsPickGoodsCompensate",
            "Next": "ChoiceTmsState",
            "Input": [
                "$.[receiveFulfillRequest]"
            ],
            "Output": {
                "WmsPickGoodsResult": "$.#root"
            },
            "Status": {
                "#root == true": "SU",
                "#root == false": "FA",
                "$Exception{java.lang.Throwable}": "UN"
            },
            "Catch": [
                {
                    "Exceptions": [
                        "java.lang.Throwable"
                    ],
                    "Next": "CompensationTrigger"
                }
            ]
        },
        "ChoiceTmsState":{
            "Type": "Choice",
            "Choices":[
                {
                    "Expression":"[WmsPickGoodsResult] == true",
                    "Next":"TmsService"
                }
            ],
            "Default":"Fail"
        },
        "TmsService": {
            "Type": "ServiceTask",
            "ServiceName": "tmsSagaService",
            "ServiceMethod": "sendOut",
            "CompensateState": "TmsSendOutCompensate",
            "Input": [
                "$.[receiveFulfillRequest]"
            ],
            "Output": {
                "TmsSendOutResult": "$.#root"
            },
            "Status": {
                "#root == true": "SU",
                "#root == false": "FA",
                "$Exception{java.lang.Throwable}": "UN"
            },
            "Catch": [
                {
                    "Exceptions": [
                        "java.lang.Throwable"
                    ],
                    "Next": "CompensationTrigger"
                }
            ],
            "Next": "Succeed"
        },
        "CreateFulfillOrderCompensate": {
            "Type": "ServiceTask",
            "ServiceName": "fulfillSagaService",
            "ServiceMethod": "createFulfillOrderCompensate",
            "Input": [
                "$.[receiveFulfillRequest]"
            ]
        },
        "WmsPickGoodsCompensate": {
            "Type": "ServiceTask",
            "ServiceName": "wmsSageService",
            "ServiceMethod": "pickGoodsCompensate",
            "Input": [
                "$.[receiveFulfillRequest]"
            ]
        },
        "TmsSendOutCompensate": {
            "Type": "ServiceTask",
            "ServiceName": "tmsSagaService",
            "ServiceMethod": "sendOutCompensate",
            "Input": [
                "$.[receiveFulfillRequest]"
            ]
        },
        "CompensationTrigger": {
            "Type": "CompensationTrigger",
            "Next": "Fail"
        },
        "Succeed": {
            "Type":"Succeed"
        },
        "Fail": {
            "Type":"Fail",
            "ErrorCode": "500",
            "Message": "订单履约异常!!"
        }
    }
}

014_订单履约链路全链路业务分析.png

Seata Saga 长事务运行原理

015_Seata Saga状态机运行原理分析.png

思考一下,空补偿,空回滚的问题,具体方案参考之前 Seata TCC 模式处理锁定库存的代码实现;