微服务架构中,请求及其参数如何做存储处理? - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
请不要在回答技术问题时复制粘贴 AI 生成的内容
steelshadow39
V2EX    程序员

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

  •  
  •   steelshadow39 2024-02-03 11:59:56 +08:00 2911 次点击
    这是一个创建于 615 天前的主题,其中的信息可能已经有所发展或是发生改变。
    本人学生,在做项目的时候,遇到如下场景,想请教大家在实际公司项目中是如何解决类似问题的:

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

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

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

    以上表述难免有疏漏之处,如果大家还需要额外信息,我会及时补充,谢谢
    第 1 条附言    2024-02-04 01:53:59 +08:00
    补充:

    上述场景其实就是用户能够在前端填写参数作为一次任务的配置参数,后端可以将该配置参数存储,后续的定时任务或管理模块的调用可以直接使用之前存储的配置参数。
    22 条回复    2024-02-04 16:36:26 +08:00
    julyclyde
        1
    julyclyde  
       2024-02-03 12:01:18 +08:00
    改改设计试试:

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

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

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

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

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

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

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

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

    场景我其实也没完全搞明白,只能说下经验:不要把技术设计和业务设计放在一起做
    thevita
        16
    thevita  
       2024-02-03 18:15:02 +08:00   1
    @thevita 我能想到比较像的场景 eg:

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

    我想一般的做法这些 “业务动作” 属于 ”业务层“,而前面的 rest api/ scheduler , 只是这个业务 对外暴露的两个”前端“,所以逻辑上,这两部分应该是完全分开的,只是这两者可能存在事实共享部分代码(比如数据结构等),那可以用一些具体的技术手段来解决,所以我认为应该是逻辑上先得分开,再考虑能不能共享部分代码(因为这两部解决的问题不一样,先理清业务关系,考虑共享代码,只是为了解决,这块领域知识的维护问题)
    steelshadow39
        17
    steelshadow39  
    OP
       2024-02-03 18:23:20 +08:00
    @thevita 我的需求和您提到的 schedule 定时任务很像,第一次用户手动配置,请求接口,执行扫描任务。然后会设定这个扫描任务定期执行一次,我纠结的就是每次执行如何获取第一次请求中的参数配置。
    akira
        18
    akira  
       2024-02-03 22:24:08 +08:00
    @steelshadow39 那你这个就是伪需求。 先保存 再执行。
    kaelamiki
        19
    kaelamiki  
       2024-02-03 22:35:33 +08:00
    建议 op 把原始需求提出来,避免 xy 问题
    itfisher
        20
    itfisher  
       2024-02-03 23:20:56 +08:00 via iPhone   1
    @steelshadow39
    @steelshadow39 如果是要记录用户请求&后续定时任务捞请求重放,把请求 url 路径和 req 保存下来就好( aop 获取数据,走异步 mq 保存)。
    //另外:
    不过看你的描述,感觉你场景更像是用户配置规则参数,然后根据规则后端来跑任务。如果是这种场景的话,其实更好的交互是,用户只能操作规则变更。 然后后段再给一个用户触发规则的操作,这个操作是: [读取规则&执行规则] ,那么后端的定时任务,代码就也可以是: [读取规则&执行规则] ,这样子逻辑会更统一一些
    xbo586
        21
    xbo586  
       2024-02-04 10:13:49 +08:00
    个人理解。网关确实不应该掺杂多余业务逻辑,但是通过你的描述,只是类似于日志的功能,所以我认为在网关里做持久化是没问题的。至于数据结构问题,根据经验用 json 是最好的,也可以解决你的问题,毕竟你的请求数据结构也不清晰。然后就是你所讲的“对于后者,子模块接口的接受类型就固定了,都是“参数信息存储地址””,这种方案不可取,没必要在一个数据请求链条中增加一次接口信息的重复读。最后,我建议 20 楼的做法,将你所描述的扫描任务抽象成固定“规则”。
    abcbuzhiming
        22
    abcbuzhiming  
       2024-02-04 16:36:26 +08:00
    @steelshadow39
    然后会设定这个扫描任务定期执行一次,我纠结的就是每次执行如何获取第一次请求中的参数配置
    =======
    这个想法好奇怪啊,你的任务第一次如何执行的,后面就如何执行。何来不能复用第一次请求中的参数呢?参数用 json ,甚至用 queryStr 键值对都能完成这个任务啊
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     5632 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 26ms UTC 06:32 PVG 14:32 LAX 23:32 JFK 02:32
    Do have faith in what you're doing.
    ubao snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86