service 层会调用 tasks 任务
tasks 任务里边也会循环调用自身。
然后就依赖循环了,对于这种,大家怎么解决。
现在最 low 的办法,就是直接写两份代码。
1
zihuyishi 2024-08-09 17:33:27 +08:00
抽出接口呗,这不是 jawa 常用手段,所以还是要多写写 jawa 呀
|
![]() |
2
czyt 2024-08-09 17:34:36 +08:00
拆分
|
![]() |
3
body007 2024-08-09 17:39:29 +08:00 ![]() A 依赖 B 的代码,和 B 依赖 A 的代码,提取出来放到 C 包里面,这样 A 依赖 C ,B 依赖 C 就行了。
|
4
FanGanXS 2024-08-09 17:40:24 +08:00
中间多加一层,让 service 依赖于这一层,task 也依赖这一层。
|
5
lt0136 2024-08-09 17:43:25 +08:00 via Android
最简单的办法:service 和 task 写在一个 package 里
|
![]() |
6
fishofcat 2024-08-09 17:46:28 +08:00
抽象出来到别的包啊
|
![]() |
7
maxwellz 2024-08-09 17:47:37 +08:00
哈哈哈,之前也遇到过,最简单的方法就是单独建一个包,让 A 依赖 C ,B 依赖 C ,要么就是通过接口来解耦
其实归结起来还是调用职责没划分好,要避免同级 package 互相调用 |
8
awanganddong OP 大家说的我理解了,但是我不知道怎么入手,有 demo 吗
|
![]() |
9
yb2313 2024-08-09 18:26:54 +08:00 ![]() 遇到这种我一般直接打屁股😡
|
![]() |
10
povsister 2024-08-09 19:03:36 +08:00
遇到这种层次设计基本功有问题的,只能说
菜,就多练.jpg |
![]() |
11
younger027 2024-08-09 19:18:07 +08:00 ![]() 人家问问题,你会就解答,不会就闭嘴。最烦 10 楼的,讲了一堆 pi 话,来显示自己来了?
|
![]() |
13
Immortal 2024-08-09 19:28:39 +08:00
把公共部分抽出来,两个分别各自引用
|
![]() |
14
weiwenhao 2024-08-09 19:46:39 +08:00
把循环调用自身的逻辑抽离出来,放在单独的 service 或者其他 tasks 里面。
|
![]() |
15
yplam 2024-08-09 19:53:23 +08:00 via Android ![]() 参考依赖注入的模式,interface 抽出来,service 只依赖 interface ,然后在 main 或者写个容器进行服务初始化操作
|
16
yrj 2024-08-09 20:20:36 +08:00
设计的问题。task 的任务应该归 task 所有。不要拆到公共 service 里
|
![]() |
17
fgwmlhdkkkw 2024-08-09 20:22:13 +08:00 via Android
|
![]() |
18
Curtion 2024-08-09 22:09:51 +08:00
加中间层呀,还能怎么办
|
19
changz 2024-08-09 22:46:42 +08:00 via Android
爪蛙还是写得太少了 /狗头
|
20
gam2046 2024-08-09 23:29:34 +08:00
@awanganddong #8
ModuleA 需要调用 ModuleB.foo ModuleB 需要调用 ModuleA.foo --- 现在创建一个 ModuleC ,把原本 A/B 内的方法移过来,fooA/fooB ModuleA 调用 ModuleC.fooB ModuleB 调用 ModuleC.fooA |
![]() |
21
codebigbang 2024-08-09 23:31:48 +08:00
「没有什么是加个中间层不能解决的,如果有,就再加一层」-by 小白 debug
|
22
james122333 2024-08-10 00:04:20 +08:00 via Android
service 为何需要调用任务包的东西? 都是写在 service 层不是吗 非即时性的在 service 层写个队列在 service 层塞入供任务层取用即可 即时性的本身就该放在 service 层或者更底层 表层呼叫
不知道你在做什么 |
23
james122333 2024-08-10 00:08:44 +08:00 via Android
象牙多层球(鬼工球)知道吧?
|
![]() |
24
Shoukaku 2024-08-10 03:33:44 +08:00
人人都恨设计模式,人人都用设计模式😂
|
![]() |
25
Yoruno 2024-08-10 09:45:28 +08:00 via Android
可以把 task 或 service 对外使用 interface
|
26
lolizeppelin 2024-08-10 10:08:24 +08:00
前面都说得不够具体....
task 设计错误或者说抽象不足,没想清楚 task 到底要负责什么,边界是什么,想清楚就好办了 通常的 task 要么在排队,要么执行,而不是和服务的概念混在一起 简化的 task 设计 执行返回的对象是下一个 task,就可以不停执行了 如果你的任务还需要条件,那么把 task 设计成状态机或工作流 简单的 task 执行返回增加一些状态之类用于工作流流控制、延迟值用于延迟灯 这样你的服务就和 task 剥离了,如何被 task 调用或者调用 task 就简单了 你会 python 的话参考一下 openstack 的 taskflow 的设计就知道如何设计 task 了 |
27
w568w 2024-08-10 10:51:29 +08:00
这个本质上不是 Java 的问题,你换哪个语言都有这样的问题,Rust 、Dart 等新兴语言,你这么写也是报错,也得拆。
根本上,就像 #26 说的,是设计上的错误:楼主没有搞懂自己想要什么架构,只是随意地把模块放在名字相近的包里,然后需要哪个模块就去直接导入那整个包…… 然后就出问题了。 |
28
wwhontheway 2024-08-10 16:24:40 +08:00
边界的问题,service 应该是提供服务的主体,不应该调用 tasks
|
29
bugfan 2024-08-11 18:24:35 +08:00
楼上说的都是抽取出公共部分单独放一个包或者文件夹里。如果 service 调用 tasks 的代码函数不多,启动时候在 tasks 里把 service 需要调用的逻辑函数 Register 到 service 里面,让 service 调那个注册进去的东西。这样就不用写两份代码了~~
这风格有点像写 c 驱动程序,你试试吧,不知道行不行😊 |
![]() |
30
cheng6563 2024-08-12 09:15:13 +08:00 ![]() 业务层循环依赖是很正常的需求,不支持循环依赖才是问题.
|
31
awanganddong OP 我有一些悟了。
这是我依托 chatgpt 生成的目录结构。 这样就可以实现,从 service 和 task 内部对任务的调用。 下一个环节就是对调用的抽离,支持所有的 task 。( HandleTask )主要是这个方法。 package main import ( "context" "fmt" "log" "time" "github.com/hibiken/asynq" ) // 定义一个任务类型 const TaskType = "task:example" // 定义一个通用的 TaskEnqueuer 结构体 type TaskEnqueuer struct { Client *asynq.Client } // 公共的 EnqueueTask 方法 func (te *TaskEnqueuer) EnqueueTask(taskType string, payload interface{}, delay time.Duration) error { task := asynq.NewTask(taskType, asynq.PayloadFrom(payload)) _, err := te.Client.Enqueue(task, asynq.ProcessIn(delay)) return err } // TaskHandler 结构体,现在包含一个 TaskEnqueuer type TaskHandler struct { Enqueuer *TaskEnqueuer } // HandleTask 方法,用于处理任务 func (h *TaskHandler) HandleTask(ctx context.Context, task *asynq.Task) error { var depth int err := task.Payload().Unmarshal(&depth) if err != nil { return err } fmt.Printf("Executing task, Depth: %d\n", depth) if depth > 0 { // 调用公共的 EnqueueTask 方法,递归调用自身 return h.Enqueuer.EnqueueTask(TaskType, depth-1, 1*time.Second) } return nil } // NewTaskHandler 工厂函数,用于初始化 TaskHandler 和 TaskEnqueuer func NewTaskHandler(redisAddr string) (*TaskHandler, *asynq.Server) { r := asynq.RedisClientOpt{Addr: redisAddr} client := asynq.NewClient(r) enqueuer := &TaskEnqueuer{Client: client} server := asynq.NewServer(r, asynq.Config{ Concurrency: 10, }) return &TaskHandler{Enqueuer: enqueuer}, server } // SetupAndRunServer 函数用于设置和启动服务器 func SetupAndRunServer(server *asynq.Server, handler *TaskHandler) { mux := asynq.NewServeMux() mux.Handle(TaskType, asynq.HandlerFunc(handler.HandleTask)) if err := server.Run(mux); err != nil { log.Fatalf("could not run server: %v", err) } } // main 函数作为程序入口 func main() { redisAddr := "127.0.0.1:6379" handler, server := NewTaskHandler(redisAddr) defer handler.Enqueuer.Client.Close() // 初始化任务并加入队列 err := handler.Enqueuer.EnqueueTask(TaskType, 3, 0) // 递归深度为 3 ,立即执行 if err != nil { log.Fatalf("could not enqueue task: %v", err) } // 启动服务器处理任务 SetupAndRunServer(server, handler) } |
32
awanganddong OP 我定义 service 主要是处理业务逻辑。tasks 主要是队列相关,用的包是 asynq 。比如服务端一些定时器。我是在 tasks 触发,然后调用这个任务,然后这个任务执行完成之后,在十秒之后会再次执行。这时候就需要在 task 内调用这个任务。
如果这两个公用一个调用方法,就会依赖循环。 |
33
xiaozhang1997 2024-08-12 12:10:33 +08:00
别尬黑哦 java 不存在 go 的这种依赖问题 而且有些人是转语言,人家问一个比较成熟的优解,上面一群人修啥优越感呢
|
34
qq978746873 2024-08-12 14:17:14 +08:00
看看能不能用回调实现
|
![]() |
35
vczyh 2024-08-12 14:48:21 +08:00
感觉没说清楚,task 调用 service ,然后 service 又调用 task 了?
|
36
securityCoding 2024-08-15 09:33:57 +08:00 via Android
抽独立组件出来就好,没那么多讲究
|
37
8520ccc 2024-08-20 04:37:48 +08:00 via iPhone
拿一个目录来专门做接口
最好用上代码自动生成 写完代码自动生成接口,自动注册 |