
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 |
&bsp; 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 拿一个目录来专门做接口 最好用上代码自动生成 写完代码自动生成接口,自动注册 |