调试后端应用
调试后端应用
本页说明如何在本地启动 Go 后端、配置 Unix Socket,并在极目桌面中完成前后端联调。
调试目标
后端进程通过 Unix Socket 监听 JSON-RPC 2.0 请求;极目桌面负责发现 Socket、执行健康检查,并将前端的统一请求转发给后端。后端不需要自行监听 HTTP 端口。
调试前提
开始前请确认:
- Go 版本满足项目要求(当前工程要求 Go 1.24+);
- 后端目录包含
main.go、config.json和i18n/; - 调试应用的服务类型选择 Socket(Unix Domain Socket);
- 调试应用中填写的是实际 Socket 文件的绝对路径,而不是后端目录路径。
启动后端服务
1. 进入后端目录
必须在 backend 目录启动服务,因为配置文件和语言文件使用相对路径加载:
cd /path/to/your-app/backend2. 初始化依赖(首次启动时)
make deps该命令会整理并下载 Go 模块依赖。依赖已经准备好时,可以直接执行下一步。
3. 启动服务
make runmake run 会创建 tmp/ 和 logs/ 目录,然后执行 go run .。
本地运行时,服务默认监听:
/path/to/your-app/backend/tmp/app.sock启动日志中应能看到类似信息:
JSON-RPC server listening on Unix socket: /path/to/your-app/backend/tmp/app.sock启动目录
不要从项目根目录直接运行 go run backend。应先进入 backend 目录,否则 config.json、i18n/ 和日志目录可能无法按预期加载。
配置调试应用
在 DevTools 的调试应用配置中,将 Socket 文件路径设置为后端实际生成的绝对路径:
/path/to/your-app/backend/tmp/app.sock配置时请注意:
- 路径必须指向
app.sock文件; - 后端进程启动后该文件才会出现;
- 调试应用中的组织名和应用名必须与后端
internal/common/constant.go中的OrgName、App和AppName一致; - 前端页面地址是另一项配置,不要把前端地址填写到 Socket 路径中。
健康检查
必须注册 ping
平台每隔 5 秒调用一次 ping 方法。后端必须注册该方法,并且只返回 pong:
func (s *Server) RegisterHandles(ds interface {
RegisterHandle(name string, handler func(*gsock.Request) (any, error), middlewares ...gsock.RPCMiddleware)
}) {
// 平台健康检查接口,不能删除或修改
ds.RegisterHandle("ping", s.Ping)
// 业务接口
ds.RegisterHandle("hello", s.Hello)
}
func (s *Server) Ping(req *gsock.Request) (any, error) {
return "pong", nil
}这里的 pong 是 RPC 方法的业务结果,不需要在 Handler 中手动拼接 {code, data, msg};响应包装由 RPC 服务负责。
不要在 ping 中加入业务逻辑
健康检查需要快速返回。不要在 ping 中执行数据库查询、文件扫描或其他耗时操作,也不要修改方法名或返回值。
健康检查失败时
按以下顺序排查:
- 后端进程是否仍在运行;
- 调试应用配置的 Socket 路径是否与日志中的路径完全一致;
ping是否已注册,返回值是否严格为"pong";- 启动目录是否为
backend,config.json和i18n/是否可读取。
调试业务 RPC
注册方法
每个业务方法都必须先注册路由。路由名使用 camelCase,并且会成为统一访问路径的最后一段:
ds.RegisterHandle("hello", s.Hello)
ds.RegisterHandle("getItemList", s.GetItemList)例如,hello 方法对应的统一入口是:
/api/call/{组织名}/{应用名}/hello解析参数
Handler 负责解析参数并调用 Service,不直接编写核心业务逻辑:
func (s *Server) Hello(req *gsock.Request) (any, error) {
var args dto.HelloReq
if err := rpcutil.ParseParams(req, &args); err != nil {
return nil, err
}
if args.Name == "" {
return nil, fmt.Errorf("%s", gi18n.Instance().T("NameRequired"))
}
return exampleService().Hello(args)
}rpcutil.ParseParams 会读取请求中的 params,将 JSON 字段解析到 DTO,并根据 params.lang 设置 i18n 语言。DTO 字段必须带有正确的 json tag。
处理请求
前端通过平台统一入口调用后端:
window.$gm.request('/api/call/official/example/hello', {
method: 'post',
data: {
version: window.$gm.version,
transport: window.$gm.communicationType,
params: {
lang: 'zh-CN',
name: 'world',
},
},
}).then((res) => {
console.log(res.data.data) // hello world
})调用前,有后端服务的应用应先完成 $gm.init(),再调用业务接口。
查看响应
RPC 服务会统一包装 Handler 的返回值:
{
"code": 200,
"data": "hello world",
"msg": "OK",
"meta": {
"endpoint": "hello",
"close": 0
}
}上面是应用后端通过 Unix Socket 返回的内层 RPC 响应。通过 /api/call/{组织名}/{应用名}/{方法名} 访问时,平台还会在外层返回统一状态码 200000,并将内层响应放在外层 data 中:
{
"code": 200000,
"data": {
"code": 200,
"data": "hello world",
"msg": "OK",
"meta": {
"endpoint": "hello",
"close": 0
}
},
"meta": {
"endpoint": "/api/call/*",
"close": 0
},
"msg": "Successful operation"
}平台 SDK 通常会把统一入口的结果整理为 $gm.request() 的 res.data,因此业务结果通常位于 res.data.data:
// res.data 对应上面的内层 RPC 响应
if (res.data.code === 200) {
console.log(res.data.data)
}如果直接检查统一入口的原始响应,则应先判断外层 code === 200000,再检查内层 data.code === 200。不要把这两个层级的状态码混用。
Handler 返回错误时,内层 RPC 响应的 code 会变为非 200,错误信息位于 msg。外层 code 表示平台是否成功处理统一入口请求,应与内层业务状态分别判断。未知方法会被 RPC 服务识别为路由不存在。
查看日志与断点
控制台日志
调试环境使用 config.json 中的 test 配置,日志会输出到控制台,并写入 backend/logs/:
tail -f logs/$(date +%F).log日志、配置文件和语言文件都以启动目录为相对路径,因此建议始终在 backend 目录执行 make run。
断点调试
在 internal/handler/rpc/ 或 internal/service/ 中设置断点后,使用 IDE 以 Go 程序方式启动 main.go,并保持以下条件不变:
- 工作目录为
backend; - 程序生成的 Socket 路径与 DevTools 配置一致;
config.json与i18n/位于工作目录下。
常见问题
| 现象 | 常见原因 | 处理方式 |
|---|---|---|
| 应用显示后端离线 | 进程未启动,或 Socket 路径配置错误。 | 在 backend 目录执行 make run,并将 DevTools 路径改为日志中显示的绝对路径。 |
| 健康检查失败 | ping 未注册、返回值被修改,或处理过程耗时。 | 确认注册了 ping,并让 Handler 直接返回 "pong"。 |
| 接口返回参数无效 | 请求缺少 params,或字段名与 DTO 的 json tag 不一致。 | 检查请求体层级,并确认业务字段位于 params 对象内。 |
| 配置或语言文件读取失败 | 启动工作目录不是 backend。 | 停止当前进程,进入 backend 后重新执行 make run。 |
| 前端页面能打开,但接口调用失败 | 未完成 $gm.init(),或 URL 中的应用标识与后端常量不一致。 | 等待初始化请求完成,并核对 /api/call/{组织名}/{应用名}/{方法名} 与 AppName。 |
| 返回方法不存在 | URL 中的方法名没有通过 RegisterHandle 注册。 | 检查方法名大小写和 camelCase 拼写,并重新启动后端。 |
停止服务
在运行 make run 的终端按 Ctrl+C。服务会收到退出信号并清理 Socket 连接;重新启动时会再次使用 tmp/app.sock。
