diff --git a/cmd/start.go b/cmd/start.go index 829513c9..1591c917 100644 --- a/cmd/start.go +++ b/cmd/start.go @@ -1,10 +1,12 @@ package cmd import ( + "log" + "github.com/spf13/cobra" "github.com/yaoapp/gou" "github.com/yaoapp/kun/utils" - "github.com/yaoapp/xiang/server" + "github.com/yaoapp/xiang/global" ) var startCmd = &cobra.Command{ @@ -12,12 +14,13 @@ var startCmd = &cobra.Command{ Short: "启动象传应用引擎", Long: `启动象传应用引擎`, Run: func(cmd *cobra.Command, args []string) { + defer global.ServiceStop(func() { log.Println("服务已关闭") }) // 启动服务 for _, api := range gou.APIs { for _, p := range api.HTTP.Paths { utils.Dump(api.Name + ":" + p.Path) } } - server.Start() + global.ServiceStart() }, } diff --git a/server/guard.go b/global/guard.go similarity index 93% rename from server/guard.go rename to global/guard.go index 3c5c374b..2c05e858 100644 --- a/server/guard.go +++ b/global/guard.go @@ -1,4 +1,4 @@ -package server +package global import ( "fmt" @@ -6,7 +6,6 @@ import ( "github.com/dgrijalva/jwt-go" "github.com/gin-gonic/gin" - "github.com/yaoapp/xiang/global" ) // Guards 服务中间件 @@ -33,7 +32,7 @@ func bearerJWT(c *gin.Context) { tokenString = strings.TrimSpace(strings.TrimPrefix(tokenString, "Bearer ")) token, err := jwt.ParseWithClaims(tokenString, &JwtClaims{}, func(token *jwt.Token) (interface{}, error) { - return global.Conf.JWT.Secret, nil + return Conf.JWT.Secret, nil }) if err != nil { diff --git a/global/load.go b/global/load.go index da0645c2..d26ed5bc 100644 --- a/global/load.go +++ b/global/load.go @@ -70,22 +70,42 @@ func LoadApp(api string, flow string, model string, plugin string) { root := strings.TrimPrefix(api, "fs://") scripts := getAppFilesFS(root, ".json") for _, script := range scripts { + // 验证API 加载逻辑 gou.LoadAPI(string(script.Content), script.Name) } // 监听API修改 if Conf.Mode == "debug" { go Watch(root, func(op string, file string) { + + if !strings.HasSuffix(file, ".json") { + return + } + if op == "write" || op == "create" || op == "rename" { script := getAppFile(root, file, ".json") gou.LoadAPI(string(script.Content), script.Name) // Reload log.Printf("API %s 已重新加载完毕", script.Name) + + // 重启服务器 + ServiceStop(func() { + log.Printf("服务器重启完毕") + go ServiceStart() + }) + } else if op == "remove" { name := getAppFileName(root, file) if _, has := gou.APIs[name]; has { delete(gou.APIs, name) + + // 重启服务器 + ServiceStop(func() { + log.Printf("服务器重启完毕") + go ServiceStart() + }) } } + }) } } diff --git a/server/middleware.go b/global/middleware.go similarity index 81% rename from server/middleware.go rename to global/middleware.go index 1c4219b9..87120c90 100644 --- a/server/middleware.go +++ b/global/middleware.go @@ -1,11 +1,10 @@ -package server +package global import ( "fmt" "strings" "github.com/gin-gonic/gin" - "github.com/yaoapp/xiang/global" ) // Middlewares 服务中间件 @@ -17,7 +16,7 @@ var Middlewares = []gin.HandlerFunc{ // BindDomain 绑定许可域名 func BindDomain(c *gin.Context) { - for _, allow := range global.AllowHosts { + for _, allow := range AllowHosts { if strings.Contains(c.Request.Host, allow) { c.Next() return @@ -39,6 +38,6 @@ func BinStatic(c *gin.Context) { } // 静态文件请求 - global.FileServer.ServeHTTP(c.Writer, c.Request) + FileServer.ServeHTTP(c.Writer, c.Request) c.Abort() } diff --git a/global/service.go b/global/service.go new file mode 100644 index 00000000..a10cbf9b --- /dev/null +++ b/global/service.go @@ -0,0 +1,31 @@ +package global + +import ( + "github.com/yaoapp/gou" +) + +var shutdown = make(chan bool) +var shutdownComplete = make(chan bool) + +// ServiceStart 启动服务 +func ServiceStart() { + gou.SetHTTPGuards(Guards) + gou.ServeHTTP( + gou.Server{ + Host: Conf.Service.Host, + Port: Conf.Service.Port, + Allows: Conf.Service.Allow, + Root: "/api", + }, + &shutdown, func(s gou.Server) { + shutdownComplete <- true + }, + Middlewares...) +} + +// ServiceStop 关闭服务 +func ServiceStop(onComplete func()) { + shutdown <- true + <-shutdownComplete + onComplete() +} diff --git a/go.mod b/go.mod index e416bac8..38006526 100644 --- a/go.mod +++ b/go.mod @@ -8,15 +8,25 @@ require ( github.com/elazarl/go-bindata-assetfs v1.0.1 // indirect github.com/fsnotify/fsnotify v1.5.1 // indirect github.com/gin-gonic/gin v1.7.4 // indirect + github.com/go-sql-driver/mysql v1.6.0 // indirect + github.com/hashicorp/go-plugin v1.4.3 // indirect + github.com/hashicorp/yamux v0.0.0-20210826001029-26ff87cf9493 // indirect + github.com/jmoiron/sqlx v1.3.4 // indirect github.com/joho/godotenv v1.3.0 // indirect github.com/json-iterator/go v1.1.12 // indirect + github.com/lib/pq v1.10.3 // indirect github.com/mattn/go-isatty v0.0.14 // indirect + github.com/mattn/go-sqlite3 v1.14.8 // indirect github.com/spf13/cobra v1.2.1 // indirect github.com/stretchr/testify v1.7.0 - github.com/yaoapp/gou v0.0.0-20210906083912-fc06d8ba7e7a // indirect + github.com/yaoapp/gou v0.0.0-20210915011439-017e751ca527 // indirect github.com/yaoapp/kun v0.6.1 github.com/yaoapp/xun v0.5.2 // indirect + golang.org/x/net v0.0.0-20210913180222-943fd674d43e // indirect golang.org/x/sys v0.0.0-20210910150752-751e447fb3d0 // indirect + google.golang.org/genproto v0.0.0-20210909211513-a8c4777a87af // indirect ) // go env -w GOPRIVATE=github.com/yaoapp/* + +// replace github.com/yaoapp/gou => ../gou // gou local diff --git a/go.sum b/go.sum index 2a739565..fa120d82 100644 --- a/go.sum +++ b/go.sum @@ -108,6 +108,8 @@ github.com/go-playground/validator/v10 v10.9.0 h1:NgTtmN58D0m8+UuxtYmGztBJB7VnPg github.com/go-playground/validator/v10 v10.9.0/go.mod h1:74x4gJWsvQexRdW8Pn3dXSGrTK4nAUsbPlLADvpJkos= github.com/go-sql-driver/mysql v1.5.0 h1:ozyZYNQW3x3HtqT1jira07DN2PArx2v7/mN66gGcHOs= github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= +github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfCHuOE= +github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= @@ -186,6 +188,8 @@ github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iP github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= github.com/hashicorp/go-plugin v1.4.2 h1:yFvG3ufXXpqiMiZx9HLcaK3XbIqQ1WJFR/F1a2CuVw0= github.com/hashicorp/go-plugin v1.4.2/go.mod h1:5fGEH17QVwTTcR0zV7yhDPLLmFX9YSZ38b18Udy6vYQ= +github.com/hashicorp/go-plugin v1.4.3 h1:DXmvivbWD5qdiBts9TpBC7BYL1Aia5sxbRgQB+v6UZM= +github.com/hashicorp/go-plugin v1.4.3/go.mod h1:5fGEH17QVwTTcR0zV7yhDPLLmFX9YSZ38b18Udy6vYQ= github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU= github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU= github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= @@ -202,6 +206,8 @@ github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/J github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM= github.com/hashicorp/yamux v0.0.0-20210707203944-259a57b3608c h1:nqkErwUGfpZZMqj29WZ9U/wz2OpJVDuiokLhE/3Y7IQ= github.com/hashicorp/yamux v0.0.0-20210707203944-259a57b3608c/go.mod h1:CtWFDAQgb7dxtzFs4tWbplKIe2jSi3+5vKbgIO0SLnQ= +github.com/hashicorp/yamux v0.0.0-20210826001029-26ff87cf9493 h1:brI5vBRUlAlM34VFmnLPwjnCL/FxAJp9XvOdX6Zt+XE= +github.com/hashicorp/yamux v0.0.0-20210826001029-26ff87cf9493/go.mod h1:CtWFDAQgb7dxtzFs4tWbplKIe2jSi3+5vKbgIO0SLnQ= github.com/hokaccha/go-prettyjson v0.0.0-20210113012101-fb4e108d2519/go.mod h1:pFlLw2CfqZiIBOx6BuCeRLCrfxBJipTY0nIOF/VbGcI= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= @@ -210,6 +216,8 @@ github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANyt github.com/jhump/protoreflect v1.6.0/go.mod h1:eaTn3RZAmMBcV0fifFvlm6VHNz3wSkYyXYWUh7ymB74= github.com/jmoiron/sqlx v1.3.1 h1:aLN7YINNZ7cYOPK3QC83dbM6KT0NMqVMw961TqrejlE= github.com/jmoiron/sqlx v1.3.1/go.mod h1:2BljVx/86SuTyjE+aPYlHCTNvZrnJXghYGpNiXLBMCQ= +github.com/jmoiron/sqlx v1.3.4 h1:wv+0IJZfL5z0uZoUjlpKgHkgaFSYD+r9CfrXjEXsO7w= +github.com/jmoiron/sqlx v1.3.4/go.mod h1:2BljVx/86SuTyjE+aPYlHCTNvZrnJXghYGpNiXLBMCQ= github.com/joho/godotenv v1.3.0 h1:Zjp+RcGpHhGlrMbJzXTrZZPrWj+1vfm90La1wgB6Bhc= github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg= github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= @@ -236,6 +244,8 @@ github.com/leodido/go-urn v1.2.1/go.mod h1:zt4jvISO2HfUBqxjfIshjdMTYS56ZS/qv49ic github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/lib/pq v1.9.0 h1:L8nSXQQzAYByakOFMTwpjRoHsMJklur4Gi59b6VivR8= github.com/lib/pq v1.9.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= +github.com/lib/pq v1.10.3 h1:v9QZf2Sn6AmjXtQeFpdoq/eaNtYP6IN+7lcrygsIAtg= +github.com/lib/pq v1.10.3/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/magiconair/properties v1.8.5/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60= github.com/matryer/is v1.4.0/go.mod h1:8I/i5uYgLzgsgEloJE1U6xx5HkBQpAZvepWuujKwMRU= github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= @@ -252,6 +262,8 @@ github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9 github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= github.com/mattn/go-sqlite3 v1.14.6 h1:dNPt6NO46WmLVt2DLNpwczCmdV5boIZ6g/tlDrlRUbg= github.com/mattn/go-sqlite3 v1.14.6/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= +github.com/mattn/go-sqlite3 v1.14.8 h1:gDp86IdQsN/xWjIEmr9MF6o9mpksUgh0fu+9ByFxzIU= +github.com/mattn/go-sqlite3 v1.14.8/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= @@ -323,6 +335,8 @@ github.com/ugorji/go/codec v1.2.6 h1:7kbGefxLoDBuYXOms4yD7223OpNMMPNPZxXk5TvFcyQ github.com/ugorji/go/codec v1.2.6/go.mod h1:V6TCNZ4PHqoHGFZuSG1W8nrCzzdgA2DozYxWFFpvxTw= github.com/yaoapp/gou v0.0.0-20210906083912-fc06d8ba7e7a h1:nfBaWiklyKXKk5oKec1WaKS+4J481hiG7FgEc9Tu5Vo= github.com/yaoapp/gou v0.0.0-20210906083912-fc06d8ba7e7a/go.mod h1:KwCRZDnEKLCxUNgMQ2acU7WI5zpt5SGOt1sBKqNAiaE= +github.com/yaoapp/gou v0.0.0-20210915011439-017e751ca527 h1:PKHppgOCzzs68jdFp8IW7U7wwjwxp3KM3GXudgCmNZM= +github.com/yaoapp/gou v0.0.0-20210915011439-017e751ca527/go.mod h1:KwCRZDnEKLCxUNgMQ2acU7WI5zpt5SGOt1sBKqNAiaE= github.com/yaoapp/kun v0.6.1 h1:gbjP5wmAuJLe1RGZ2evH4rPCVWykf0iqbksaHikF/GI= github.com/yaoapp/kun v0.6.1/go.mod h1:igsTcWDnzpp0HtRN7sBP+XOEN2tzoC5hk2MyoPFs3xA= github.com/yaoapp/xun v0.5.2 h1:DedZ26FpcXfkLnPKXzJsrbVmpWDRx9adUo8AvOcNT+g= @@ -430,6 +444,8 @@ golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLd golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/net v0.0.0-20210813160813-60bc85c4be6d h1:LO7XpTYMwTqxjLcGWPijK3vRXg1aWdlNOVOHRq45d7c= golang.org/x/net v0.0.0-20210813160813-60bc85c4be6d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20210913180222-943fd674d43e h1:+b/22bPvDYt4NPDcy4xAGCmON713ONAWFeY3Z7I3tR8= +golang.org/x/net v0.0.0-20210913180222-943fd674d43e/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -652,6 +668,8 @@ google.golang.org/genproto v0.0.0-20210402141018-6c239bbf2bb1/go.mod h1:9lPAdzaE google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= google.golang.org/genproto v0.0.0-20210821163610-241b8fcbd6c8 h1:XosVttQUxX8erNhEruTu053/VchgYuksoS9Bj/OITjU= google.golang.org/genproto v0.0.0-20210821163610-241b8fcbd6c8/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= +google.golang.org/genproto v0.0.0-20210909211513-a8c4777a87af h1:aLMMXFYqw01RA6XJim5uaN+afqNNjc9P8HPAbnpnc5s= +google.golang.org/genproto v0.0.0-20210909211513-a8c4777a87af/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= google.golang.org/grpc v1.8.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= diff --git a/main_test.go b/main_test.go index 0796bb5a..9912bec0 100644 --- a/main_test.go +++ b/main_test.go @@ -3,6 +3,7 @@ package main import ( "fmt" "io/ioutil" + "log" "net/http" "os" "testing" @@ -77,3 +78,52 @@ func TestCommandStart(t *testing.T) { assert.True(t, false) } + +func TestCommandStop(t *testing.T) { + oldArgs := os.Args + defer func() { os.Args = oldArgs }() + go func() { + os.Args = append(os.Args, "start") + main() + }() + + // 发送请求 + request := func() (maps.MapStr, error) { + time.Sleep(time.Microsecond * 1000) + url := fmt.Sprintf("http://%s:%d/api/user/find/1?select=id,name", "local.iqka.com", global.Conf.Service.Port) + // utils.Dump(url) + resp, err := http.Get(url) + if err != nil { + return nil, err + } + defer resp.Body.Close() + body, err := ioutil.ReadAll(resp.Body) + res := maps.MakeMapStr() + err = jsoniter.Unmarshal(body, &res) + if err != nil { + return nil, err + } + return res, nil + } + + // 等待服务启动 + times := 0 + for times < 20 { // 2秒超时 + times++ + res, err := request() + if err != nil { + continue + } + assert.Equal(t, 1, any.Of(res.Get("id")).CInt()) + assert.Equal(t, "管理员", res.Get("name")) + + // 测试关闭 + global.ServiceStop(func() { log.Println("服务已关闭") }) + time.Sleep(time.Second * 1) + _, err = request() + assert.NotNil(t, err) + return + } + + assert.True(t, false) +} diff --git a/server/service.go b/server/service.go deleted file mode 100644 index 7a8429e3..00000000 --- a/server/service.go +++ /dev/null @@ -1,17 +0,0 @@ -package server - -import ( - "github.com/yaoapp/gou" - "github.com/yaoapp/xiang/global" -) - -// Start 启动服务 -func Start() { - gou.SetHTTPGuards(Guards) - gou.ServeHTTP(gou.Server{ - Host: global.Conf.Service.Host, - Port: global.Conf.Service.Port, - Allows: global.Conf.Service.Allow, - Root: "/api", - }, Middlewares...) -}