zoukankan      html  css  js  c++  java
  • 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)
    	}
    }
    
    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进程的执行

  • 相关阅读:
    自主问题--KMP算法
    题解--luogu--CSP2019.S.Day2.T4--Emiya 家今天的饭
    浅谈——RMQ
    浅谈——LCA
    NOIP(si le)或者CSP初赛之——前序中序后序,前缀中缀后缀
    浅说——查分约束
    浅说——tarjan
    C++ RQNOJ 星门龙跃
    C++ 洛谷 1261:【例9.5】城市交通路网
    刷题
  • 原文地址:https://www.cnblogs.com/tobemaster/p/11986814.html
Copyright © 2011-2022 走看看