Skip to content

Commit

Permalink
Merge branch 'feature/go-1.20' of github.com:gogf/gf into feature/go-…
Browse files Browse the repository at this point in the history
…1.20

* 'feature/go-1.20' of github.com:gogf/gf:
  ci: fix mssql docker service failed in ci (#3792)
  fix(net/ghttp): server shutdown not graceful using admin api `/debug/admin/shutdown` (#3777)
  • Loading branch information
houseme committed Sep 23, 2024
2 parents fbc7f9d + 5cadff8 commit 2fc1562
Show file tree
Hide file tree
Showing 17 changed files with 208 additions and 64 deletions.
13 changes: 7 additions & 6 deletions .github/workflows/ci-main.sh
Original file line number Diff line number Diff line change
Expand Up @@ -7,23 +7,24 @@ for file in `find . -name go.mod`; do
dirpath=$(dirname $file)
echo $dirpath

# ignore mssql tests as its docker service failed
# TODO remove this ignoring codes after the mssql docker service OK
if [ "mssql" = $(basename $dirpath) ]; then
continue 1
fi

if [[ $file =~ "/testdata/" ]]; then
echo "ignore testdata path $file"
continue 1
fi

# package kuhecm needs golang >= v1.19
# package kuhecm was moved to sub ci procedure.
if [ "kubecm" = $(basename $dirpath) ]; then
continue 1
if ! go version|grep -qE "go1.[2-9][0-9]"; then
echo "ignore kubecm as go version: $(go version)"
continue 1
fi
fi

# package consul needs golang >= v1.19
if [ "consul" = $(basename $dirpath) ]; then
continue 1
if ! go version|grep -qE "go1.[2-9][0-9]"; then
echo "ignore consul as go version: $(go version)"
continue 1
Expand Down
34 changes: 18 additions & 16 deletions .github/workflows/ci-main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -108,22 +108,24 @@ jobs:
# -e MSSQL_USER=root \
# -e MSSQL_PASSWORD=LoremIpsum86 \
# loads/mssqldocker:14.0.3391.2
mssql:
image: loads/mssqldocker:14.0.3391.2
env:
ACCEPT_EULA: Y
SA_PASSWORD: LoremIpsum86
MSSQL_DB: test
MSSQL_USER: root
MSSQL_PASSWORD: LoremIpsum86
ports:
- 1433:1433
options: >-
--health-cmd="/opt/mssql-tools/bin/sqlcmd -S localhost -U sa -P LoremIpsum86 -l 30 -Q \"SELECT 1\" || exit 1"
--health-start-period 10s
--health-interval 10s
--health-timeout 5s
--health-retries 10

# TODO mssql docker failed, will be enabled later after it is OK in github action.
# mssql:
# image: loads/mssqldocker:14.0.3391.2
# env:
# ACCEPT_EULA: Y
# SA_PASSWORD: LoremIpsum86
# MSSQL_DB: test
# MSSQL_USER: root
# MSSQL_PASSWORD: LoremIpsum86
# ports:
# - 1433:1433
# options: >-
# --health-cmd="/opt/mssql-tools/bin/sqlcmd -S localhost -U sa -P LoremIpsum86 -l 30 -Q \"SELECT 1\" || exit 1"
# --health-start-period 10s
# --health-interval 10s
# --health-timeout 5s
# --health-retries 10

# ClickHouse backend server.
# docker run -d --name clickhouse \
Expand Down
2 changes: 1 addition & 1 deletion .golangci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ linters-settings:
# Checks the number of lines in a function.
# If lower than 0, disable the check.
# Default: 60
lines: 330
lines: 340
# Checks the number of statements in a function.
# If lower than 0, disable the check.
# Default: 40
Expand Down
8 changes: 5 additions & 3 deletions cmd/gf/internal/cmd/gendao/gendao.go
Original file line number Diff line number Diff line change
Expand Up @@ -208,9 +208,11 @@ type (
NoModelComment bool `name:"noModelComment" short:"m" brief:"{CGenDaoBriefNoModelComment}" orphan:"true"`
Clear bool `name:"clear" short:"a" brief:"{CGenDaoBriefClear}" orphan:"true"`

TypeMapping map[DBFieldTypeName]CustomAttributeType `name:"typeMapping" short:"y" brief:"{CGenDaoBriefTypeMapping}" orphan:"true"`
FieldMapping map[DBTableFieldName]CustomAttributeType `name:"fieldMapping" short:"fm" brief:"{CGenDaoBriefFieldMapping}" orphan:"true"`
genItems *CGenDaoInternalGenItems
TypeMapping map[DBFieldTypeName]CustomAttributeType `name:"typeMapping" short:"y" brief:"{CGenDaoBriefTypeMapping}" orphan:"true"`
FieldMapping map[DBTableFieldName]CustomAttributeType `name:"fieldMapping" short:"fm" brief:"{CGenDaoBriefFieldMapping}" orphan:"true"`

// internal usage purpose.
genItems *CGenDaoInternalGenItems
}
CGenDaoOutput struct{}

Expand Down
2 changes: 1 addition & 1 deletion net/gclient/gclient_observability.go
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ func internalMiddlewareObservability(c *Client, r *http.Request) (response *Resp
if response == nil || response.Response == nil {
return
}

// TODO ignore binary content ReadAll, for example downloading request.
reqBodyContentBytes, _ := io.ReadAll(response.Body)
response.Body = utils.NewReadCloser(reqBodyContentBytes, false)

Expand Down
8 changes: 7 additions & 1 deletion net/ghttp/ghttp_server.go
Original file line number Diff line number Diff line change
Expand Up @@ -247,7 +247,13 @@ func (s *Server) Start() error {

// If this is a child process, it then notifies its parent exit.
if gproc.IsChild() {
gtimer.SetTimeout(ctx, time.Duration(s.config.GracefulTimeout)*time.Second, func(ctx context.Context) {
var gracefulTimeout = time.Duration(s.config.GracefulTimeout) * time.Second
gtimer.SetTimeout(ctx, gracefulTimeout, func(ctx context.Context) {
intlog.Printf(
ctx,
`pid[%d]: notice parent server graceful shuttingdown, ppid: %d`,
gproc.Pid(), gproc.PPid(),
)
if err := gproc.Send(gproc.PPid(), []byte("exit"), adminGProcCommGroup); err != nil {
intlog.Errorf(ctx, `server error in process communication: %+v`, err)
}
Expand Down
12 changes: 9 additions & 3 deletions net/ghttp/ghttp_server_admin.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,14 @@ func (p *utilAdmin) Index(r *Request) {
<body>
<p>Pid: {{.pid}}</p>
<p>File Path: {{.path}}</p>
<p><a href="{{$.uri}}/restart">Restart</a></p>
<p><a href="{{$.uri}}/shutdown">Shutdown</a></p>
<p>
<a href="{{$.uri}}/restart">Restart</a>
please make sure it is running using standalone binary not from IDE or "go run"
</p>
<p>
<a href="{{$.uri}}/shutdown">Shutdown</a>
graceful shutdown the server
</p>
</body>
</html>
`, data)
Expand Down Expand Up @@ -89,7 +95,7 @@ func (s *Server) Shutdown() error {
// Only shut down current servers.
// It may have multiple underlying http servers.
for _, v := range s.servers {
v.close(ctx)
v.shutdown(ctx)
}
return nil
}
21 changes: 14 additions & 7 deletions net/ghttp/ghttp_server_admin_process.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import (
"github.com/gogf/gf/v2/errors/gcode"
"github.com/gogf/gf/v2/errors/gerror"
"github.com/gogf/gf/v2/internal/intlog"
"github.com/gogf/gf/v2/os/gfile"
"github.com/gogf/gf/v2/os/glog"
"github.com/gogf/gf/v2/os/gproc"
"github.com/gogf/gf/v2/os/gtime"
Expand Down Expand Up @@ -55,7 +56,10 @@ var (
// The optional parameter `newExeFilePath` specifies the new binary file for creating process.
func RestartAllServer(ctx context.Context, newExeFilePath string) error {
if !gracefulEnabled {
return gerror.NewCode(gcode.CodeInvalidOperation, "graceful reload feature is disabled")
return gerror.NewCode(
gcode.CodeInvalidOperation,
"graceful reload feature is disabled",
)
}
serverActionLocker.Lock()
defer serverActionLocker.Unlock()
Expand Down Expand Up @@ -115,13 +119,16 @@ func checkActionFrequency() error {
// forkReloadProcess creates a new child process and copies the fd to child process.
func forkReloadProcess(ctx context.Context, newExeFilePath ...string) error {
var (
path = os.Args[0]
binaryPath = os.Args[0]
)
if len(newExeFilePath) > 0 && newExeFilePath[0] != "" {
path = newExeFilePath[0]
binaryPath = newExeFilePath[0]
}
if !gfile.Exists(binaryPath) {
return gerror.Newf(`binary file path "%s" does not exist`, binaryPath)
}
var (
p = gproc.NewProcess(path, os.Args[1:], os.Environ())
p = gproc.NewProcess(binaryPath, os.Args[1:], os.Environ())
sfm = getServerFdMap()
)
for name, m := range sfm {
Expand All @@ -145,9 +152,9 @@ func forkReloadProcess(ctx context.Context, newExeFilePath ...string) error {
buffer, _ := gjson.Encode(sfm)
p.Env = append(p.Env, adminActionReloadEnvKey+"="+string(buffer))
if _, err := p.Start(ctx); err != nil {
glog.Errorf(
intlog.Errorf(
ctx,
"%d: fork process failed, error:%s, %s",
"%d: fork process failed, error: %s, %s",
gproc.Pid(), err.Error(), string(buffer),
)
return err
Expand Down Expand Up @@ -254,7 +261,7 @@ func shutdownWebServersGracefully(ctx context.Context, signal os.Signal) {
gproc.Pid(), signal.String(),
)
} else {
glog.Printf(ctx, "%d: server gracefully shutting down by api", gproc.Pid())
glog.Printf(ctx, "pid[%d]: server gracefully shutting down by api", gproc.Pid())
}
serverMapping.RLockFunc(func(m map[string]interface{}) {
for _, v := range m {
Expand Down
22 changes: 13 additions & 9 deletions net/ghttp/ghttp_server_config.go
Original file line number Diff line number Diff line change
Expand Up @@ -230,6 +230,19 @@ type ServerConfig struct {
SwaggerPath string `json:"swaggerPath"` // SwaggerPath specifies the swagger UI path for route registering.
SwaggerUITemplate string `json:"swaggerUITemplate"` // SwaggerUITemplate specifies the swagger UI custom template

// ======================================================================================================
// Graceful reload & shutdown.
// ======================================================================================================

// Graceful enables graceful reload feature for all servers of the process.
Graceful bool `json:"graceful"`

// GracefulTimeout set the maximum survival time (seconds) of the parent process.
GracefulTimeout int `json:"gracefulTimeout"`

// GracefulShutdownTimeout set the maximum survival time (seconds) before stopping the server.
GracefulShutdownTimeout int `json:"gracefulShutdownTimeout"`

// ======================================================================================================
// Other.
// ======================================================================================================
Expand All @@ -254,15 +267,6 @@ type ServerConfig struct {

// DumpRouterMap specifies whether automatically dumps router map when server starts.
DumpRouterMap bool `json:"dumpRouterMap"`

// Graceful enables graceful reload feature for all servers of the process.
Graceful bool `json:"graceful"`

// GracefulTimeout set the maximum survival time (seconds) of the parent process.
GracefulTimeout uint8 `json:"gracefulTimeout"`

// GracefulShutdownTimeout set the maximum survival time (seconds) before stopping the server.
GracefulShutdownTimeout uint8 `json:"gracefulShutdownTimeout"`
}

// NewConfig creates and returns a ServerConfig object with default configurations.
Expand Down
22 changes: 22 additions & 0 deletions net/ghttp/ghttp_server_config_api.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
//
// This Source Code Form is subject to the terms of the MIT License.
// If a copy of the MIT was not distributed with this file,
// You can obtain one at https://github.com/gogf/gf.

package ghttp

// SetSwaggerPath sets the SwaggerPath for server.
func (s *Server) SetSwaggerPath(path string) {
s.config.SwaggerPath = path
}

// SetSwaggerUITemplate sets the Swagger template for server.
func (s *Server) SetSwaggerUITemplate(swaggerUITemplate string) {
s.config.SwaggerUITemplate = swaggerUITemplate
}

// SetOpenApiPath sets the OpenApiPath for server.
func (s *Server) SetOpenApiPath(path string) {
s.config.OpenApiPath = path
}
35 changes: 26 additions & 9 deletions net/ghttp/ghttp_server_config_mess.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,17 +27,34 @@ func (s *Server) SetFormParsingMemory(maxMemory int64) {
s.config.FormParsingMemory = maxMemory
}

// SetSwaggerPath sets the SwaggerPath for server.
func (s *Server) SetSwaggerPath(path string) {
s.config.SwaggerPath = path
// SetGraceful sets the Graceful for server.
func (s *Server) SetGraceful(graceful bool) {
s.config.Graceful = graceful
// note: global setting.
gracefulEnabled = graceful
}

// SetSwaggerUITemplate sets the Swagger template for server.
func (s *Server) SetSwaggerUITemplate(swaggerUITemplate string) {
s.config.SwaggerUITemplate = swaggerUITemplate
// GetGraceful returns the Graceful for server.
func (s *Server) GetGraceful() bool {
return s.config.Graceful
}

// SetOpenApiPath sets the OpenApiPath for server.
func (s *Server) SetOpenApiPath(path string) {
s.config.OpenApiPath = path
// SetGracefulTimeout sets the GracefulTimeout for server.
func (s *Server) SetGracefulTimeout(gracefulTimeout int) {
s.config.GracefulTimeout = gracefulTimeout
}

// GetGracefulTimeout returns the GracefulTimeout for server.
func (s *Server) GetGracefulTimeout() int {
return s.config.GracefulTimeout
}

// SetGracefulShutdownTimeout sets the GracefulShutdownTimeout for server.
func (s *Server) SetGracefulShutdownTimeout(gracefulShutdownTimeout int) {
s.config.GracefulShutdownTimeout = gracefulShutdownTimeout
}

// GetGracefulShutdownTimeout returns the GracefulShutdownTimeout for server.
func (s *Server) GetGracefulShutdownTimeout() int {
return s.config.GracefulShutdownTimeout
}
1 change: 1 addition & 0 deletions net/ghttp/ghttp_server_graceful.go
Original file line number Diff line number Diff line change
Expand Up @@ -259,6 +259,7 @@ func (s *gracefulServer) getRawListener() net.Listener {
}

// close shuts down the server forcibly.
// for graceful shutdown, please use gracefulServer.shutdown.
func (s *gracefulServer) close(ctx context.Context) {
if s.status.Val() == ServerStatusStopped {
return
Expand Down
39 changes: 39 additions & 0 deletions net/ghttp/ghttp_z_unit_feature_config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -163,3 +163,42 @@ func Test_ClientMaxBodySize_File(t *testing.T) {
)
})
}

func Test_Config_Graceful(t *testing.T) {
var (
defaultConfig = ghttp.NewConfig()
expect = true
)
gtest.C(t, func(t *gtest.T) {
s := g.Server(guid.S())
t.Assert(s.GetGraceful(), defaultConfig.Graceful)
s.SetGraceful(expect)
t.Assert(s.GetGraceful(), expect)
})
}

func Test_Config_GracefulTimeout(t *testing.T) {
var (
defaultConfig = ghttp.NewConfig()
expect = 3
)
gtest.C(t, func(t *gtest.T) {
s := g.Server(guid.S())
t.Assert(s.GetGracefulTimeout(), defaultConfig.GracefulTimeout)
s.SetGracefulTimeout(expect)
t.Assert(s.GetGracefulTimeout(), expect)
})
}

func Test_Config_GracefulShutdownTimeout(t *testing.T) {
var (
defaultConfig = ghttp.NewConfig()
expect = 10
)
gtest.C(t, func(t *gtest.T) {
s := g.Server(guid.S())
t.Assert(s.GetGracefulShutdownTimeout(), defaultConfig.GracefulShutdownTimeout)
s.SetGracefulShutdownTimeout(expect)
t.Assert(s.GetGracefulShutdownTimeout(), expect)
})
}
4 changes: 4 additions & 0 deletions util/gconv/gconv.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,10 @@ var (
}
)

// IUnmarshalValue is the interface for custom defined types customizing value assignment.
// Note that only pointer can implement interface IUnmarshalValue.
type IUnmarshalValue = localinterface.IUnmarshalValue

func init() {
// register common converters for internal usage.
structcache.RegisterCommonConverter(structcache.CommonConverter{
Expand Down
Loading

0 comments on commit 2fc1562

Please sign in to comment.