go设置守护进程启动服务器+平滑重启

之前我们搭建过一个 ai服务器并设置了https访问 : https://blog.csdn.net/daily886/article/details/98080151

现在在他基础上 使用守护进程启动服务器+平滑重启

守护进程是在后台运行不受终端控制的进程(如输入、输出等),一般的网络服务都是以守护进程的方式运行。守护进程脱离终端的主要原因有两点:(1)用来启动守护进程的终端在启动守护进程之后,需要执行其他任务。(2)(如其他用户登录该终端后,以前的守护进程的错误信息不应出现)由终端上的一些键所产生的信号(如中断信号),不应对以前从该终端上启动的任何守护进程造成影响。要注意守护进程与后台运行程序(即加&启动的程序)的区别。

建立文件夹和文件 aichat/service/hotupdate.go ,hotupdate.go 内容如下:

package service

import "C"
import (
	"context"
	"errors"
	"flag"
	"log"
	"net"
	"net/http"
	"os"
	"os/exec"
	"os/signal"
	"syscall"
	"time"
)

/************************** 热重启 ***************************/

var (
	listener net.Listener = nil

	graceful =  flag.Bool("graceful", false, "listen on fd open 3 (internal use only)")

	pemPath = "/usr/local/orange/conf/cert/online/go.daily886.com.pem"
	keyPath = "/usr/local/orange/conf/cert/online/go.daily886.com.key"
)


//监听服务器
func Listenserver(server *http.Server){
	var err error

	//解析参数
	flag.Parse()

	//设置监听的对象(新建或已存在的socket描述符)
	if *graceful {
		//子进程监听父进程传递的 socket描述符
		log.Println("listening on the existing file descriptor 3")
		//子进程的 0 1 2 是预留给 标准输入 标准输出 错误输出
		//因此传递的socket 描述符应该放在子进程的 3
		f := os.NewFile(3,"")
		listener,err = net.FileListener(f)
		log.Printf( "graceful-reborn  %v %v  %#v \n", f.Fd(), f.Name(), listener)
	}else{
		//启动守护进程
		daemonProcce(1,1);
		//父进程监听新建的 socket 描述符
		log.Println("listening on a new file descriptor")
		listener,err = net.Listen("tcp",server.Addr)
		log.Printf("Actual pid is %d\n", syscall.Getpid())

	}
	if err != nil{
		log.Fatalf("listener error: %v\n",err)
	}
	go func(){
		err = server.ServeTLS(listener,pemPath,keyPath)
		log.Printf("server.Serve err: %v\n",err)
		tcp,_ := listener.(*net.TCPListener)
		fd,_ := tcp.File()
		log.Printf( "first-boot  %v %v %#v \n ", fd.Fd(),fd.Name(), listener)
	}()
	//监听信号
	handleSignal(server)
	log.Println("signal end")
}

//处理信号
func handleSignal(server *http.Server){
	//把信号 赋值给 通道
	ch := make(chan os.Signal, 1)
	//监听信号
	signal.Notify(ch, syscall.SIGINT,syscall.SIGTERM,syscall.SIGUSR2)
	//阻塞主进程, 不停的监听系统信号
	for{
		//通道 赋值给 sig
		sig := <-ch
		log.Printf("signal receive: %v\n", sig)
		ctx,_ := context.WithTimeout(context.Background(),20*time.Second)
		switch sig{
		case syscall.SIGINT,syscall.SIGTERM:  //终止进程执行
			log.Println("shutdown")
			signal.Stop(ch)       //停止通道
			server.Shutdown(ctx)  //关闭服务器窗口
			log.Println("graceful shutdown")
			return
		case syscall.SIGUSR2:  //进程热重启
			log.Println("reload")
			err := reload()  //执行热重启
			if err != nil{
				log.Fatalf("listener error: %v\n",err)
			}
			//server.Shutdown(ctx)
			log.Println("graceful reload")
			return
		}
	}
}

//热重启
func reload() error{
	tl, ok := listener.(*net.TCPListener)
	if !ok {
		return errors.New("listener is not tcp listener")
	}
	//获取socket描述符
	currentFD, err := tl.File()
	if err != nil {
		return err
	}
	//设置传递给子进程的参数(包含 socket描述符)
	args := []string{"-graceful"}
	//args = append(args, "-continue")
	cmd := exec.Command(os.Args[0],args...)
	cmd.Stdout = os.Stdout  //标准输出
	cmd.Stderr = os.Stderr  //错误输出
	cmd.ExtraFiles = []*os.File{currentFD} //文件描述符

	err = cmd.Start()
	log.Printf("forked new pid %v: \n",cmd.Process.Pid)
	if err != nil{
		return err
	}
	return nil
}
/*
我们在父进程执行 cmd.ExtraFiles = []*os.File{f} 来传递 socket 描述符给子进程,子进程通过执行 f := os.NewFile(3, "") 来获取该描述符。值得注意的是,子进程的 0 、1 和 2 分别预留给标准输入、标准输出和错误输出,所以父进程传递的 socket 描述符在子进程的顺序是从 3 开始。
*/


//nochdir 是 程序初始路径 1是当前路径,0是系统根目录
//noclose 是 错误信息输出 1是输出当前, 0是不显示错误信息
func daemonProcce(nochdir, noclose int) (int,error){
	// already a daemon
	log.Printf("syscall.Getppid() %+v\n",syscall.Getppid())
	//如果是守护进程 syscall.Getppid() = 1
	if syscall.Getppid() == 1 {
		/* Change the file mode mask */
		syscall.Umask(0)

		if nochdir == 0 {
			os.Chdir("/")
		}

		return 0, nil
	}

	files := make([]*os.File, 3, 6)
	if noclose == 0 {
		nullDev, err := os.OpenFile("/dev/null", 0, 0)
		if err != nil {
			return 1, err
		}
		files[0], files[1], files[2] = nullDev, nullDev, nullDev
	} else {
		files[0], files[1], files[2] = os.Stdin, os.Stdout, os.Stderr
	}

	dir, _ := os.Getwd()
	sysattrs := syscall.SysProcAttr{Setsid: true}
	attrs := os.ProcAttr{Dir: dir, Env: os.Environ(), Files: files, Sys: &sysattrs}

	proc, err := os.StartProcess(os.Args[0], os.Args, &attrs)
	if err != nil {
		return -1, err
	}
	proc.Release()
	os.Exit(0)

	return 0, nil
}

修改文件 aichat/main.go ,main.go 内容如下:

package main

import (
	"log"
	"net/http"

	"github.com/gin-gonic/gin"
	"github.com/spf13/viper"

	"aichat/config"
	"aichat/router"
	"aichat/service"
)

func main() {
	if err := config.Init();err != nil{
		panic(err)
	}
	//设置gin模式
	gin.SetMode(viper.GetString("common.server.runmode"))

	//创建一个gin引擎
	g := gin.New()

	router.InitRouter(g)
	log.Printf("开始监听服务器地址: %s\n", viper.GetString("common.server.url"))

	// Listen and Server in https://127.0.0.1:8080
	//err := g.Run(viper.GetString("common.server.addr")) //使用http
	//err := g.RunTLS( //使用https
	//	viper.GetString("common.server.addr"),
	//	"/usr/local/orange/conf/cert/online/go.daily886.com.pem",
	//	"/usr/local/orange/conf/cert/online/go.daily886.com.key")
	//if err != nil {
	//	log.Fatal("监听错误:", err)
	//}


	//使用热重启
	// kill -USR2 pid 重启
	// kill -INT pid 关闭
	add := viper.GetString("common.server.addr")
	srv := &http.Server{
		Addr:    add,
		Handler: g,
	}

	log.Printf( "srv.Addr  %v  \n", srv.Addr)
	service.Listenserver(srv)

}

打包并运行

[root@izj6c4jirdug8kh3uo6rdez aichat]# go build
[root@izj6c4jirdug8kh3uo6rdez aichat]# ./aichat
[GIN-debug] [WARNING] Running in "debug" mode. Switch to "release" mode in production.
 - using env:	export GIN_MODE=release
 - using code:	gin.SetMode(gin.ReleaseMode)

[GIN-debug] GET    /                         --> aichat/service.Index (6 handlers)
[GIN-debug] GET    /chat                     --> aichat/service.AiChat (6 handlers)
[root@izj6c4jirdug8kh3uo6rdez aichat]# [GIN-debug] [WARNING] Running in "debug" mode. Switch to "release" mode in production.
 - using env:	export GIN_MODE=release
 - using code:	gin.SetMode(gin.ReleaseMode)

[GIN-debug] GET    /                         --> aichat/service.Index (6 handlers)
[GIN-debug] GET    /chat                     --> aichat/service.AiChat (6 handlers)

 

2019/08/02 17:36:17 main.go:26: 开始监听服务器地址: :6663
2019/08/02 17:36:17 main.go:49: srv.Addr  :6663  
2019/08/02 17:36:17 hotupdate.go:137: syscall.Getppid() 4023
2019/08/02 17:36:17 main.go:26: 开始监听服务器地址: :6663
2019/08/02 17:36:17 main.go:49: srv.Addr  :6663  
2019/08/02 17:36:17 hotupdate.go:137: syscall.Getppid() 1
2019/08/02 17:36:17 hotupdate.go:50: listening on a new file descriptor
2019/08/02 17:36:17 hotupdate.go:52: Actual pid is 5359

 

平滑重启

[root@izj6c4jirdug8kh3uo6rdez ~]# kill -USR2 5359
2019/08/02 17:37:15 hotupdate.go:80: signal receive: user defined signal 2
2019/08/02 17:37:15 hotupdate.go:90: reload
2019/08/02 17:37:15 hotupdate.go:122: forked new pid 5412: 
2019/08/02 17:37:15 hotupdate.go:96: graceful reload
2019/08/02 17:37:15 hotupdate.go:67: signal end
2019/08/02 17:37:15 main.go:26: 开始监听服务器地址: :6663
2019/08/02 17:37:15 main.go:49: srv.Addr  :6663  
2019/08/02 17:37:15 hotupdate.go:40: listening on the existing file descriptor 3
2019/08/02 17:37:15 hotupdate.go:45: graceful-reborn  3   &net.TCPListener{fd:(*net.netFD)(0xc0000e3580)} 

参考:https://segmentfault.com/q/1010000000699471

go daemon参考:https://github.com/9466/daemon/blob/master/daemon.go

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
基于MATLAB GUI的自适应滤波、平滑滤波和小波滤波是心电信号处理中常用的方法。以下是一个简单的示例MATLAB代码,用于对心电信号进行这些滤波处理。 首先,我们需要获取心电信号数据。假设我们有一个名为data的数组,包含了采样率为Fs的心电信号数据。 ``` matlab % 获取心电信号数据 Fs = 1000; % 采样率 t = 0:1/Fs:1; % 时间轴 f = 5; % 心电信号频率 data = sin(2*pi*f*t); % 心电信号数据,这里用一个正弦波信号代替 ``` 接下来,我们可以编写自适应滤波函数。自适应滤波通过动态调整滤波器参数来对信号进行平滑。这里我们使用MATLAB的adaptivefilter函数。 ``` matlab % 自适应滤波函数 filtered_data = adaptivefilter(data); ``` 然后,我们可以使用MATLAB内置的smooth函数进行平滑滤波。smooth函数可以通过移动平均、高斯滤波等方法对信号进行平滑处理。 ``` matlab % 平滑滤波函数 window_size = 10; % 窗口大小 smoothed_data = smooth(data, window_size); ``` 最后,我们可以使用MATLAB的Wavelet Toolbox提供的小波滤波函数对信号进行小波变换和滤波处理。 ``` matlab % 小波滤波函数 wname = 'db4'; % 小波基函数名 level = 4; % 分解级数 [C, L] = wavedec(data, level, wname); % 小波分解 threshold = 0.5; % 阈值 C_thresh = wthresh(C, 'h', threshold); % 高频分量阈值处理 filtered_data = waverec(C_thresh, L, wname); % 小波重构 ``` 以上是一个基于MATLAB GUI的自适应滤波、平滑滤波和小波滤波的心电信号处理的简单示例代码。注意,这只是一个演示,并不一定适用于所有情况。根据实际需求,可能需要进行更多的参数调整和优化。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值