V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
steelshadow39
V2EX  ›  程序员

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

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

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

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

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

    以上表述难免有疏漏之处,如果大家还需要额外信息,我会及时补充,谢谢
    第 1 条附言  ·  292 天前
    补充:

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

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

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

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

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

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

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

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

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

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

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

    我想一般的做法这些 “业务动作” 属于 ”业务层“,而前面的 rest api/ scheduler , 只是这个业务 对外暴露的两个”前端“,所以逻辑上,这两部分应该是完全分开的,只是这两者可能存在事实共享部分代码(比如数据结构等),那可以用一些具体的技术手段来解决,所以我认为应该是逻辑上先得分开,再考虑能不能共享部分代码(因为这两部解决的问题不一样,先理清业务关系,考虑共享代码,只是为了解决,这块领域知识的维护问题)
    steelshadow39
        17
    steelshadow39  
    OP
       292 天前
    @thevita 我的需求和您提到的 schedule 定时任务很像,第一次用户手动配置,请求接口,执行扫描任务。然后会设定这个扫描任务定期执行一次,我纠结的就是每次执行如何获取第一次请求中的参数配置。
    akira
        18
    akira  
       292 天前
    @steelshadow39 那你这个就是伪需求。 先保存 再执行。
    kaelamiki
        19
    kaelamiki  
       292 天前
    建议 op 把原始需求提出来,避免 xy 问题
    itfisher
        20
    itfisher  
       292 天前 via iPhone   ❤️ 1
    @steelshadow39
    @steelshadow39 如果是要记录用户请求&后续定时任务捞请求重放,把请求 url 路径和 req 保存下来就好( aop 获取数据,走异步 mq 保存)。
    //另外:
    不过看你的描述,感觉你场景更像是用户配置规则参数,然后根据规则后端来跑任务。如果是这种场景的话,其实更好的交互是,用户只能操作规则变更。 然后后段再给一个用户触发规则的操作,这个操作是: [读取规则&执行规则] ,那么后端的定时任务,代码就也可以是: [读取规则&执行规则] ,这样子逻辑会更统一一些
    xbo586
        21
    xbo586  
       291 天前
    个人理解。网关确实不应该掺杂多余业务逻辑,但是通过你的描述,只是类似于日志的功能,所以我认为在网关里做持久化是没问题的。至于数据结构问题,根据经验用 json 是最好的,也可以解决你的问题,毕竟你的请求数据结构也不清晰。然后就是你所讲的“对于后者,子模块接口的接受类型就固定了,都是“参数信息存储地址””,这种方案不可取,没必要在一个数据请求链条中增加一次接口信息的重复读。最后,我建议 20 楼的做法,将你所描述的扫描任务抽象成固定“规则”。
    abcbuzhiming
        22
    abcbuzhiming  
       291 天前
    @steelshadow39
    然后会设定这个扫描任务定期执行一次,我纠结的就是每次执行如何获取第一次请求中的参数配置
    =======
    这个想法好奇怪啊,你的任务第一次如何执行的,后面就如何执行。何来不能复用第一次请求中的参数呢?参数用 json ,甚至用 queryStr 键值对都能完成这个任务啊
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   1008 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 34ms · UTC 20:01 · PVG 04:01 · LAX 12:01 · JFK 15:01
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.