微服务架构中,请求及其参数如何做存储处理?

95 天前
 steelshadow39
本人学生,在做项目的时候,遇到如下场景,想请教大家在实际公司项目中是如何解决类似问题的:

在第一次调用一个 POST 接口时,POST 请求体参数由用户前端填写,如果后端需要主动调用这个接口执行一次相同的任务,就必须要对这个 POST 请求的参数做持久化。做持久化就需要对请求中每个参数的字段做解析才能入库。

我使用 SpringCloudGateway 作为网关,管理模块和其他子模块都用 springboot 。我最初的想法是在网关层对请求做持久化,但由于每个请求体的参数对网关来说并不透明,网关需要对 JSON 请求体中的字段依次解析才能入库,而且我认为网关只用负责鉴权和路由,做持久化对整体性能肯定有影响(尽管项目规模小时并不明显)。

所以我现在的想法是,每个服务的请求由各自单独做持久化,因为请求体内的参数类型对负责处理它的接口来说都是透明的。如果用这种方式,是否还需要对每个请求参数做标识?这样管理模块才能够使用之前的参数来调用子模块的同一接口。管理模块和子模块一般是如何实现调度的呢?

以上表述难免有疏漏之处,如果大家还需要额外信息,我会及时补充,谢谢
2154 次点击
所在节点    程序员
22 条回复
julyclyde
95 天前
改改设计试试:

前端 POST 过来的那个,和实际干活的服务应该拆分
实际干活的服务可以被内部调用
steelshadow39
95 天前
@julyclyde #1 但内部调用实际干活的接口时,还是需要前端 POST 过来的参数
oldManNewThought
95 天前
环绕 aop 获取参数列表,取到后最好发 mq 。不影响接口性能
julyclyde
95 天前
@steelshadow39 那说明你的拆分边界错误吧?
julyclyde
95 天前
@steelshadow39
把实际干活的服务独立出来之后,它就是简单的参数输入、干活输出
至于参数怎么输入的,可以由多个调用方发过来,其中一个是原有的内部调用方,另一个是原来负责被 POST 的那一方
1+2 的模式

你原来的“内部需求也模拟一个 POST”是 1+1 的方式,我觉得边界错误
Avn
95 天前
持久化不一定「需要对请求中每个参数的字段做解析」,技术设计取决于对「管理模块」的重试功能的业务定位。

例如,如果「管理模块」的重试功能,定位成面向偏技术人员的接口调用记录,前端展示给管理人员的是一个包含服务名、接口地址、URL 参数、请求体、请求时间、响应体等字段的列表,然后有个重试按钮。那么持久化就要做到网关层,请求体不用解析直接用文本类型存 JSON 到表字段。重试的时候拿到请求的相关数据,构造 HTTP 请求发送出去。

如果「管理模块」的重试功能,定位成面向偏业务人员的业务请求记录,前端展示给管理人员的是一个包含订单号、卖方、买方、订单状态、订单金额、订单创建时间等字段的订单列表,然后有个扣款失败重新扣款、退款失败重新退款等等业务操作按钮。那么显然每个想要有重试功能的业务,都要有自己的前端页面,来展示自己的业务字段,相应的微服务也要自己解析相关的字段、持久化相关的数据、提供相关的重试接口,来完成自己的业务重试逻辑。

看你的描述很像是第一种面向技术人员的接口日志,这种情况不需要解析请求体,因为网关不关心请求体的含义,把日志记清楚、到时候能根据记录的信息把 HTTP 请求重新发一遍就好了。
orioleq
95 天前
不要用关系数据库,参数用 json 直接存
steelshadow39
95 天前
@Avn #6 非常感谢,您提到的两种情况我都有涉及,请求体信息(接口地址,时间等)我是准备在网关层持久化的,以便后续审计。但我的业务不像您提到的:必须要有前端显示,它可以理解为一次任务,第一次的任务配置需要用户前端填写,后续的任务执行都可以使用同一套配置。所以任务执行需求和审计需求是否都能用“直接存储 JSON 请求体”的方式满足呢?

如果是的话,每次管理模块调用子模块,就先从库中获取 JSON 请求体,再构造请求调用子模块接口。我还想过另外一种方案:管理模块不直接构造请求调用子模块,而是告诉子模块接口请求参数的存储地址,由子模块自行获取参数执行任务。对于前者,子模块接口接收不同类型的 RequestBody ,比如登录接口接收 LoginBody ,注册接口接收 RegisterBody 。对于后者,子模块接口的接受类型就固定了,都是“参数信息存储地址”。除此之外,我目前还没有想到这两种方式在实际开发中的优劣。
SorcererXW
95 天前
看看 opentelemetry 那一套,比较标准化,直接打 log 或者把请求体 attch 到 trace 上,具体存储都有现成的 opentelemetry exportor 。
vance123
95 天前
从楼主的回复来看,似乎是通过存储 body 的方式来同时实现审计和回放的目的,一表两用,节省存储空间和编码逻辑。如果是这样,我的建议是:
不要这样做,A 是 A ,B 是 B 。审计可以通过打日志的方式实现,回放用到的参数另外存表
Hopetree
95 天前
存 json 不就不需要管结构了吗,而且你也说了做一次重复请求,那就是改都不用改,所以 json 就是最简单的
vance123
95 天前
别存 json ,如果接口参数变了怎么办?
steelshadow39
95 天前
@vance123 我这里的意思是:审计和重放都可以使用存 JSON 这种方式来实现,不是一表两用。确实应该按你说的,分开用,毕竟日志审计和重放还有差别,重放的话,管理模块还需要知道重放哪一条参数,所以我认为重放参数的持久化还要和某些属性关联,比如用户。
steelshadow39
95 天前
@vance123 接口参数变的话,就存一个新的请求信息吧,不对原来的做修改
thevita
95 天前
关键是这个 “如果后端需要主动调用这个接口执行一次相同的任务” 吧,这里初步看,应该是业务设计的问题,

op 这里纠结的就是 是在这里用某种通用技术实现一个 通用的“重放” 方案呢,还是各个业务自己搞

我是没看明白 管理模块 为什么要重放,倾向于认为设计错误

场景我其实也没完全搞明白,只能说下经验:不要把技术设计和业务设计放在一起做
thevita
95 天前
@thevita 我能想到比较像的场景 eg:

比如我们有一些业务动作,可以前端直接触发,也可以有个 scheduler ( cron 等)调度触发

我想一般的做法这些 “业务动作” 属于 ”业务层“,而前面的 rest api/ scheduler , 只是这个业务 对外暴露的两个”前端“,所以逻辑上,这两部分应该是完全分开的,只是这两者可能存在事实共享部分代码(比如数据结构等),那可以用一些具体的技术手段来解决,所以我认为应该是逻辑上先得分开,再考虑能不能共享部分代码(因为这两部解决的问题不一样,先理清业务关系,考虑共享代码,只是为了解决,这块领域知识的维护问题)
steelshadow39
95 天前
@thevita 我的需求和您提到的 schedule 定时任务很像,第一次用户手动配置,请求接口,执行扫描任务。然后会设定这个扫描任务定期执行一次,我纠结的就是每次执行如何获取第一次请求中的参数配置。
akira
95 天前
@steelshadow39 那你这个就是伪需求。 先保存 再执行。
kaelamiki
95 天前
建议 op 把原始需求提出来,避免 xy 问题
itfisher
95 天前
@steelshadow39
@steelshadow39 如果是要记录用户请求&后续定时任务捞请求重放,把请求 url 路径和 req 保存下来就好( aop 获取数据,走异步 mq 保存)。
//另外:
不过看你的描述,感觉你场景更像是用户配置规则参数,然后根据规则后端来跑任务。如果是这种场景的话,其实更好的交互是,用户只能操作规则变更。 然后后段再给一个用户触发规则的操作,这个操作是: [读取规则&执行规则] ,那么后端的定时任务,代码就也可以是: [读取规则&执行规则] ,这样子逻辑会更统一一些

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

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

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

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

© 2021 V2EX