功能设计-如何设计一个回调功能
回调功能实现详解
一、回调功能概述
回调(Callback)是一种常见的异步编程模式,主要用于服务间的异步通信。当服务A需要服务B执行某个耗时操作时,服务A不会一直等待服务B完成,而是提供一个回调地址,服务B完成操作后主动通知服务A。
二、回调流程详解
1. 流程步骤
- 客户端触发操作:用户或系统通过客户端发起请求到服务A
- 服务A调用服务B:服务A将任务委托给服务B,并传递回调URL
- 服务B处理任务:服务B开始执行实际业务逻辑
- 服务B回调通知:任务完成后,服务B调用预先提供的回调URL
- 服务A后续处理:服务A收到回调后执行后续业务逻辑
2. 关键组件说明
- 回调URL:服务A暴露的HTTP端点,用于接收服务B的通知
- 异步调用:服务A调用服务B时不会阻塞等待
- 任务标识:通常需要唯一ID关联请求和回调(简化版未展示)
三、代码实现
服务A代码实现
- 双端点架构:
/start
:启动工作流/callback
:接收回调通知
- 关键处理逻辑:
- 收到启动请求后,立即调用服务B
- 同步响应客户端避免阻塞
- 在回调端点处理服务B完成通知
- 异步通信原则:
go callServiceB()
确保非阻塞调用- 回调机制解除服务依赖
package main
import (
"fmt"
"log"
"net/http"
"strings"
)
func main() {
fmt.Println("启动服务A (监听 :8080)")
// 设置HTTP路由
http.HandleFunc("/start", startHandler)
http.HandleFunc("/callback", callbackHandler)
log.Fatal(http.ListenAndServe(":8080", nil))
}
// 开始请求处理
func startHandler(w http.ResponseWriter, r *http.Request) {
fmt.Println("服务A: 收到新请求")
// 调用服务B
go callServiceB()
fmt.Fprint(w, "任务已提交,处理中...")
}
// 调用服务B
func callServiceB() {
fmt.Println("服务A: 调用服务B")
// 设置回调地址
callbackURL := "http://localhost:8080/callback"
// 构造服务B请求
serviceBURL := "http://localhost:8081/task"
payload := fmt.Sprintf(`{"callback_url": "%s"}`, callbackURL)
// 发送HTTP请求
_, err := http.Post(serviceBURL, "application/json",
strings.NewReader(payload))
if err != nil {
log.Printf("调用服务B失败: %v", err)
}
}
// 回调处理
func callbackHandler(w http.ResponseWriter, r *http.Request) {
fmt.Println("服务A: 收到服务B的回调")
fmt.Println("服务A: 执行后续操作...")
fmt.Fprint(w, "回调已接收")
}
服务B代码实现
- 任务处理核心:
- 接收包含回调URL的任务请求
- 解析JSON格式的回调地址
- 异步处理耗时任务
- 完成后发送回调通知
- 关键处理流程:
- 解析请求 → 验证参数 → 异步处理 → 发送回调
package main
import (
"encoding/json"
"fmt"
"log"
"net/http"
"strings"
"time"
)
func main() {
fmt.Println("启动服务B (监听 :8081)")
// 设置HTTP路由
http.HandleFunc("/task", taskHandler)
log.Fatal(http.ListenAndServe(":8081", nil))
}
// 请求结构体
type TaskRequest struct {
CallbackURL string `json:"callback_url"`
}
// 任务处理
func taskHandler(w http.ResponseWriter, r *http.Request) {
fmt.Println("服务B: 收到新任务")
// 解析JSON请求
var req TaskRequest
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
http.Error(w, "无效请求数据: "+err.Error(), http.StatusBadRequest)
log.Printf("请求解析错误: %v", err)
return
}
// 获取回调URL
callbackURL := req.CallbackURL
if callbackURL == "" {
http.Error(w, "缺少回调URL", http.StatusBadRequest)
return
}
fmt.Printf("服务B: 开始处理任务,回调URL: %s\n", callbackURL)
// 异步处理任务
go func() {
// 模拟处理时间(3秒)
time.Sleep(3 * time.Second)
fmt.Println("服务B: 任务处理完成")
// 发送回调通知
sendCallback(callbackURL)
}()
fmt.Fprint(w, "任务已开始处理")
}
// 发送回调
func sendCallback(callbackURL string) {
fmt.Printf("服务B: 发送回调至 %s\n", callbackURL)
// 发送回调通知
payload := `{"status": "完成", "message": "任务处理成功"}`
_, err := http.Post(callbackURL, "application/json", strings.NewReader(payload))
if err != nil {
log.Printf("回调发送失败: %v", err)
return
}
fmt.Println("服务B: 回调发送成功")
}
使用方式
- 首先启动两个服务
- 然后访问A服务的Start接口
- 然后两个服务就请求和回调成功了