Core Primer
Core Primer
Section titled “Core Primer”当你已经通过 稳定根 确认改动仍属于默认服务路径,而且问题进一步收窄到 HTTP 应用内核时,就打开这一页。
core 拥有 app construction、route attachment、middleware attachment 与 server lifecycle。只有当改动应该继续属于每个 Plumego 服务都应理解的最小 canonical 服务形态时,它才是正确归属。
app := core.New(core.DefaultConfig(), core.AppDependencies{Logger: plog.NewLogger()})_ = app.Get("/ping", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { _ = contract.WriteResponse(w, r, http.StatusOK, map[string]string{"status": "ok"}, nil)}))app.Run()什么时候从这里开始
Section titled “什么时候从这里开始”- 你正在通过显式 routes 与 middleware 组装应用
- 你要修改
Prepare、Server或Shutdown生命周期行为 - 你正在处理应用构造或共享运行时依赖 wiring
什么时候不该从这里开始
Section titled “什么时候不该从这里开始”- 改动真正关心的是 route matching,而不是应用内核
- 改动引入了 feature catalog、plugin container 或 tenant policy
- 工作其实是持久化行为,或 debug-tool payload 设计
当前仓库里先读哪些文件
Section titled “当前仓库里先读哪些文件”core/module.yamlcore/app.gocore/dependencies.goreference/standard-service/internal/app/app.go
更具体的归属例子
Section titled “更具体的归属例子”这些工作适合留在 core | 一旦变成这些问题就应移出 |
|---|---|
通过 DefaultConfig() / AppConfig 与 AppDependencies 做显式 app construction | 应归 app-local code 或扩展层的 feature-specific wiring |
单一路径生命周期:Prepare + Server + Shutdown | x/observability/devtools 中的 debug metadata transport 与 runtime snapshot payload |
通过 App.AddRoute、App.Get、App.Post 与命名路由 URL lookup 做 route binding | 原始 router 替换、feature catalog 或能力归属的运行时行为 |
| logger 这类被动依赖注入 | logger 子系统 ownership、flush policy 或应保持 app-local 的 readiness policy |
import "github.com/spcent/plumego/core"cfg := core.DefaultConfig() // 默认值:监听 :8080,超时 30s,HTTP/2 开启cfg.Addr = ":9090" // 覆盖任意字段后再传入 New
app := core.New(cfg, core.AppDependencies{ Logger: myLogger, // 不传则丢弃所有日志})DefaultConfig 返回一个随时可用的 AppConfig,只需覆盖与默认值不同的字段。
AppConfig 字段说明
Section titled “AppConfig 字段说明”| 字段 | 默认值 | 说明 |
|---|---|---|
Addr | :8080 | 监听地址 |
TLS.Enabled | false | 启用 TLS |
TLS.CertFile | "" | TLS 证书路径 |
TLS.KeyFile | "" | TLS 私钥路径 |
Router.MethodNotAllowed | false | 方法不匹配时返回 405 + Allow 头,而非 404 |
ReadTimeout | 30s | 读取完整请求的最长时间 |
ReadHeaderTimeout | 5s | 读取请求头的最长时间(防 slowloris) |
WriteTimeout | 30s | 写响应的最长时间 |
IdleTimeout | 60s | keep-alive 连接最长空闲时间 |
MaxHeaderBytes | 1 MiB | 请求头最大尺寸 |
HTTP2Enabled | true | 启用 HTTP/2 |
DrainInterval | 500ms | 优雅关闭时打印在途连接数的间隔 |
app.Get("/api/hello", http.HandlerFunc(handler.Hello))app.Post("/api/items", http.HandlerFunc(handler.CreateItem))app.Put("/api/items/:id", http.HandlerFunc(handler.UpdateItem))app.Delete("/api/items/:id", http.HandlerFunc(handler.DeleteItem))
// 命名路由反查 URLapp.AddRoute(http.MethodGet, "/api/users/:id", http.HandlerFunc(handler.GetUser), router.WithRouteName("user-detail"))url := app.URL("user-detail", "id", "42") // → "/api/users/42"import ( "github.com/spcent/plumego/middleware/requestid" "github.com/spcent/plumego/middleware/recovery" "github.com/spcent/plumego/middleware/accesslog")
recoveryMw, err := recovery.Middleware(recovery.Config{Logger: app.Logger()})if err != nil { return err}accesslogMw, err := accesslog.Middleware(accesslog.Config{Logger: app.Logger()})if err != nil { return err}app.Use(requestid.Middleware(), recoveryMw, accesslogMw)Use 必须在 Prepare 之前调用。中间件按注册顺序执行。
合并路径 — 适合大多数服务:
app.Run() // Prepare + ListenAndServe 一次完成;出错时记录日志并退出显式路径 — 当你需要在启动前检查或包装 server(TLS、连接追踪、OS 信号处理的优雅退出):
if err := app.Prepare(); err != nil { log.Fatalf("prepare: %v", err)}srv, err := app.Server()if err != nil { log.Fatalf("获取 server: %v", err)}
// 监听 OS 信号以实现优雅退出ctx, stop := signal.NotifyContext(context.Background(), syscall.SIGTERM, syscall.SIGINT)defer stop()
serverErr := make(chan error, 1)go func() { serverErr <- srv.ListenAndServe() }()
select {case err := <-serverErr: log.Fatalf("server 停止: %v", err)case <-ctx.Done(): // 收到信号——开始优雅排空}
shutdownCtx, cancel := context.WithTimeout(context.Background(), 15*time.Second)defer cancel()_ = app.Shutdown(shutdownCtx)Prepare 冻结路由表并构建处理链。Server 返回 *http.Server。Shutdown 等待在途连接完成并遵守 context deadline。完整模式(含资源清理)见优雅退出。
为什么单独写这一页
Section titled “为什么单独写这一页”core 很容易被误读成”凡是基础设施都可以往里放”的地方。当前仓库的实际规则不是这样:core 是内核,不是 feature catalog。只要改动开始让内核学习能力语义,它大概率就已经放错地方了。
- core API 快速参考 — 完整函数签名和类型定义
- 配置模型
- 参考应用
- Middleware Primer
- 仓库边界