github.com/oklog/runパッケージはhttpのスムーズなシャットダウンを実現します

1。概要:

httpサービスは独立したコルーチンであり、オペレーティングシステムのシグナルを監視する独立したコルーチンもあります。これはgithub.com/oklog/runパッケージを介して結合され、コラボレーションします。オペレーティングシステムのシグナルを監視するゴルーチンがkillを受信するとシグナル、それはこのコルーチンを終了します、httpサービスコルーチンはこの時点で自動的に終了します。github.com/oklog/runパッケージを使用して、比較的独立した相互に関連する複数のコルーチンを組み合わせると非常に便利です。
コード:https://github.com/gzlj/http-demo.git

2)コード構造

ここに画像の説明を挿入

3)メインコード

http-demo / cmd / gracehttp-demo /main.go文件

package main

import (
	"fmt"
	"os"
	"os/signal"
	"syscall"
	"time"
	"github.com/go-kit/kit/log"
	"github.com/go-kit/kit/log/level"
	"github.com/gzlj/http-demo/pkg/prober"
	"github.com/oklog/run"
	"github.com/pkg/errors"
	httpserver "github.com/gzlj/http-demo/pkg/server/http"
)


const (
	logFormatLogfmt                     = "logfmt"
	logFormatJson                       = "json"
)

func main() {
	var logFormat string = "logfmt"
	logger := log.NewLogfmtLogger(log.NewSyncWriter(os.Stdout))
	if logFormat == logFormatJson {
		logger = log.NewJSONLogger(log.NewSyncWriter(os.Stdout))
	}
	logger = log.With(logger, "ts", log.DefaultTimestamp)
	logger = log.With(logger, "caller", log.DefaultCaller)

	httpProbe := prober.NewHTTP()
	probers := prober.Combine(
		httpProbe,
	)

	//返回http服务结构体实例,它封装了原生http服务
	srv := httpserver.New(
		logger,
		"test-http",
		httpProbe,
		httpserver.WithListen(":80"),
		httpserver.WithGracePeriod(time.Duration(5)),
	)

	var g run.Group
	//启动http服务
	g.Add(
	//此方法是actor的execute()方法
	func() error {
		probers.Healthy()
		return srv.ListenAndServe()
	},
	//此方法是actor的interrupt()方法
	func(err error) {
		probers.NotReady(err)
		defer probers.NotHealthy(err)
		//平滑关闭http服务
		srv.Shutdown(err)
	})

	//监听来自操作系统的杀死信号
	{
		cancel := make(chan struct{})
		g.Add(func() error {
			return interrupt(logger, cancel)
		}, func(error) {
			close(cancel)
		})
	}

	//Run方法中,先遍历所有actor执行actor的execute()方法
	//一旦有一个actor返回error接口(值可能是nil),则遍历actor调用其interrupt()方法(入参都是这个error接口 )
	//interrupt()中往往记录日志以及平滑关闭actor
	//Run方法最后部分是等待所有剩余actor执行退出
	//Run方法返回的是第一个actor返回的error接口
	if err := g.Run(); err != nil {
		level.Error(logger).Log("err", fmt.Sprintf("%+v", errors.Wrapf(err, "command run failed")))
		os.Exit(1)
	}
	level.Info(logger).Log("msg", "exiting")
}

func interrupt(logger log.Logger, cancel <-chan struct{}) error {
	c := make(chan os.Signal, 1)
	signal.Notify(c, syscall.SIGINT, syscall.SIGTERM)
	//接收到杀死信号或结束信号时,进行方法返回
	select {
	case s := <-c:
		level.Info(logger).Log("msg", "caught signal. Beginng to exit.", "signal", s)
		return nil
	case <-cancel:
		return errors.New("canceled")
	}
}


http-demo / pkg / server / http / http.go


package http

import (
	"context"
	"github.com/go-kit/kit/log"
	"github.com/go-kit/kit/log/level"
	"github.com/gzlj/http-demo/pkg/prober"
	"github.com/pkg/errors"
	"net/http"
)

// A Server defines parameters for serve HTTP requests, a wrapper around http.Server.
type Server struct {
	logger log.Logger
	prober *prober.HTTPProbe
	mux *http.ServeMux
	srv *http.Server
	opts options
}

// constructor
func New(logger log.Logger, name string, prober *prober.HTTPProbe, opts ...Option) *Server {
	options := options{}
	for _, o := range opts {
		o.apply(&options)
	}
	mux := http.NewServeMux()
	registerProbes(mux, prober, logger)

	return &Server{
		logger: log.With(logger, "service", "http/server", "component", name),
		prober: prober,
		mux:    mux,
		srv:    &http.Server{Addr: options.listen, Handler: mux},
		opts:   options,
	}
}

func registerProbes(mux *http.ServeMux, p *prober.HTTPProbe, logger log.Logger) {
	if p != nil {
		mux.Handle("/-/healthy", p.HealthyHandler(logger))
		mux.Handle("/-/ready", p.ReadyHandler(logger))
	}
}


func (s *Server) ListenAndServe() error {
	level.Info(s.logger).Log("msg", "listening for requests and metrics", "address", s.opts.listen)
	return errors.Wrap(s.srv.ListenAndServe(), "serve HTTP and metrics")
}

func (s *Server) Shutdown(err error) {
	level.Info(s.logger).Log("msg", "internal server is shutting down", "err", err)
	if err == http.ErrServerClosed {
		level.Warn(s.logger).Log("msg", "internal server closed unexpectedly")
		return
	}

	if s.opts.gracePeriod == 0 {
		s.srv.Close()
		level.Info(s.logger).Log("msg", "internal server is shutdown", "err", err)
		return
	}

	ctx, cancel := context.WithTimeout(context.Background(), s.opts.gracePeriod)
	defer cancel()

	if err := s.srv.Shutdown(ctx); err != nil {
		level.Error(s.logger).Log("msg", "internal server shut down failed", "err", err)
		return
	}
	level.Info(s.logger).Log("msg", "internal server is shutdown gracefully", "err", err)
}

func (s *Server) Handle(pattern string, handler http.Handler) {
	s.mux.Handle(pattern, handler)
}

4)効果


ここに画像の説明を挿入
httpサービスのヘルスインターフェイスにアクセスします。httpサービスにkillシグナルを送信します。
ここに画像の説明を挿入

おすすめ

転載: blog.csdn.net/nangonghen/article/details/108016969