跳转到内容

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()
  • 你正在通过显式 routes 与 middleware 组装应用
  • 你要修改 PrepareServerShutdown 生命周期行为
  • 你正在处理应用构造或共享运行时依赖 wiring
  • 改动真正关心的是 route matching,而不是应用内核
  • 改动引入了 feature catalog、plugin container 或 tenant policy
  • 工作其实是持久化行为,或 debug-tool payload 设计
  1. core/module.yaml
  2. core/app.go
  3. core/dependencies.go
  4. reference/standard-service/internal/app/app.go
这些工作适合留在 core一旦变成这些问题就应移出
通过 DefaultConfig() / AppConfigAppDependencies 做显式 app construction应归 app-local code 或扩展层的 feature-specific wiring
单一路径生命周期:Prepare + Server + Shutdownx/observability/devtools 中的 debug metadata transport 与 runtime snapshot payload
通过 App.AddRouteApp.GetApp.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,只需覆盖与默认值不同的字段。

字段默认值说明
Addr:8080监听地址
TLS.Enabledfalse启用 TLS
TLS.CertFile""TLS 证书路径
TLS.KeyFile""TLS 私钥路径
Router.MethodNotAllowedfalse方法不匹配时返回 405 + Allow 头,而非 404
ReadTimeout30s读取完整请求的最长时间
ReadHeaderTimeout5s读取请求头的最长时间(防 slowloris)
WriteTimeout30s写响应的最长时间
IdleTimeout60skeep-alive 连接最长空闲时间
MaxHeaderBytes1 MiB请求头最大尺寸
HTTP2Enabledtrue启用 HTTP/2
DrainInterval500ms优雅关闭时打印在途连接数的间隔
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))
// 命名路由反查 URL
app.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.ServerShutdown 等待在途连接完成并遵守 context deadline。完整模式(含资源清理)见优雅退出

core 很容易被误读成”凡是基础设施都可以往里放”的地方。当前仓库的实际规则不是这样:core 是内核,不是 feature catalog。只要改动开始让内核学习能力语义,它大概率就已经放错地方了。