golang 依赖循环的问题。

2024-08-09 17:31:51 +08:00
 awanganddong

service 层会调用 tasks 任务

tasks 任务里边也会循环调用自身。

然后就依赖循环了,对于这种,大家怎么解决。

现在最 low 的办法,就是直接写两份代码。

6090 次点击
所在节点    Go 编程语言
37 条回复
codebigbang
2024-08-09 23:31:48 +08:00
「没有什么是加个中间层不能解决的,如果有,就再加一层」-by 小白 debug
james122333
2024-08-10 00:04:20 +08:00
service 为何需要调用任务包的东西? 都是写在 service 层不是吗 非即时性的在 service 层写个队列在 service 层塞入供任务层取用即可 即时性的本身就该放在 service 层或者更底层 表层呼叫
不知道你在做什么
james122333
2024-08-10 00:08:44 +08:00
象牙多层球(鬼工球)知道吧?
Shoukaku
2024-08-10 03:33:44 +08:00
人人都恨设计模式,人人都用设计模式😂
Yoruno
2024-08-10 09:45:28 +08:00
可以把 task 或 service 对外使用 interface
lolizeppelin
2024-08-10 10:08:24 +08:00
前面都说得不够具体....

task 设计错误或者说抽象不足,没想清楚 task 到底要负责什么,边界是什么,想清楚就好办了
通常的 task 要么在排队,要么执行,而不是和服务的概念混在一起

简化的 task 设计
执行返回的对象是下一个 task,就可以不停执行了

如果你的任务还需要条件,那么把 task 设计成状态机或工作流
简单的 task 执行返回增加一些状态之类用于工作流流控制、延迟值用于延迟灯

这样你的服务就和 task 剥离了,如何被 task 调用或者调用 task 就简单了

你会 python 的话参考一下 openstack 的 taskflow 的设计就知道如何设计 task 了
w568w
2024-08-10 10:51:29 +08:00
这个本质上不是 Java 的问题,你换哪个语言都有这样的问题,Rust 、Dart 等新兴语言,你这么写也是报错,也得拆。

根本上,就像 #26 说的,是设计上的错误:楼主没有搞懂自己想要什么架构,只是随意地把模块放在名字相近的包里,然后需要哪个模块就去直接导入那整个包…… 然后就出问题了。
wwhontheway
2024-08-10 16:24:40 +08:00
边界的问题,service 应该是提供服务的主体,不应该调用 tasks
bugfan
2024-08-11 18:24:35 +08:00
楼上说的都是抽取出公共部分单独放一个包或者文件夹里。如果 service 调用 tasks 的代码函数不多,启动时候在 tasks 里把 service 需要调用的逻辑函数 Register 到 service 里面,让 service 调那个注册进去的东西。这样就不用写两份代码了~~

这风格有点像写 c 驱动程序,你试试吧,不知道行不行😊
cheng6563
2024-08-12 09:15:13 +08:00
业务层循环依赖是很正常的需求,不支持循环依赖才是问题.
awanganddong
2024-08-12 09:45:35 +08:00
我有一些悟了。
这是我依托 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)
}
awanganddong
2024-08-12 10:33:00 +08:00
我定义 service 主要是处理业务逻辑。tasks 主要是队列相关,用的包是 asynq 。比如服务端一些定时器。我是在 tasks 触发,然后调用这个任务,然后这个任务执行完成之后,在十秒之后会再次执行。这时候就需要在 task 内调用这个任务。
如果这两个公用一个调用方法,就会依赖循环。
xiaozhang1997
2024-08-12 12:10:33 +08:00
别尬黑哦 java 不存在 go 的这种依赖问题 而且有些人是转语言,人家问一个比较成熟的优解,上面一群人修啥优越感呢
qq978746873
2024-08-12 14:17:14 +08:00
看看能不能用回调实现
vczyh
2024-08-12 14:48:21 +08:00
感觉没说清楚,task 调用 service ,然后 service 又调用 task 了?
securityCoding
2024-08-15 09:33:57 +08:00
抽独立组件出来就好,没那么多讲究
8520ccc
2024-08-20 04:37:48 +08:00
拿一个目录来专门做接口

最好用上代码自动生成

写完代码自动生成接口,自动注册

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/1063855

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX