Skip to content

reaper idle php-cgi process and graceful restart php-cgi process #1

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions conf/conf.go
Original file line number Diff line number Diff line change
@@ -20,10 +20,14 @@ type Instance struct {
ExecPath string `json:"ExecPath"`
Args []string `json:"Args"`
Env []string `json:"Env"`
// MaxRequestsPerProcess 每個php-cgi行程最多能夠處理幾次要求 , Default 500
MaxRequestsPerProcess int `json:"MaxRequestsPerProcess,500"`
// MaxRequestsPerProcess 每個php-cgi行程最多能夠處理幾次要求 , Default 5000
MaxRequestsPerProcess int `json:"MaxRequestsPerProcess,5000"`
// MaxProcesses 定義 Instance 啟動 php-cgi 的最大數量,default 4
MaxProcesses int `json:"MaxProcesses,4"`
// MaxProcesses 定義 Instance 啟動 php-cgi 的最大數量,default 2
MinProcesses int `json:"MinProcesses,2"`
// IdleTimeout 回收空闲时间超过 IdleTimeout 秒的进程,default 20s, set 0 to disable
IdleTimeout int `json:"IdleTimeout,20"`
// Note 只是註解,此欄位沒有任何作用
Note string `json:"-"`
}
5 changes: 0 additions & 5 deletions go.mod
Original file line number Diff line number Diff line change
@@ -10,13 +10,8 @@ require (
github.com/konsorten/go-windows-terminal-sequences v1.0.2 // indirect
github.com/kr/pretty v0.1.0 // indirect
github.com/sirupsen/logrus v1.4.2
github.com/stretchr/objx v0.2.0 // indirect
golang.org/x/crypto v0.0.0-20190911031432-227b76d455e7 // indirect
golang.org/x/net v0.0.0-20190912160710-24e19bdeb0f2
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e // indirect
golang.org/x/sys v0.0.0-20190912141932-bc967efca4b8
golang.org/x/text v0.3.2 // indirect
golang.org/x/tools v0.0.0-20190912152909-b0a6c2aa3ffa // indirect
gopkg.in/alecthomas/kingpin.v2 v2.2.6
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 // indirect
gopkg.in/natefinch/lumberjack.v2 v2.0.0
200 changes: 0 additions & 200 deletions go.sum

Large diffs are not rendered by default.

148 changes: 111 additions & 37 deletions main.go
Original file line number Diff line number Diff line change
@@ -25,11 +25,14 @@ var (
commandInstall *kingpin.CmdClause
commandUninstall *kingpin.CmdClause
commandStart *kingpin.CmdClause
commandReload *kingpin.CmdClause
commandStop *kingpin.CmdClause
commandRun *kingpin.CmdClause
flagConfigFile *string

servers []*server.Server

config *conf.Conf
)

func main() {
@@ -41,7 +44,12 @@ func main() {
checkConfigFileExist(*flagConfigFile)

fmt.Println(serviceName, "Run service")
if err := winsvc.RunAsService(serviceName, startService, stopService, false); err != nil {
serviceHandel := &server.WinService{
Start: startService,
Stop: stopService,
Reload: reloadService,
}
if err := server.RunAsService(serviceName, serviceHandel); err != nil {
log.Fatalf(serviceName+" run: %v\n", err)
}
} else {
@@ -62,16 +70,21 @@ func main() {
startService()
case commandStart.FullCommand():
if err := winsvc.StartService(serviceName); err != nil {
fmt.Println("Start service:", err)
fmt.Println("Start service: ", err)
os.Exit(1)
}
fmt.Println("Start service: success")
fmt.Println("Start service : success")
case commandReload.FullCommand():
if err := server.ReloadService(serviceName); err != nil {
fmt.Println("Reload service :", err)
}
fmt.Println("Reload service : success")
case commandStop.FullCommand():
if err := winsvc.StopService(serviceName); err != nil {
fmt.Println("Stop service:", err)
fmt.Println("Stop service :", err)
os.Exit(1)
}
fmt.Println("Stop service: success")
fmt.Println("Stop service : success")
return
}
}
@@ -83,6 +96,7 @@ func initCommandFlag() {
commandUninstall = kingpin.Command("uninstall", "Uninstall service")
commandStart = kingpin.Command("start", "Start service.")
commandStop = kingpin.Command("stop", "Stop service.")
commandReload = kingpin.Command("reload", "reload config and graceful restart php-cgi processes.")
commandRun = kingpin.Command("run", "Run in console mode")
flag := kingpin.Flag("conf", "Config file path , required by install or run.")
if len(os.Args) > 1 && (os.Args[1] == "install" || os.Args[1] == "run") {
@@ -121,25 +135,15 @@ func installService() {

// 啟動服務
func startService() {
config, err := conf.LoadFile(*flagConfigFile)
var err error

if err != nil {
fmt.Printf("Config load error : %s\n", err.Error())
os.Exit(1)
}

fmt.Printf("Start in console mode , press CTRL+C to exit ...\r\n")
initLogger(config)
err = phpfpm.Start(config)
if err != nil {
log.Fatalf("Can not start service : %s\n", err.Error())
}
loadConfig()

var events server.Event

events.OnConnect = func(c *server.Conn) (action server.Action) {

p := phpfpm.GetIdleProcess(c.Server().Tag.(int))
pool := c.Server().PhpfpmPool
p := pool.GetIdleProcess()

if p == nil {
if log.IsLevelEnabled(log.ErrorLevel) {
@@ -153,12 +157,12 @@ func startService() {
log.Debugf("php-cgi(%s) proxy error , serr : %s , terr : %s", p.ExecWithPippedName(), serr, terr)
}

phpfpm.PutIdleProcess(p)
pool.PutIdleProcess(p)

return
}

conf := phpfpm.Conf()
conf := config

var wg sync.WaitGroup

@@ -168,7 +172,23 @@ func startService() {

for i := 0; i < len(conf.Instances); i++ {
instance := conf.Instances[i]
servers[i] = &server.Server{MaxConnections: instance.MaxProcesses, BindAddress: instance.Bind, Tag: i}
phpfpmInst := phpfpm.NewPhpFpmInstance(i, instance)

if err != nil {
log.Fatalf("Can not start service : %s\n", err.Error())
}

err = phpfpmInst.Start()
if err != nil {
log.Fatalf("Can not start service : %s\n", err.Error())
}

servers[i] = &server.Server{
MaxConnections: instance.MaxProcesses,
BindAddress: instance.Bind,
Tag: i,
PhpfpmPool: phpfpmInst,
}

log.Infof("Start server #%d on %s", i, servers[i].BindAddress)

@@ -194,14 +214,47 @@ func startService() {
}()

wg.Wait()
phpfpm.Stop()
log.Info("Service Stopped.")
}

func reloadService() {
var err error

loadConfig()

for i := 0; i < len(servers); i++ {

oldFpm := servers[i].PhpfpmPool

// create and replace instance
if len(config.Instances)-1 >= i {
instance := config.Instances[i]
phpfpmInst := phpfpm.NewPhpFpmInstance(i, instance)
if err != nil {
log.Fatalf("Can not restart service : %s\n", err.Error())
continue
}

err = phpfpmInst.Start()
if err != nil {
log.Fatalf("Can not restart service : %s\n", err.Error())
continue
}

servers[i].PhpfpmPool = phpfpmInst
}

// stop old instance
oldFpm.Stop()
}

}

// 停止服務
func stopService() {

for i := 0; i < len(servers); i++ {
servers[i].PhpfpmPool.Stop()
servers[i].Shutdown()
}

@@ -215,6 +268,41 @@ func checkConfigFileExist(filepath string) {
}
}

func loadConfig() {

var err error
config, err = conf.LoadFile(*flagConfigFile)

if err != nil {
fmt.Printf("Config load error : %s\n", err.Error())
os.Exit(1)
}

fmt.Printf("Start in console mode , press CTRL+C to exit ...\r\n")
initLogger(config)

// Repair config
for i := 0; i < len(config.Instances); i++ {
if config.Instances[i].MaxRequestsPerProcess < 1 {
// Repair MaxRequestsPerProcess
log.Warnf("Instance #%d MaxRequestsPerProcess is less 1 , set to 500", i)
config.Instances[i].MaxRequestsPerProcess = 500
}

if config.Instances[i].MaxProcesses < 1 {
//Repair MaxProcesses
log.Warnf("Instance #%d MaxProcesses is less 1 , set to 4", i)
config.Instances[i].MaxProcesses = 4
}

if config.Instances[i].IdleTimeout < 5 {
//Repair MaxProcesses
log.Warnf("Instance #%d IdleTimeout is less than 5s , set to 120s", i)
config.Instances[i].IdleTimeout = 120
}
}
}

func initLogger(config *conf.Conf) {

formatter := &MyTextFormatter{timeFormat: "2006-01-02 15:04:05 -0700"}
@@ -258,20 +346,6 @@ func initLogger(config *conf.Conf) {
log.SetLevel(logLevel)
log.Infof("Set LogLevel to %s.", strings.ToUpper(logLevel.String()))

// Repair config
for i := 0; i < len(config.Instances); i++ {
if config.Instances[i].MaxRequestsPerProcess < 1 {
// Repair MaxRequestsPerProcess
log.Warnf("Instance #%d MaxRequestsPerProcess is less 1 , set to 500", i)
config.Instances[i].MaxRequestsPerProcess = 500
}

if config.Instances[i].MaxProcesses < 1 {
//Repair MaxProcesses
log.Warnf("Instance #%d MaxProcesses is less 1 , set to 4", i)
config.Instances[i].MaxProcesses = 4
}
}
}

// MyTextFormatter logrus custom formatter
Loading