优化最小服务

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

@ -36,7 +36,7 @@ func (s *subServer) Start(ctx context.Context) (err error) {
[]string{"SSS", "aaa"},
})
})
kos.Command().Handle("/users", "test command", func(ctx *cli.Context) (err error) {
return ctx.Success([]*users{
{Name: "Zhan", Age: 10, Tags: []string{"a", "b"}},

View File

@ -191,8 +191,12 @@ func serializeArray(val []any) (buf []byte, err error) {
row := make([]any, 0, rv.Type().NumField())
for j := 0; j < rv.Type().NumField(); j++ {
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)
} else {
if columnName == "-" {
continue
}
}
row = append(row, columnName)
}

View File

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

View File

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

View File

@ -19,53 +19,59 @@ var (
std *application
)
func initApplication(cbs ...Option) {
func initialization(cbs ...Option) {
once.Do(func() {
std = New(cbs...)
})
}
func Init(cbs ...Option) *application {
initApplication(cbs...)
func Init(cbs ...Option) Application {
initialization(cbs...)
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 {
initApplication()
initialization()
return std.Info()
}
func Http() *http.Server {
initApplication()
initialization()
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 {
initApplication()
initialization()
return std.Command()
}
func ShortName() string {
initApplication()
return std.opts.ShortName()
}
func Handle(method string, cb HandleFunc) {
initApplication()
initialization()
std.Handle(method, cb)
}

View File

@ -18,10 +18,11 @@ type (
Address string //绑定地址
Port int //端口
EnableDebug bool //开启调试模式
DisableGateway bool //禁用HTTP和COMMAND入口
DisableHttp bool //禁用HTTP入口
EnableDirectHttp bool //启用HTTP直连模式
DisableCommand bool //禁用命令行入口
EnableDirectCommand bool //启用命令行直连模式
DisableCommand bool //禁用COMMAND入口
EnableDirectCommand bool //启用COMMAND直连模式
DisableStateApi bool //禁用系统状态接口
Metadata map[string]string //原数据
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 {
return func(o *Options) {
o.Port = port

View File

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

View File

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

View File

@ -1,7 +1,6 @@
package fs
import (
"errors"
"os"
)
@ -11,22 +10,26 @@ func IsDir(filename string) (bool, error) {
if err != nil {
return false, err
}
fm := fd.Mode()
return fm.IsDir(), nil
return fd.Mode().IsDir(), nil
}
// DirectoryOrCreate checking directory, is not exists will create
func DirectoryOrCreate(dirname string) error {
// IsFile Tells whether the filename is a file
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 errors.Is(err, os.ErrNotExist) {
return os.MkdirAll(dirname, 0755)
} else {
return err
}
return os.MkdirAll(dirname, perm)
} else {
if fi.IsDir() {
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 (
"os"
"path/filepath"
"runtime"
)
// HomeDir return user home directory
func HomeDir() string {
if runtime.GOOS == "windows" {
return os.Getenv("HOMEDRIVE") + os.Getenv("HOMEPATH")
if dirname, err := os.UserHomeDir(); err == nil {
return dirname
}
if h := os.Getenv("HOME"); h != "" {
return h
}
return "/"
return os.TempDir()
}
// HiddenFile get hidden file prefix
@ -29,20 +25,12 @@ func HiddenFile(name string) string {
// CacheDir return user cache directory
func CacheDir() string {
switch runtime.GOOS {
case "darwin":
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 dirname, err := os.UserCacheDir(); err == nil {
return dirname
}
if xdg := os.Getenv("XDG_CACHE_HOME"); xdg != "" {
return xdg
}
return filepath.Join(HomeDir(), ".cache")
return os.TempDir()
}
func TempFile() (*os.File, error) {
return os.CreateTemp(os.TempDir(), "kos_*")
}