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

Java 多线程导致 Service 之间互相调用,有什么解耦或者优化办法吗?

  •  
  •   cencoroll · 2023-03-24 14:21:12 +08:00 · 2396 次点击
    这是一个创建于 616 天前的主题,其中的信息可能已经有所发展或是发生改变。

    目前碰到一个需求,serviceA 向外发送数据,走完前调用 serviceB 开启一条支线程去重复持续获取操作结果,直到获取结果为止,然后更新 serviceA 的数据状态。 这样导致了 serviceA 调用 serviceB ,然后 serviceB 又要调用 serviceA ,造成了循环调用。 求问各位大佬有什么办法优化一下,另外有多线程和设计模式的书推荐一下嘛?谢谢!

    12 条回复    2023-03-25 08:21:19 +08:00
    xiaohundun
        1
    xiaohundun  
       2023-03-24 14:26:52 +08:00
    这种为啥不用发布订阅或者基于事件的方式?
    shinyruo2020
        2
    shinyruo2020  
       2023-03-24 14:35:01 +08:00
    这不就是回调吗,serviceA 传一个 Function 给 serviceB
    liudaolunhuibl
        3
    liudaolunhuibl  
       2023-03-24 14:37:39 +08:00
    1 、你要解耦的话,就重新写一个 serviceC 来获取 A 的数据,serviceA 可以叫 producrService serviceC 就是 consumer Service ,A 负责发数据和写数据,C 负责读取查询,这样 A 发送完数据,调用 B ,B 调用 C 去查询数据,可以理解为读写分离
    2 、重复持续获取就是轮询吧 ,轮询不是非常的优雅,因为长时间的轮训会给 CPU 造成压力,最好是按照一楼说的基于事件驱动,也就是操作结果完成之后调用 B ,可以用 MQ 或者单节点就用谷歌的 eventBUs 或者 spring event
    3 、多线程和设计模式的书网上一搜一大把
    yazinnnn
        4
    yazinnnn  
       2023-03-24 14:51:18 +08:00
    你用的啥客户端, 异步却不支持设置回调?

    client
    .send(xxx)
    .onSuccess(result-> handle(result))
    .onFailure(error->handle(error))

    这种形式的代码都不支持码?
    litchinn
        5
    litchinn  
       2023-03-24 15:08:14 +08:00
    事件驱动
    serviceA 里发布事件,eventA.send()
    serviceB 作为一个 eventA 的监听器,执行完任务后再发送 eventB
    serviceA 里再包含一个 eventB 的监听器,监听到 B 事件后更新数据

    eventA 和 eventB 可以是同一个事件具有不同的状态,也可以是多个事件,看你的业务进行设计

    单体服务用多线程就可以了,分布式集群的话用 mq ,如 3 楼所说的方案
    cencoroll
        6
    cencoroll  
    OP
       2023-03-24 15:24:49 +08:00
    @liudaolunhuibl
    @xiaohundun
    @litchinn
    非常感谢,大概找到方向了,我研究一下。

    @shinyruo2020 回调不太符合需求,但也感谢解答

    @yazinnnn 程序其实是操作工控设备,将参数 A 下发以后,等待执行完毕后获取结果。但是这个等待的时长不确定,而且这个结果只需要更新到数据库就行,除非程序遇到情况报错了,否则前端不太需要查看执行结果。
    nothingistrue
        7
    nothingistrue  
       2023-03-24 16:24:00 +08:00
    如果把 serviceA 、serviceB 看成两个独立的微服务,那么这就不是互相调用,是分布式事件流,虽然看起来比较乱,但是并没有耦合,因为每个 service 都是自成一体,仅通过接口跟外部沟通的。

    不过你这个还是有点耦合,需要稍微拆解一下。改成如下过程:
    -> A 在队列( A 让 B 处理)当中发送一条任务
    -> B 从队列收到任务后,执行,得出结果后,在另一个队列( B 返回结果给 A )上,发送处理结果
    -> A 从队列收到结果后,执行后续处理(更新数据状态、向外发送等)。

    注意一点:如果 A 之前还有调用者,那么 A 是无法向调用者返回最终执行结果的,因为 A 在第一个过程就必须终止同步处理,后面的全是异步的。
    zhuisui
        8
    zhuisui  
       2023-03-24 17:08:17 +08:00
    解耦的关键是消除业务循环依赖关系,从来都不是程序的依赖关系。
    不管是程序内部的回调函数,还是程序外部的 rpc 或消息队列,或者是什么别的调用形式。
    A 委托 B 做一件事,那么 A 就要负责主动接受处理结果。
    几种调用方式上,提供回调明示了这个意图,其他异步方式较为隐晦。如果是 rpc ,应该 B 提供 rpc 给 A 进行委托处理和获取结果。如果是消息队列,应该由 B 声明两种工作的消息通道和内容。
    总结来说,就是 B 不知道 A 。
    winglight2016
        9
    winglight2016  
       2023-03-24 17:22:10 +08:00
    工控设备居然用 java 操作?一般不是用串口线走 mobus 协议,只管轮询 /更新数据区这种方式吗?

    想模拟这种方式的话,用 redis 充当数据区就可以了
    Pony69
        10
    Pony69  
       2023-03-24 17:51:42 +08:00
    mq 或者 redis 队列
    urnoob
        11
    urnoob  
       2023-03-24 18:33:48 +08:00
    AB 是
    两个 jvm ,没问题啊(又不是不能用)
    两个类的 instance
    合二为一
    抽到 C
    学多线程,避免 hang 在那
    学多线程知识,避免 hang 在那里
    cencoroll
        12
    cencoroll  
    OP
       2023-03-25 08:21:19 +08:00
    @winglight2016 这边大部分都是网口,西门子或是三菱的,直接通过 ip 地址读写数据块的信息。
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   2930 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 22ms · UTC 02:44 · PVG 10:44 · LAX 18:44 · JFK 21:44
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.