现在我们该履行合同了。第一步就是出库,我们假设库房管理系统已经存在,为此我们需要实现一个执行器来与库房系统通讯。但因为库房的拣货、打包涉及到人工和(或)机械设备的处理,时间很长,导致执行器执行超时,无法与Nature 协作,既同步的方式无法满足 Nature 的通讯要求。
一些限制说明:
在真实的情况中,一个订单可能包含不同的商品,而这些商品也可能分布在不同的库房中。本示例为了简单起见,假定所有的商品都在同一个库房里。
这里有两种解决方法,第一种方式是建立如下关系
, 用执行器将 Nature 的订单加工成出库单
并导入到库房系统。
-- orderState:paid --> stockOutApplication
INSERT INTO relation
(from_meta, to_meta, settings)
VALUES('B:sale/orderState:1', 'N:warehouse/outApplication:1', '{"selector":{"state_all":["paid"]},"executor":{"protocol":"localRust","url":"nature_demo:stock_out_application"}}');
- Nature 要点:请注意这里的
N:warehouse/outApplication:1
,我们之前并没有定义过,这是一个不存在的Meta
。 为了简化配置工作,对于没有存储意义的Meta
不需要定义就可以使用,出库单
是存储到库房系统里的,没有必要再在 Nature 里存储一份。我们用N:
来标记这样的Meta
,N 代表MetaType::Null
。warehouse/outApplication
只是助记符,N:warehouse/outApplication:1
完全可以写成N::1
,后面的版本号无论是多少都会被置为1,因为“空”有很多版本也没有意义。请参考 meta.md。 - Nature 要点:Nature 并不会因为不保存 MetaType::Null 类型的实例数据,而降低服务质量,同样的保障机制会作用在相关的执行器上。
出库单
进入库房系统后,人员及设备就可以开工了,当打包完成后就需要调用 Nature 的 input 接口来改变订单的状态,以驱动订单后面的流程。但这种方式,少了一些规范性和约束性,因为库房系统必须填写下面的信息如下:
- 目标
Meta
为:B:sale/orderState:1
- 将
instance
的状态置为 package - 设置状态的版本号
会有下面的问题:
- 这些信息必须通过编程的方式提交,这样程序员就必须要了解订单状态相关的知识,扩大了信息沟通和维护成本。
- 程序员可能会指定不规范的状态版本号,还有就是必须编程应对状态版本冲突的问题。
其实这两个问题都可以避免,这就是我们的第二中方法,也是本示例所采用的方法:利用 Nature 的回调机制。
当我们支付完成后,订单状态就会停在paid
上,直到库房系统给出一个新的状态,所以我们可以定义一个订单状态到订单状态的关系
。
-- orderState:paid --> orderState:package
INSERT INTO relation
(from_meta, to_meta, settings)
VALUES('B:sale/orderState:1', 'B:sale/orderState:1', '{"selector":{"state_all":["paid"]},"executor":{"protocol":"http","url":"http://localhost:8082/send_to_warehouse"},"target":{"state_add":["package"]}}');
-
Nature 要点:我们这里看到了一种新的执行器:
http
,借助它 Nature 可以在全球范围内编织一个庞大的系统。 -
Nature 要点:nature_demo_executor_restful 项目已经提供了对上面url的支持,实现逻辑大家可自行下载源码进行查看。
send_to_warehouse 的实现方式是这样的,将入参直接传递给一个新的线程(实际生产中,你可以采用更好的处理方式)来处理,自己什么也不做并直接返回 下面的结果给 Nature:
ConverterReturned::Delay(60)
这个的意思是说,我要晚会给你(Nature)结果,多晚呢?60秒内。
- Nature 要点:Nature 在
Delay
指定的时间内不会进行重试。如果不指定Delay
Nature 在没有得到响应的情况下,会在接下来的第2、4、8、16、32、64...秒(依据启动参数来确定)进行重试,直到有反馈为止。 - Nature 要点:这里的延迟时间是个技术问题,不是业务问题,所以就不放到
关系
里面进行配置了,如果放到那里反而不灵活了。
因为这是个Demo,我们只在50ms便返回了结果。当返回结果时我们不能调用 Nature 的 input 接口了,否则 Nature 挂起的任务会在将来的某个时刻重试。这里应当调用 Nature 的 callback
接口,它接受 DelayedInstances
类型的示例。请注意别忘了把执行器得到的 task_id 给带上,具体请看示例代码。
让我们来看下效果,运行:
nature.exe
nature_demo_executor_restful.exe
cargo.exe test --color=always --package nature-demo --lib emall::emall_test
结束后我们会发现有下面的数据产生:
ins_key | states | state_version | from_key |
---|---|---|---|
B:sale/orderState:1|3827f37003127855b32ea022daa04cd| | ["package"] | 3 | B:sale/orderState:1|3827f37003127855b32ea022daa04cd||2 |
打包对库房来说只是个中间状态,只是为了让顾客及时了解到货物的状态。我们还需要把货物放到出库区,让配送人员将货物拉走。这个出库
状态也可以走 Nature 回调的路子,实现状态的配置化,但为了演示如何向 Nature 提交状态数据,这里放弃了这种做法,而是直接将状态数据提交到 Nature 的 input 接口,具体请看示例代码。
- Nature 要点:一定要设置
instance.id
为要订单的ID,否则Nature 会分配一个新的ID,这将导致订单在系统中无法出库。 - Nature 要点:
state_version
必须要在原有的基础上加一,否则会引起冲突,无法处理。
让我们来看下效果,运行:
nature.exe
nature_demo_executor_restful.exe
cargo.exe test --color=always --package nature-demo --lib emall::emall_test
结束后我们会发现有下面的数据产生:
ins_key | states | state_version | from_key |
---|---|---|---|
B:sale/orderState:1|3827f37003127855b32ea022daa04cd| | ["outbound"] | 4 |
请留意这里的 from_key 为空,这是因为这条数据是外部输入时没有指定这个值,对于这种情况 Nature 不能自动填充这个值。
这里并不是刻意想着构建一个庞大的电商系统(当然 Nature 有这个能力),只是因为借助多库房来演示 Nature 的一种新技术:上下文选择。如想立马了解可以点击链接:附录-多个库房