问个微服务调用问题。

2020-10-14 11:38:51 +08:00
 Hanggi
假设有三个服务,订单服务,商品服务和用户服务。

三个服务均独立部署,有独立数据库。

现在要调用订单列表接口,获取订单数组,每个订单要带着对应订单的商品信息,每个商品信息又要带着对应的用户信息(可能是卖家信息)。服务间均可以通过 Id 获取想要的数据。

这种场景下,最佳实践要怎么实现?
4514 次点击
所在节点    程序员
34 条回复
whevether
2020-10-14 17:03:27 +08:00
商城服务我觉得应该和订单服务为一个一个服务。 因为订单和商城耦合度高。且不通用。
libook
2020-10-14 17:49:28 +08:00
@zarte 我用微服务的方式跟用数据库表的方式是一样的,宏服务提供数据要聚合多个表的数据,那么微服务提供数据也要聚合多个服务的数据。

从例子上看,查询的主体是订单,订单里会存商品 ID,商品里会存卖家用户的 ID,所以首先是查订单,查出订单后根据订单里的商品 ID 去商品服务查商品信息,然后再根据商品信息里的卖家用户 ID 来查用户信息,因为商品、用户数据可能有重复,所以每一步查询条件可以去重,查出来的数据可以重用。
乍一看可能会觉得这样比较麻烦,这就是微服务拆分需要权衡的事情,我自己做订单业务的时候是把订单、商品、SKU 、活动优惠等放在一个支付微服务中,用户数据集中放在用户微服务上,相应的订单成交后发货和产生的权益又有各自的微服务,这样能做到既不会有过于繁重的跨服务调用,又能做好微服务之间的开发、性能、故障的隔离以及复用。
整体上建议微服务设计以加法为主(服务由少逐渐变多),一开始业务简单的时候可以做宏服务,等某一功能模块具备一定的规模和独立性后再考虑拆成微服务。
bl
2020-10-14 17:54:02 +08:00
搞一个聚合服务,由一方打包好数据给你,比如订单服务把商品服务打包一起给你。
zarte
2020-10-14 18:04:00 +08:00
@libook 受教了
zc1249274251
2020-10-14 18:06:25 +08:00
订单信息是晚于商品和用户信息的,换个思路我们去获取某用户的订单信息,其实我理解在订单服务里已经包含了该订单相关的信息,比如用户相关,商品相关,商家相关,物流相关等等(冗余),我们去获取订单列表信息时候只能拿到一些主要信息,如果冗余信息不能满足,再去调用其他的服务来获取,而且每一笔订单对应的流水号以及商品库的 sku 是固定的,一般不会修改
zc1249274251
2020-10-14 18:13:24 +08:00
@libook 请教个问题 查询主题是订单 后续由订单来引起其他操作 比如查用户 查卖家 查商品 有一个问题 同一商品 有相关营销策略的调整 sku 变化吗?
wangyzj
2020-10-14 18:14:21 +08:00
中间放一层 node 做这种组装
让前端自己去写
KingOfUSA
2020-10-14 18:23:53 +08:00
好问题。我也经常拿这个问题来面试。
正如#17 所说,「千万别把查询做成了数据统计」,但是在实际中很难(也懒得去分太清楚)查询和统计之间的边界,据我接触的需求来看,一开始订单列表包含商品,进而包括卖家信息,再包括买家信息,后续可能包括其他的信息等,所以这样的需求一般情况下是逐步的迭代形成的。

我个人有以下思路:
1. 和#5 一样
这样好处是不用考虑商品表结构变化(前提是商品服务不是由你维护),坏处是写 n 多代码

2. 放到数据仓库里面去查询,例如 es 里面
坏处是要搭建 es,使用成本有点高

3. 新开一个数据库实例,将用户库、订单库、商品库同步到这个实例里面,做跨库查询(具体同步的技术可以用 canal 、DTS,实在不行,自己用 mq 解决)
坏处是要维护表结构的同步,且需要了解基本的业务(例如哪个字段代表什么意思之类的),好处是查询起来很爽很爽
libook
2020-10-14 18:27:36 +08:00
@zc1249274251 没大看懂问题。

假设像楼主例子描述一样,因为各种原因最终这几部分确实拆成了几个独立的微服务,那么微服务之间的关系也是需要根据实际情况考虑的,比如你可以让 BFF 分别去调用订单服务、用户服务、商品服务,也可以让 BFF 去调用订单服务、让订单服务去调用商品服务、让商品服务去调用用户服务,一个微服务可能内部是由多个更小的微服务组成,调用链路可以根据需求优化缩短,总之一切都是要按照实际需求来权衡。
519718366
2020-10-14 18:53:06 +08:00
#28 楼总结的很好了,一看就是大佬👍
---
首先把我最想说的说出来:楼主这三个微服务一定不能为了信息聚合而相互依赖。他们就是 3 个独立的域。

所以我反对#29 楼里提到的在订单服务里去调用其他两个服务组装的方案。应该是在 BFF 里分别调用这三个独立的域去组装。(微服务难就难在边界,以及再未来的迭代中,大家一直能认识 /遵守着这个边界,保持这个微服务的干净)

大公司我比较赞成采用#28 楼的方法 2
小公司如果没有 solr 或者 es 这种条件,就先老老实实按方法 1,BFF 里分别调三个域的服务组装实现了先
newtype0092
2020-10-14 19:56:56 +08:00
@KingOfUSA #28 大佬请教下,如果是查询的情况,有排序需求的话是不是只能按 3 来跨库连表?
ShylockGou
2020-10-14 20:16:00 +08:00
订单交易数据不需要快照 /历史记录的么?😂
namelosw
2020-10-14 20:47:31 +08:00
这个东西没有完美解,微服务就是这样的,跟这些搏斗了几年,感觉有几种不同解:

1. 互相调用:优点直接,缺点增加依赖和循环依赖,后期会难维护

2. 外面一层用 Service 或者 BFF 聚合:优点是能让微服务继续独立,缺点有两种情况:
2.2 Service 也分层,分层的问题是分层会越来越多,ABC 公用 DE,DE 又公用 F,最后变成金字塔,总需要重构,要不问题就会大量重现
2.2 BFF,尽量避免 2.1 保持一层,问题是很多逻辑会跑到 BFF,最后发现微服务没干啥事

3. Event+读模型:各个 Service 互相消费 Event,然后建立自己所需的读模型。优点是不会导致以上设计问题,而且每个 Service 都有自己舒服的模型,缺点是复杂,最终一致,对基础设施要求高,而且 migration 啥的很难弄。

4. 共享数据库:不建议,这根本就不是微服务,不如把代码合回单体

5. 还有一些比较新的方案,比如 GraphQL Federation,有一个通用的 Gateway 可以根据同一个主键全自动聚合。看起来很不错,还没有深入使用过,不过感觉比较面向前端,后端 Service 绕回 Gateway 请求不知道会不会有问题

6. PS:很多时候真心还不如单体了,很多时候可测试性和可靠性都反被削弱了。之前用了一阵 Phoenix Framework,感觉只要技术栈本身编译快,运行快,其实没啥搞微服务的必要,除非 50 人以上的大 team,或者数据库有严重瓶颈,可能拆几个。很多 team 一共没 20 个人就搞一大堆微服务,基本都是自讨苦吃,浪费时间。
KingOfUSA
2020-10-15 10:30:06 +08:00
@newtype0092 ES 也可以做的。只是单纯的数据库(比如 mysql)查询对于大多数开发者来说上手最快。

我上面说的第三种指的是,把多个业务库的只读库放在同一个实例里面,单纯的用于查询(业务、一般的报表都可以)。

我个人倾向这种做法。

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/714791

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX