脚本之家,脚本语言编程技术及教程分享平台!
分类导航

Python|VBS|Ruby|Lua|perl|VBA|Golang|PowerShell|Erlang|autoit|Dos|bat|

服务器之家 - 脚本之家 - Golang - golang 监听服务的信号,实现平滑启动,linux信号说明详解

golang 监听服务的信号,实现平滑启动,linux信号说明详解

2021-06-23 00:52胖达喵 Golang

这篇文章主要介绍了golang 监听服务的信号,实现平滑启动,linux信号说明详解,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧

监听服务的信号,实现平滑启动,linux信号说明

 

golang 监听服务的信号,实现平滑启动,linux信号说明详解

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
package main
import (
    "context"
    "fmt"
    "golang.org/x/sync/errgroup"
    "net/http"
    "os"
    "os/signal"
    "syscall"
)
 
func main() {
    g, ctx := errgroup.WithContext(context.Background())
    fmt.Println("服务启动start!")
    addr := ":9091"
    s :=&http.Server{
        Addr: addr,
        Handler:http.DefaultServeMux,
    }
    g.Go(func() error {
        http.HandleFunc("/test1", func(writer http.ResponseWriter, request *http.Request) {
            fmt.Println("tes1")
            writer.Write([]byte("tes1"))
        })
        return s.ListenAndServe()
    })
    g.Go(func() error {
        exit := make(chan os.Signal)
        //监听 Ctrl+C 信号
        signal.Notify(exit, syscall.SIGINT, syscall.SIGTERM)
        select {
        case <-exit:
            fmt.Println("进程已被取消~")
            return s.Shutdown(ctx)
        }
    })
    err := g.Wait()
    if err != nil {
        fmt.Println(err)
    }
    fmt.Println("服务启动成功!")
    if ctx.Err() !=nil {
        fmt.Println(ctx.Err())
        fmt.Println("服务关闭成功!")
        os.Exit(0)
    }
 
}

补充:golang http服务实现平滑重启

看代码吧~

 

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
package main
import (
    "context"
    "encoding/json"
    "fmt"
    "math/rand"
    "net/http"
    "os"
    "os/signal"
    "time"
)
 
var logChan  = make(chan map[string]interface{})
var requestStatusMap = map[int]bool{} 
var done = make(chan bool, 1)
var quit = make(chan os.Signal, 1)
 
//为什么这样可以平滑重启?
// 正常情况下是server.ListenAndServe() 这个位置hang住整个进程的
// 可以把这个程序看成两部分,1个是web服务的监听部分,一个是处理部分, 如果web服务器不开启了,那么就不能处理新进来的请求了(可以理解为一个带路的)
// 真正让这个请求断掉  是因为主进程(main)被kill
// 所以平滑重启的原理就是,先kill掉web服务器,不让新的请求进来,等现有的全部请求完了,然后结束当前进程
func main() {
    server := newServer()
    signal.Notify(quit, os.Interrupt)
    go monitorKill(server, quit)
    server.ListenAndServe()
    <-done
}
 
func newServer() *http.Server {
    router := http.NewServeMux()
    router.HandleFunc("/hello", sayHello)
    return &http.Server{
        Addr:         ":8262",
        Handler:      router,
    }
}
 
func monitorKill(server *http.Server, quit <-chan os.Signal)  {
    <-quit
    go shutDown(server)
    for {
        if len(requestStatusMap) != 0 {
            fmt.Println("目前还有进行中的请求,请稍等")
            time.Sleep(time.Second * 1)
            continue
        } else {
            close(done)
            break
        }
    }
}
 
func shutDown(server *http.Server) {
    if err := server.Shutdown(context.Background()); err != nil {
        fmt.Println(err)
    }
}
 
func sayHello(w http.ResponseWriter, r *http.Request) {
    go WriteInfo()//请求写日志
    var uniqueId = GenerateRangeNum(1, 1000)
    requestStatusMap[uniqueId] = false
    url := r.URL.Path
    query  := r.URL.RawQuery
    method := r.Method
    a := map[string] interface{}{
        "url" : url,
        "method" : method,
        "query" : query,
        "response": "hello world!",
    }
    logChan<-a
    w.Write([]byte("hello world!"))
    time.Sleep(time.Second * 10)
    delete(requestStatusMap, uniqueId)
}
 
func WriteInfo() {
    info := <-logChan
    fileName := "/tmp/weekhomework.log"
    _, err := os.Stat(fileName)
    if err != nil || os.IsNotExist(err) {
        _, _ = os.Create(fileName)
    }
    f,err := os.OpenFile(fileName, os.O_WRONLY, 0644)
    defer f.Close()
    if err !=nil {
        fmt.Println(err.Error())
    } else {
        //追加写入   为什么O_APPEND 模式无法写入? todo
        n, _ := f.Seek(0, 2)
        infostr, _ := json.Marshal(info)
        _,err=f.WriteAt([]byte(string(infostr) +"\n"), n)
    }
}
 
func GenerateRangeNum(min int, max int) int {
    if min == max {
        return min
    }
    rand.Seed(time.Now().Unix())
    randNum := rand.Intn(max-min) + min
    return randNum
}

主要思路:

 

对于每个请求都做记录,处理完成之后做删除。 用一个协程去监控中断信号,有中断信号先把http服务关闭。

如果这个时候还有请求没有处理完,那么就轮训等待,等全部处理完那么就 发出终止信号结束main进程的执行

以上为个人经验,希望能给大家一个参考,也希望大家多多支持服务器之家。如有错误或未考虑完全的地方,望不吝赐教。

原文链接:https://pangda.blog.csdn.net/article/details/116259908

延伸 · 阅读

精彩推荐
  • Golanggo语言制作端口扫描器

    go语言制作端口扫描器

    本文给大家分享的是使用go语言编写的TCP端口扫描器,可以选择IP范围,扫描的端口,以及多线程,有需要的小伙伴可以参考下。 ...

    脚本之家3642020-04-25
  • Golanggo日志系统logrus显示文件和行号的操作

    go日志系统logrus显示文件和行号的操作

    这篇文章主要介绍了go日志系统logrus显示文件和行号的操作,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧...

    SmallQinYan12302021-02-02
  • Golanggolang如何使用struct的tag属性的详细介绍

    golang如何使用struct的tag属性的详细介绍

    这篇文章主要介绍了golang如何使用struct的tag属性的详细介绍,从例子说起,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看...

    Go语言中文网11352020-05-21
  • GolangGolang中Bit数组的实现方式

    Golang中Bit数组的实现方式

    这篇文章主要介绍了Golang中Bit数组的实现方式,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧...

    天易独尊11682021-06-09
  • GolangGolang通脉之数据类型详情

    Golang通脉之数据类型详情

    这篇文章主要介绍了Golang通脉之数据类型,在编程语言中标识符就是定义的具有某种意义的词,比如变量名、常量名、函数名等等,Go语言中标识符允许由...

    4272021-11-24
  • Golanggolang的httpserver优雅重启方法详解

    golang的httpserver优雅重启方法详解

    这篇文章主要给大家介绍了关于golang的httpserver优雅重启的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,...

    helight2992020-05-14
  • Golanggolang json.Marshal 特殊html字符被转义的解决方法

    golang json.Marshal 特殊html字符被转义的解决方法

    今天小编就为大家分享一篇golang json.Marshal 特殊html字符被转义的解决方法,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧 ...

    李浩的life12792020-05-27
  • Golanggolang 通过ssh代理连接mysql的操作

    golang 通过ssh代理连接mysql的操作

    这篇文章主要介绍了golang 通过ssh代理连接mysql的操作,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧...

    a165861639710342021-03-08