大家好,我是汤师爷~
应用交互是指不同应用结构之间的数据交换和通信方式。
在一个复杂的系统中,各个应用并不是孤立存在的,它们往往需要相互协作,才能完成更复杂的业务流程。
应用交互的设计就是为了确保这些系统和组件能够顺畅地“对话”,实现系统整体目标。
应用交互的形式有多种,包括同步调用、异步消息通信等。每种交互方式都有其特定的应用场景和优缺点。
通过合理的交互设计,系统中的各个部分能够高效协同,减少耦合度,增加系统的灵活性。
同时,良好的交互设计还能显著提升系统的性能和容错能力,即使在大流量访问、业务需求复杂的情况下,也依然保持稳定运行。
应用服务的上下游
应用服务是系统对外提供的核心业务功能。
应用服务也是如此,应用服务可以独立演化和实现,但它们并非完全独立,必须相互交互,才能实现整体系统目标。
如何设计应用服务之间的交互?首先需要了解清楚服务上下游的概念。
1、服务上下游的概念
服务的上下游关系可以通过DDD(领域驱动设计)的建模方法来定义,主要使用限界上下文(bounded context)和上下文映射(context mapping)这两个概念。
上下游表示上下文之间的依赖方向,下游需要了解上游的领域知识来实现业务,而上游不需要了解下游。
换句话说,上游服务不需要关心下游服务的存在,但下游服务的实现却依赖于上游服务提供的能力。
这个概念听起来有些抽象,确实让许多人犯迷糊。让我们通过线上商城的几个应用服务来具体说明:
- 用户服务:管理用户的账户信息,包括注册、登录、认证、个人资料等。处理用户的权限和角色管理。
- 商品服务:管理商品的基本信息,包括名称、描述、价格、图片、分类等。提供商品的查询、筛选和浏览功能。
- 库存服务:管理商品的库存数量。处理库存的预占、扣减和回补操作。
- 交易服务:处理订单的创建、修改、取消和查询。管理订单的状态和生命周期。
- 支付服务:处理支付事务,支持多种支付方式。管理支付状态。
- 履约服务:处理订单的履约,包括拣货、包装、发货等。管理物流信息和配送状态。
如图所示,我们可以看出各个服务的上下游关系。
商品服务和用户服务是上游服务,它们提供基础数据,其他服务依赖于这些数据。
交易服务位于中间位置。对用户服务和商品服务而言,交易服务是下游,因为它依赖于这两个服务的基础数据。
对库存服务来说,交易服务也是下游,因为交易下单过程中,需要库存服务来预占、扣减库存。
对履约服务而言,交易服务是上游,因为它提供订单数据,驱动后续的订单履约流程。
2、为什么要区分上下游?
区分上下游关系的核心目标是为了解耦。
"解耦"这个词相信大家都不陌生,但它的含义往往过于抽象和模糊。在这里,我们探讨一下解耦到底指什么。
耦合是指两个或多个结构之间的相互作用和影响。在软件开发中,这可以理解为不同模块、系统或团队之间的相互依赖和影响。
随着软件需要解决的业务问题越来越复杂,单个系统或团队很难独立实现业务目标。因此,解耦的目的并非完全消除耦合,而是减少不必要的依赖关系。
在上文中我们提到,上游服务不需要关心下游服务的存在,但下游服务的实现却依赖于上游服务提供的能力。
因此,当下游服务的团队迭代新功能时,无需评估是否影响上游服务,因为基于明确的上下游关系,能快速判断出不会影响上游服务。只需评估是否影响自己的下游服务。
例如,交易服务的功能发生变更时,只需通知履约服务的团队,评估是否会影响到他们,上游服务团队则无需知晓。
这种方式能大大减少影响面的评估工作,提高团队协作效率。
相反,如果上下游关系混乱,存在各种循环依赖,那么任何一个服务的改动都难以准确评估影响面。此时就需要召集所有服务的团队,逐一评估是否有影响。
实际场景中,每次项目会议都需要一屋子人才能评估出影响面,这样的协作效率极低。
3、上下游关系的核心使用场景
在软件研发过程中,上下游关系在许多关键场景中发挥着重要作用。
- 明确服务之间的依赖关系:上下游关系让开发者清晰地了解服务间的依赖。这有助于减少不合理的依赖,确保服务的独立性和模块化设计。同时,它也避免了服务间的循环依赖,降低了一个服务出现故障引发连锁反应的风险。
- 评估影响面:当上游服务变更时,可以预见其对下游服务的影响,从而制定相应的应对策略。
- 指导团队协作:上下游关系有助于明确各团队的职责和工作范围。上游团队需要考虑下游团队的需求,提供稳定的接口和服务;下游团队则需要适应上游的变化。
应用服务的交互方式
应用服务的交互方式多种多样,其中最主要的两种是同步调用和异步消息。
1、同步调用
同步调用是一种通信方式,其中调用方(客户端)向被调用方(服务端)发送请求,并等待服务端处理完成后返回结果。在此期间,调用方会阻塞,直到收到服务端的响应。这种方式要求调用方和被调用方同时在线,且调用方在等待响应期间无法执行其他操作。
在微服务架构中,同步调用的典型技术实现协议包括HTTP、REST API、Dubbo、Thrift、gRPC和SOAP等。
同步调用适用于下游服务需要立即获取上游服务的数据或功能的场景。这种通信方式简单直接,但需要处理服务之间的可用性问题。
例如,用户下单时,订单服务需要同步调用商品服务,获取商品的最新价格和库存信息,以确保订单有效。
通常来说,上游服务不应同步调用下游服务。如果上游服务同步调用下游服务,会导致上游需要了解下游的领域知识,违背DDD上下游的设计原则,加深系统耦合,并增加团队协作复杂性。
此外,这种做法还可能引发级联故障,降低系统可靠性。如果上下游直接互相调用,那下游服务发生故障也将直接影响上游服务的可用性,可能导致整个系统都不可用。
2、异步消息
异步消息是另一种通信方式,其中消息的发送者(生产者)和接收者(消费者)通过消息队列或消息中间件进行通信。
发送者无需等待接收者处理完成即可继续其他操作。消息被发送到消息队列后,接收者从队列中异步获取并处理。这种方式将发送者和接收者的时间依赖解除,让两者能够独立运作,提高了系统的灵活性和可扩展性。
在微服务架构中,异步消息通常通过消息中间件实现,如RabbitMQ、Kafka和RocketMQ等。
异步消息适用于上游服务向下游服务发布事件或通知的场景,能有效解耦服务,提高系统的弹性和可靠性。下游服务也可通过异步消息向上游服务反馈信息,实现双向通信。
例如,当用户提交订单后,订单服务调用支付服务发起支付。用户完成支付后,支付服务发布"支付成功"消息,订单服务接收该消息后,更新订单状态并发送通知。
3、其他交互方式
1)共享数据库方式
多个服务访问同一个数据库,直接读取或写入数据。
在微服务架构中,通常不建议采用共享数据库的方式,因为它违反了服务自治原则,增加了服务间的耦合度。
2)文件传输
服务之间通过共享文件系统或FTP等方式交换数据文件。这种交互方式通常是批处理的,实时性较差。
3)服务总线(ESB)
使用统一的通信总线来连接不同的服务和系统。服务之间不直接通信,而是通过总线中介,适用于需要集成多种异构系统和服务的大型企业级系统。
然而,这种方式引入了额外的架构层,增加了系统复杂性。所有服务都耦合到总线上,存在单点故障风险。
本文已收录于,我的技术网站:tangshiye.cn 里面有,算法Leetcode详解,面试八股文、BAT面试真题、简历模版、架构设计,等经验分享。