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

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

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服务实现平滑重启

看代码吧~

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进程的执行

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

(0)

相关推荐

  • golang 如何实现HTTP代理和反向代理

    代理的核心功能可以用一句话概括:接受客户端的请求,转发到后端服务器,获得应答之后返回给客户端. 代理的功能有很多,事实上整个互联网到处都充斥着代理服务器.如果所有的 HTTP 访问都是客户端和服务器端直接进行的话,我们的网络不仅会变得缓慢,而且性能会大打折扣. 代理服务器根据不同的配置和使用,可能会有不同的功能,这些功能主要包括: 内容过滤:代理可以根据一定的规则限制某些请求的连接.比如有些公司会设置内部网络无法访问某些购物.游戏网站,或者学校的网络不让学生访问色情暴力的网站等 节省成本:代理服

  • golang 实现时间戳和时间的转化

    说实话,golang的时间转化还是很麻烦的,最起码比php麻烦很多,上码好好学学一下 package main import ( "time" "fmt" ) func main() { //获取当前时间 t := time.Now() //2018-07-11 15:07:51.8858085 +0800 CST m=+0.004000001 fmt.Println(t) //获取当前时间戳 fmt.Println(t.Unix()) //1531293019 /

  • golang如何获得一个变量的类型

    直接使用reflect的TypeOf方法就可以了 fmt.Println(reflect.TypeOf(var)) 补充:golang 获取变量类型的三种方式 Using string formatting func typeof(v interface{}) string { return fmt.Sprintf("%T", v) } Using reflect package func typeof(v interface{}) string { return reflect.Ty

  • Golang Gob编码(gob包的使用详解)

    gob是Golang包自带的一个数据结构序列化的编码/解码工具.编码使用Encoder,解码使用Decoder.一种典型的应用场景就是RPC(remote procedure calls). gob和json的pack之类的方法一样,由发送端使用Encoder对数据结构进行编码.在接收端收到消息之后,接收端使用Decoder将序列化的数据变化成本地变量. 基本使用 package main import ( "bytes" "encoding/gob" "

  • golang 如何获取文件夹下面的文件列表

    golang的文档里面比较容易找到的一个方法是filepath.Walk,这个方法有个问题是会自动递归遍历当前目录的子目录,其实我们通常只是想得到一个目录下面的文件列表,并不需要这么多的信息,同时这个方法代码写起来也比较多比较复杂,我们也没有必要这么做. 如果仅仅是想获取一个目录下面的文件和文件夹的列表,有两个比较简单的方法 利用ioutil的ReadDir方法 package main import ( "fmt" "io/ioutil" ) func main(

  • golang值类型转换成[]uint8类型的操作

    在go语言中,byte其实是uint8的别名,byte 和 uint8 之间可以直接进行互转. 目前来只能将0~255范围的int转成byte. func Int64ToBytes(num int64) []uint8 { var buffer bytes.Buffer err := binary.Write(&buffer, binary.BigEndian, num) if err != nil { logs.Error("int64转[]uint8失败%v", err)

  • Golang实现http文件上传小功能的案例

    看代码吧~ package main import ( "fmt" "io" "net/http" "os" ) func main() { http.HandleFunc("/", index) http.HandleFunc("/upload", upload) http.ListenAndServe(":1789", nil) } func upload(w h

  • 基于golang uint8、int8与byte的区别说明

    简单说明 uint8与byte可以说是一样的,因为文档中有这样的定义: The Go Programming Language Specification Numeric types uint8 the set of all unsigned 8-bit integers (0 to 255) byte alias for uint8 也就是说,我们在需要将这两种类型转换为string的时候都是可以直接使用string()来进行的. 而int8的取值范围为-128~127,所以int8不能直接与

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

    监听服务的信号,实现平滑启动,linux信号说明 package main import ( "context" "fmt" "golang.org/x/sync/errgroup" "net/http" "os" "os/signal" "syscall" ) func main() { g, ctx := errgroup.WithContext(context.

  • 关于golang监听rabbitmq消息队列任务断线自动重连接的问题

    golang监听消息队列rabbitmq任务脚本,当rabbimq消息队列断开连接后自动重试,重新唤起协程执行任务 需求背景: goalng常驻内存任务脚本监听rbmq执行任务 任务脚本由supervisor来管理 当rabbitmq长时间断开连接会出现如下图 进程处于fatal状态 假如因为不可抗拒因素,rabbitmq服务器内存满了或者其它原因导致rabbitmq消息队列服务停止了 如果是短时间的停止重启,supervisor是可以即时唤醒该程序.如果服务器长时间没有恢复正常运行,程序就会出

  • Golang监听日志文件并发送到kafka中

    目录 前言 涉及的golang库和可视化工具: 工作的流程 环境准备 代码分层 关键的代码 main.go kafka.go tail.go 前言 日志收集项目的准备中,本文主要讲的是利用golang的tail库,监听日志文件的变动,将日志信息发送到kafka中. 涉及的golang库和可视化工具: go-ini,sarama,tail其中: go-ini:用于读取配置文件,统一管理配置项,有利于后其的维护 sarama:是一个go操作kafka的客户端.目前我用于向kefka发送消息 tail

  • npm script命令同时进行多个监听服务的方法

    最近在搭建一个静态页面偏多的网站, 用vue或React有点大材小用,使用纯html / css / js 又不好用, 于是就用npm手动搭建一个简单的本地开发环境, 本地环境要实现几个基本功能 在本地开启http服务 ; 且开启服务后, 会自动打开浏览器 浏览器自动刷新 ; 源码变化后, 浏览器会自动刷新显示内容 支持sass语法 ; 将sass代码实时转换为css 支持es6语法 ; 使用babel将es6转换为es5 开启http服务, 自动开启浏览器, 实现浏览器自动刷新的实现思路是,在

  • golang使用 gomodule 在公共测试环境管理go的依赖的实例详解

    背景:调试服务最好的方式就是直接上机实践.对在公司的员工来说,在同一套服务上协同开发比在单独的环境上开发,应该会更有感觉.有问题可以一起发现并解决,也能够一同开发需求. 但是,公司的测试机往往是没办法连外网的,而golang 的大部分工程都需要直接从github 上下载依赖,这就导致 依赖文件需要先提前上传到开发机上.那么当开发机上需要运行多个golang 工程的时候,如何共享这些依赖,减少维护依赖库的工作量呢? 这也是需要大家协作完成的- 最终总结:项目采用 go module + vendo

  • GoLang jwt无感刷新与SSO单点登录限制解除方法详解

    目录 前言 为什么使用JWT Cookie和Session token (header.payload.signature) token 安全性 基于token安全性的处理 客户端与服务端基于无感刷新流程图 golang实现atoken和rtoken 颁发token 校验token 无感刷新token 完整实现代码 SSO(Single Sign On)单用户登录以及无感刷新token 实现思路 实战代码 小结 前言 为什么使用JWT Jwt提供了生成token以及token验证的方法,而tok

  • golang实现php里的serialize()和unserialize()序列和反序列方法详解

    Golang 实现 PHP里的 serialize() . unserialize() 安装 go get -u github.com/techleeone/gophp/serialize 用法 package main import ( "fmt" "github.com/techleeone/gophp/serialize" ) func main() { str := `a:1:{s:3:"php";s:24:"世界上最好的语言&

  • golang常用库之操作数据库的orm框架-gorm基本使用详解

    golang常用库:gorilla/mux-http路由库使用 golang常用库:配置文件解析库-viper使用 golang常用库:操作数据库的orm框架-gorm基本使用 一:字段映射-模型定义 gorm中通常用struct来映射字段. gorm教程中叫模型定义 比如我们定义一个模型Model: type User struct { gorm.Model UserId int64 `gorm:"index"` //设置一个普通的索引,没有设置索引名,gorm会自动命名 Birth

  • golang将多路复异步io转成阻塞io的方法详解

    前言 本文主要给大家介绍了关于golang 如何将多路复异步io转变成阻塞io的相关内容,分享出来供大家参考学习,下面话不多说了,来一起看看详细的介绍: package main import ( "net" ) func handleConnection(c net.Conn) { //读写数据 buffer := make([]byte, 1024) c.Read(buffer) c.Write([]byte("Hello from server")) } fu

  • 在服务端(Page.Write)调用自定义的JS方法详解

    首先,我们应该可以先明确,为什么我们用Page.Write把自定义的JS方法输出到页面上为什么IE不能识别,会出现"XXX未定义"的错误.原因很简单,因为我们用Page.Write输出的脚本是出现在页面的最顶端.IE读到是javascript函数的时候,就开始执行,但是此时我们link的js文件并未被IE读入,所以IE无法识别我们定义在js文件里面的方法.那write alert为什么可以呢?因为alert是IE内嵌的脚本功能函数,不管有没有页面,IE都认得它.找到问题所在,自然就好解

随机推荐