我们已经拥有众多 API 架构风格,例如 REST、RPC和 SOAP 等,将命令模式添加到其中具有以下优势,它可以成为这一角色的良好候选者:
- 提供一种对交易事务进行建模的方法。命令共享一个通用接口,并且可以一次执行多个操作,因此非常适合此目的。
- 允许将用户操作保存为命令列表,这对于记录用户活动、重放操作或实施审计机制很有用。
- 提供撤消/重做功能。
- 遵循开放/封闭原则。可以轻松添加新命令,而无需修改现有代码。
- 增强可测试性,因为可以单独测试命令。
命令模式与 RPC比较
因为它们很相似:两种方法都涉及在服务器上执行任意操作。
一、概念不同
最明显的区别是:
- 命令模式通过命令进行操作
- 而 RPC 依赖于函数。
二、类似表述
尽管如此,它们在通过网络传输时看起来是一样的:
Cmd { prop1, prop1, … } -> cmd_type, prop1, prop2, … |
三、每次执行的操作数
与Command命令模式不同,RPC每次只能执行一个动作,不太方便,我们看下面的函数组成:
bar(foo())
RPC 建议发出两个请求或使用类似foobar 的函数来减少往返次数,后一种选择并不理想。
降低延迟问题的愿望又会影响通信接口,这个两难问题很广泛和复杂,所以,Cap'n Proto提供了自己的解决方案 solution ,更详细地描述了这个问题。
另一方面,函数组合对于命令模式来说不是问题:
FooBarCmd |
一般来说,它允许通过单个请求执行无限数量的操作,而不会增加接口复杂性或影响性能。
四、函数内部的命令
RPC 实际上可以使用命令模式来实现。在这种情况下,函数可以简单地向服务器发送命令:
func foo() { send FooCmd } |
因此,我们只需要知道如何处理命令。这些知识用途更广泛,甚至可以改进现有的 RPC 系统。
命令模式挑战
在提供更灵活和通用的抽象的同时,命令模式也引入了一些挑战:
1、需要服务器主动以某种方式区分一个命令与另一个命令。
- 解决方案:在每个命令之前,发送其类型。
2、命令必须返回结果,并且每个命令都可以有自己的结果。
- 解决方案:命令本身可以负责返回一个或多个结果,而不是返回结果。
Invoker |
3、命令在执行过程中可能会出错,如何处理?
解决方案:
- 如果想让客户端知道这个错误,命令可以返回错误结果。
- 另一种情况是,命令可以终止与客户端的连接,并向调用者返回错误。
Invoker |
为了限制其执行时间,命令必须知道服务器何时收到它。此时间可能与执行开始时间不同。解决方案:命令可以将其作为参数接收。
Invoker |
这就是适合我们需求的命令模式的样子。
要看到它的实际作用,我们需要考虑另外一件事。
序列化格式
要将数据发送到某个地方,必须先将其转换为字节序列。这可以通过多种方式完成,这就是为什么存在如此多的序列化格式。需要考虑的最重要的指标之一是格式使用的字节数。我们需要通过网络传输的字节数越少,我们的应用程序就会越快。
MUS 格式就是基于这些想法而创建的。它几乎不使用元数据,实际上是一种相当简单的格式。我不想重复太多,所以这里有一个 link 链接,你可以在那里阅读更多相关信息。
这就是理论的全部内容。
具体实现
上述想法已经以两个库的形式在 Golang 中实现:cmd-stream-go 和 mus-go。
1、cmd-stream-go
cmd-stream-go是一个高性能客户端-服务器库,它实现了命令模式并且:
- 可以通过 TCP、TLS 或相互 TLS 工作。
- 具有异步客户端,它仅使用一个连接来发送命令和接收结果。
- 支持服务器流式传输,即一个命令可以返回多个结果(不直接支持客户端流式传输,但也可以实现)。
- 支持重新连接功能。
- 支持保活功能。
- 可以与各种序列化格式一起使用。
- 具有模块化架构。
2、mus-go
mus-go是一个 MUS 格式的序列化器,它:
- 表示一组序列化原语,不仅可用于实现 MUS,还可用于实现其他序列化格式。
- 有流媒体版本。
- 可以在 32 位和 64 位系统上运行。
- 可以在解组时验证和跳过数据。
- 支持指针。
- 可以序列化图形或链表等数据结构。
- 支持数据版本控制。
- 支持 oneof 功能。
- 支持无序反序列化。
- 支持零分配反序列化。
此外,正如您在基准测试 benchmarks中所看到的,它表现出了出色的性能。
cmd-stream-go/MUS 比 gRPC/Protobuf快 3 倍左右。
概括
发送命令是一个非常好的抽象。它类似于 RPC,但并不限制我们只能执行一项操作。此外,命令模式既可以替代 RPC,也可以用作构建 RPC 的工具。它还提供了上述几个优点,并且已经具有高性能实现。所有这些都使命令模式成为 API 架构风格的绝佳选择。