优化最小服务

This commit is contained in:
fancl 2024-04-29 10:52:19 +08:00
parent 281b44f637
commit 57c134b6ba
11 changed files with 114 additions and 91 deletions

View File

@ -191,8 +191,12 @@ func serializeArray(val []any) (buf []byte, err error) {
row := make([]any, 0, rv.Type().NumField()) row := make([]any, 0, rv.Type().NumField())
for j := 0; j < rv.Type().NumField(); j++ { for j := 0; j < rv.Type().NumField(); j++ {
st := rv.Type().Field(j).Tag st := rv.Type().Field(j).Tag
if columnName, ok = st.Lookup("name"); !ok { if columnName, ok = st.Lookup("kos"); !ok {
columnName = strings.ToUpper(rv.Type().Field(j).Name) columnName = strings.ToUpper(rv.Type().Field(j).Name)
} else {
if columnName == "-" {
continue
}
} }
row = append(row, columnName) row = append(row, columnName)
} }

View File

@ -10,6 +10,7 @@ import (
"runtime" "runtime"
"strings" "strings"
"sync" "sync"
"sync/atomic"
"time" "time"
"git.nspix.com/golang/kos/util/env" "git.nspix.com/golang/kos/util/env"
@ -29,6 +30,7 @@ type Server struct {
middleware []Middleware middleware []Middleware
router *Router router *Router
l net.Listener l net.Listener
exitFlag int32
} }
func (svr *Server) applyContext() *Context { func (svr *Server) applyContext() *Context {
@ -200,10 +202,14 @@ func (svr *Server) Serve(l net.Listener) (err error) {
return ctx.Success(svr.router.String()) return ctx.Success(svr.router.String())
}) })
svr.serve() svr.serve()
atomic.StoreInt32(&svr.exitFlag, 0)
return return
} }
func (svr *Server) Shutdown() (err error) { func (svr *Server) Shutdown() (err error) {
if !atomic.CompareAndSwapInt32(&svr.exitFlag, 0, 1) {
return
}
err = svr.l.Close() err = svr.l.Close()
svr.ctxMap.Range(func(key, value any) bool { svr.ctxMap.Range(func(key, value any) bool {
if ctx, ok := value.(*Context); ok { if ctx, ok := value.(*Context); ok {

View File

@ -9,6 +9,7 @@ import (
"path" "path"
"strings" "strings"
"sync" "sync"
"sync/atomic"
"time" "time"
) )
@ -26,6 +27,7 @@ type Server struct {
fileSystem http.FileSystem fileSystem http.FileSystem
beforeRequests []HandleFunc beforeRequests []HandleFunc
anyRequests map[string]http.Handler anyRequests map[string]http.Handler
exitFlag int32
} }
func (svr *Server) applyContext() *Context { func (svr *Server) applyContext() *Context {
@ -216,10 +218,14 @@ func (svr *Server) Serve(l net.Listener) (err error) {
} }
svr.router.NotFound = NotFound{} svr.router.NotFound = NotFound{}
svr.router.MethodNotAllowed = NotAllowed{} svr.router.MethodNotAllowed = NotAllowed{}
atomic.StoreInt32(&svr.exitFlag, 0)
return svr.serve.Serve(l) return svr.serve.Serve(l)
} }
func (svr *Server) Shutdown() (err error) { func (svr *Server) Shutdown() (err error) {
if !atomic.CompareAndSwapInt32(&svr.exitFlag, 0, 1) {
return
}
if svr.serve != nil { if svr.serve != nil {
err = svr.serve.Shutdown(svr.ctx) err = svr.serve.Shutdown(svr.ctx)
} }

View File

@ -19,53 +19,59 @@ var (
std *application std *application
) )
func initApplication(cbs ...Option) { func initialization(cbs ...Option) {
once.Do(func() { once.Do(func() {
std = New(cbs...) std = New(cbs...)
}) })
} }
func Init(cbs ...Option) *application { func Init(cbs ...Option) Application {
initApplication(cbs...) initialization(cbs...)
return std return std
} }
func Name() string {
initialization()
return std.opts.Name
}
func ShortName() string {
initialization()
return std.opts.ShortName()
}
func Version() string {
initialization()
return std.opts.Version
}
func Debug(args ...any) bool {
initialization()
if len(args) <= 0 {
return std.opts.EnableDebug
}
if b, ok := args[0].(bool); ok {
std.opts.EnableDebug = b
}
return std.opts.EnableDebug
}
func Node() *Info { func Node() *Info {
initApplication() initialization()
return std.Info() return std.Info()
} }
func Http() *http.Server { func Http() *http.Server {
initApplication() initialization()
return std.Http() return std.Http()
} }
func Name() string {
initApplication()
return std.opts.Name
}
func Version() string {
initApplication()
return std.opts.Version
}
func Debug() bool {
initApplication()
return std.opts.EnableDebug
}
func Command() *cli.Server { func Command() *cli.Server {
initApplication() initialization()
return std.Command() return std.Command()
} }
func ShortName() string {
initApplication()
return std.opts.ShortName()
}
func Handle(method string, cb HandleFunc) { func Handle(method string, cb HandleFunc) {
initApplication() initialization()
std.Handle(method, cb) std.Handle(method, cb)
} }

View File

@ -18,10 +18,11 @@ type (
Address string //绑定地址 Address string //绑定地址
Port int //端口 Port int //端口
EnableDebug bool //开启调试模式 EnableDebug bool //开启调试模式
DisableGateway bool //禁用HTTP和COMMAND入口
DisableHttp bool //禁用HTTP入口 DisableHttp bool //禁用HTTP入口
EnableDirectHttp bool //启用HTTP直连模式 EnableDirectHttp bool //启用HTTP直连模式
DisableCommand bool //禁用命令行入口 DisableCommand bool //禁用COMMAND入口
EnableDirectCommand bool //启用命令行直连模式 EnableDirectCommand bool //启用COMMAND直连模式
DisableStateApi bool //禁用系统状态接口 DisableStateApi bool //禁用系统状态接口
Metadata map[string]string //原数据 Metadata map[string]string //原数据
Context context.Context Context context.Context
@ -64,6 +65,12 @@ func WithName(name string, version string) Option {
} }
} }
func WithoutGateway() Option {
return func(o *Options) {
o.DisableGateway = true
}
}
func WithPort(port int) Option { func WithPort(port int) Option {
return func(o *Options) { return func(o *Options) {
o.Port = port o.Port = port

View File

@ -94,7 +94,6 @@ func (app *application) httpServe() (err error) {
var ( var (
l net.Listener l net.Listener
) )
app.http = http.New(app.ctx)
if l, err = app.gateway.Apply( if l, err = app.gateway.Apply(
entry.Feature(http.MethodGet), entry.Feature(http.MethodGet),
entry.Feature(http.MethodHead), entry.Feature(http.MethodHead),
@ -141,7 +140,6 @@ func (app *application) commandServe() (err error) {
var ( var (
l net.Listener l net.Listener
) )
app.command = cli.New(app.ctx)
if l, err = app.gateway.Apply( if l, err = app.gateway.Apply(
cli.Feature, cli.Feature,
); err != nil { ); err != nil {
@ -213,23 +211,26 @@ func (app *application) preStart() (err error) {
app.Log().Infof("server starting") app.Log().Infof("server starting")
env.Set(EnvAppName, app.opts.ShortName()) env.Set(EnvAppName, app.opts.ShortName())
env.Set(EnvAppVersion, app.opts.Version) env.Set(EnvAppVersion, app.opts.Version)
addr = net.JoinHostPort(app.opts.Address, strconv.Itoa(app.opts.Port)) app.http = http.New(app.ctx)
app.Log().Infof("server listen on: %s", addr) app.command = cli.New(app.ctx)
app.gateway = entry.New(addr) if !app.opts.DisableGateway {
if err = app.gateway.Start(app.ctx); err != nil { addr = net.JoinHostPort(app.opts.Address, strconv.Itoa(app.opts.Port))
return app.Log().Infof("server listen on: %s", addr)
} app.gateway = entry.New(addr)
if !app.opts.DisableHttp { if err = app.gateway.Start(app.ctx); err != nil {
if err = app.httpServe(); err != nil {
return return
} }
} if !app.opts.DisableHttp {
if !app.opts.DisableCommand { if err = app.httpServe(); err != nil {
if err = app.commandServe(); err != nil { return
return }
}
if !app.opts.DisableCommand {
if err = app.commandServe(); err != nil {
return
}
} }
} }
app.plugins.Range(func(key, value any) bool { app.plugins.Range(func(key, value any) bool {
if plugin, ok := value.(Plugin); ok { if plugin, ok := value.(Plugin); ok {
if err = plugin.BeforeStart(); err != nil { if err = plugin.BeforeStart(); err != nil {
@ -289,18 +290,20 @@ func (app *application) preStop() (err error) {
} }
return true return true
}) })
if app.http != nil { if !app.opts.DisableGateway {
if err = app.http.Shutdown(); err != nil { if app.http != nil {
app.Log().Warnf("server http shutdown error: %s", err.Error()) if err = app.http.Shutdown(); err != nil {
app.Log().Warnf("server http shutdown error: %s", err.Error())
}
} }
} if app.command != nil {
if app.command != nil { if err = app.command.Shutdown(); err != nil {
if err = app.command.Shutdown(); err != nil { app.Log().Warnf("server command shutdown error: %s", err.Error())
app.Log().Warnf("server command shutdown error: %s", err.Error()) }
}
if err = app.gateway.Stop(); err != nil {
app.Log().Warnf("server gateway shutdown error: %s", err.Error())
} }
}
if err = app.gateway.Stop(); err != nil {
app.Log().Warnf("server gateway shutdown error: %s", err.Error())
} }
app.plugins.Range(func(key, value any) bool { app.plugins.Range(func(key, value any) bool {
if plugin, ok := value.(Plugin); ok { if plugin, ok := value.(Plugin); ok {
@ -345,7 +348,7 @@ func (app *application) Run() (err error) {
return app.preStop() return app.preStop()
} }
func New(cbs ...Option) *application { func New(cbs ...Option) Application {
opts := NewOptions(cbs...) opts := NewOptions(cbs...)
app := &application{ app := &application{
opts: opts, opts: opts,

View File

@ -23,6 +23,7 @@ type (
Http() *http.Server Http() *http.Server
Command() *cli.Server Command() *cli.Server
Handle(method string, cb HandleFunc, opts ...HandleOption) Handle(method string, cb HandleFunc, opts ...HandleOption)
Run() (err error)
} }
// Info application information // Info application information

View File

@ -1,7 +1,6 @@
package fs package fs
import ( import (
"errors"
"os" "os"
) )
@ -11,22 +10,26 @@ func IsDir(filename string) (bool, error) {
if err != nil { if err != nil {
return false, err return false, err
} }
fm := fd.Mode() return fd.Mode().IsDir(), nil
return fm.IsDir(), nil
} }
// DirectoryOrCreate checking directory, is not exists will create // IsFile Tells whether the filename is a file
func DirectoryOrCreate(dirname string) error { func IsFile(filename string) (bool, error) {
fd, err := os.Stat(filename)
if err != nil {
return false, err
}
return !fd.Mode().IsDir(), nil
}
// Mkdir checking directory, is not exists will create
func Mkdir(dirname string, perm os.FileMode) error {
if fi, err := os.Stat(dirname); err != nil { if fi, err := os.Stat(dirname); err != nil {
if errors.Is(err, os.ErrNotExist) { return os.MkdirAll(dirname, perm)
return os.MkdirAll(dirname, 0755)
} else {
return err
}
} else { } else {
if fi.IsDir() { if fi.IsDir() {
return nil return nil
} }
return errors.New("file not directory") return os.ErrPermission
} }
} }

View File

@ -1 +0,0 @@
package fs

View File

@ -2,19 +2,15 @@ package sys
import ( import (
"os" "os"
"path/filepath"
"runtime" "runtime"
) )
// HomeDir return user home directory // HomeDir return user home directory
func HomeDir() string { func HomeDir() string {
if runtime.GOOS == "windows" { if dirname, err := os.UserHomeDir(); err == nil {
return os.Getenv("HOMEDRIVE") + os.Getenv("HOMEPATH") return dirname
} }
if h := os.Getenv("HOME"); h != "" { return os.TempDir()
return h
}
return "/"
} }
// HiddenFile get hidden file prefix // HiddenFile get hidden file prefix
@ -29,20 +25,12 @@ func HiddenFile(name string) string {
// CacheDir return user cache directory // CacheDir return user cache directory
func CacheDir() string { func CacheDir() string {
switch runtime.GOOS { if dirname, err := os.UserCacheDir(); err == nil {
case "darwin": return dirname
return filepath.Join(HomeDir(), "Library", "Caches")
case "windows":
for _, ev := range []string{"APPDATA", "CSIDL_APPDATA", "TEMP", "TMP"} {
if v := os.Getenv(ev); v != "" {
return v
}
}
// Worst case:
return HomeDir()
} }
if xdg := os.Getenv("XDG_CACHE_HOME"); xdg != "" { return os.TempDir()
return xdg }
}
return filepath.Join(HomeDir(), ".cache") func TempFile() (*os.File, error) {
return os.CreateTemp(os.TempDir(), "kos_*")
} }