DDD 领域驱动设计学习(七)- 接口与集成

系统只有在被集成后才会实现业务价值,软件供应商一般而言都比较分散,即使在同一个企业内部的不同团队之间,也容易形成沟通壁垒。这些都会造成后期系统集成的困难。如何高效集成往往也是很多项目执行的痛点。

集成限界上下文(BC)

一个项目中会存在多个BC,业务需要对它们进行集成。有多种直接的方法进行集成。最简单的方式就是一个BC中暴露API,然后在另外一个BC中通过RPC进行调用。另外我们也可以通过消息机制进行集成,系统通过消息队列或者发布-订阅机制进行通讯。第三种方式是通过使用RESTful的方式进行集成。当然,还存在有其他的集成方式。

分布式系统的特征

分布式系统不同于本地的进程内服务调用,RPC的方式解决了远程服务调用和系统垂直扩展问题,但是由此带来的服务调用性能下降和分布式系统的复杂性也会提高很多。
在我们实际的业务中,也遇到过改造一个分布式业务系统的目标,为了赶进度和便于实现,开始阶段是直接把原来内部调用方式修改为了分布式调用方式。结果在项目上,系统整体性能出现了严重下降情况,数据库的性能也出现了严重问题,不得己又对不少接口做了重新设计和整合。
另外采用分布式系统的部署,运维和故障排查难度也大大增加。从单体应用向分布式应用的变化确实不是简单的改变调用方式。从领域设计到系统接口设计需要充分考虑分布式系统的特性。

推荐以下一些分布式计算的原则,网络是不可靠的,要基于这些原则去考虑分布式系统的设计和实现方案:
1. 网络是不可靠的
2. 总会存在时间延迟,有时甚至非常严重
3. 带宽是有限的
4. 不要假设网络是安全的
5. 网络拓扑结构将发生变化
6. 知识和政策在多个管理员之间传播
7. 网络传输是有成本的
8. 网络是异构的

跨系统的信息交换

系统间需要可以交换消息才能实现业务。信息需要能被系统所识别,大多数人会选择一些标准的信息数据结构。以参数或消息的形式传送的信息数据只是机器能读懂的数据接口。有多种方式都可以生成信息数据的结构,例如XML,JSON,或者其他特殊格式。

在使用这些中间格式的时候,我们希望所有这些接口和类的部署到所有的系统中,然后使用工具把这些中间格式转化为类型安全的对象。但是,部署这些接口和类也意味着如果出现了新版本的类和接口,系统双方需要重新编译以保证系统可用和兼容(类似于Dubbo的方式)。另外使用外部对象实际上也违背了DDD的设计原则。即使采用共享内核的方式,对共享对象的访问过于方便,也有可能适得其反。

可以考虑设计一种契约,该契约用于不同系统之间创建可交换的信息接口,消费方使用时候也并不需要反序列化为对象。这种契约便是某种形式的发布语言。这种契约框架一般会对应开放主机服务(OHS) 的关系。

实现方式可以采用流行的RESTful架构,通过REST集成BC,为了增强客户端的自治性,还可以通过定时器或者消息机制来实现暂时解耦。另外还可以在客户方可以采用防腐层(ACL)实现REST的实现的封装和值对象转化。

另外也可以通过消息集成BC,通过领域事件驱动,把消息发送到订阅方去。为了避免消息处理场景中的各种异常和复杂场景,服务端的方法应该尽量采用无状态和幂等的方式来实现。

基于消息的长事务处理

跨系统的集成不可避免会产生长事务的问题,采用消息集成的方式也会存在消息顺序的乱序执行的风险,另外消息传送可靠性也是问题,如何解决这个问题是一个有趣的话题。

以RocketMQ为例(RocketMQ支持事务消息),业务场景采用一个转帐的例子来说明:Bob向Smith转账100块,并且Bob和Smith的账户及余额信息不在同一台服务器上。Bob账户扣100元和Smith账户增加100元就会被拆成两个小事务。两个小事务通过异步消息来同步。

 
小事务+异步消息
执行本地事务(Bob账户扣款)和发送异步消息应该保证同时成功或者同时失败,也就是扣款成功了,发送消息一定要成功,如果扣款失败了,就不能再发送消息。这种情况下,无论是先发消息还是后发消息都可能存在一致性的问题。RocketMQ支持事务消息,采用新方案如下所示:
 
 
RocketMQ事务消息

RocketMQ第一阶段发送Prepared消息,并拿到消息的地址,第二阶段执行本地事物,第三阶段通过第一阶段拿到的地址去访问消息,并修改消息的状态。如果确认消息发送失败了怎么办?RocketMQ会定期扫描消息集群中的事物消息,如果发现了Prepared消息,它会向消息发送端(生产者)确认,Bob的钱到底是减了还是没减呢?如果减了是回滚还是继续发送确认消息呢?RocketMQ会根据发送端设置的策略来决定是回滚还是继续发送确认消息。这样就保证了消息发送与本地事务同时成功或同时失败

 

回到转账的例子,Bob的账户的余额已经减少,且消息已经发送成功,Smith端开始消费这条消息,这个时候也会出现消费失败和消费超时两个问题,解决超时问题的思路就是一直重试。不过也要考虑适当的限流和熔断机制,否则可能会出现雪崩效应。

但是如果消费失败怎么办?阿里提供给我们的解决方法是:人工解决



作者:数行者
链接:https://www.jianshu.com/p/3bfca4cd4214
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

 

posted on 2022-02-23 13:50  1450811640  阅读(219)  评论(0编辑  收藏  举报