update
This commit is contained in:
252
myhttp/httpsr/server.go
Normal file
252
myhttp/httpsr/server.go
Normal file
@@ -0,0 +1,252 @@
|
||||
package httpsr
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"git.makemake.in/kzkzzzz/mycommon"
|
||||
"git.makemake.in/kzkzzzz/mycommon/graceful"
|
||||
"git.makemake.in/kzkzzzz/mycommon/myconf"
|
||||
"git.makemake.in/kzkzzzz/mycommon/myhttp"
|
||||
"git.makemake.in/kzkzzzz/mycommon/mylog"
|
||||
"git.makemake.in/kzkzzzz/mycommon/myregistry"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/spf13/pflag"
|
||||
"log"
|
||||
"net"
|
||||
"net/http"
|
||||
|
||||
"runtime/debug"
|
||||
"time"
|
||||
)
|
||||
|
||||
var _ graceful.IRunner = (*Server)(nil)
|
||||
|
||||
type Conf struct {
|
||||
Addr string
|
||||
Port int
|
||||
Ip string
|
||||
Log bool
|
||||
ReadTimeoutMs int
|
||||
WriteTimeoutMs int
|
||||
IdleTimeoutMs int
|
||||
ShutdownTimeoutMs int
|
||||
}
|
||||
|
||||
type Opt func(server *Server)
|
||||
|
||||
type Server struct {
|
||||
serviceId string
|
||||
serviceName string
|
||||
serverConf *Conf
|
||||
reg myregistry.IRegister
|
||||
logger mylog.ILogger
|
||||
|
||||
useDefaultBufferCfg bool
|
||||
delayStopMs int
|
||||
|
||||
serviceRegInfo *myregistry.ServiceInfo
|
||||
|
||||
hs *http.Server
|
||||
ginEngine *gin.Engine
|
||||
|
||||
shutdownTimeout time.Duration
|
||||
}
|
||||
|
||||
func WithRegistry(serviceName string, reg myregistry.IRegister) Opt {
|
||||
return func(server *Server) {
|
||||
server.serviceName = myhttp.ServicePrefix + serviceName
|
||||
server.reg = reg
|
||||
}
|
||||
}
|
||||
|
||||
func WithDelayStopMs(v int) Opt {
|
||||
return func(server *Server) {
|
||||
server.delayStopMs = v
|
||||
}
|
||||
}
|
||||
|
||||
func SetFlag() {
|
||||
pflag.Int("http.port", 0, "listen port, 0 is random port")
|
||||
pflag.String("http.log", "true", "enable request log")
|
||||
}
|
||||
|
||||
func New(cfg *myconf.Config, opts ...Opt) *Server {
|
||||
cf := &Conf{}
|
||||
err := cfg.UnmarshalKey("http", cf)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// 命令行的参数覆盖一次, Unmarshal解析的时候, 不会用命令行的参数覆盖 https://github.com/spf13/viper/issues/190
|
||||
cf.Port = cfg.GetInt(fmt.Sprintf("http.port"))
|
||||
cf.Log = cfg.GetBool(fmt.Sprintf("http.log"))
|
||||
|
||||
return NewByConf(cf, opts...)
|
||||
}
|
||||
|
||||
func NewByConf(conf *Conf, opts ...Opt) *Server {
|
||||
s := &Server{
|
||||
serverConf: conf,
|
||||
useDefaultBufferCfg: true,
|
||||
}
|
||||
for _, opt := range opts {
|
||||
opt(s)
|
||||
}
|
||||
|
||||
if s.logger == nil {
|
||||
s.logger = mylog.GetLoggerSkip(-1)
|
||||
}
|
||||
|
||||
if s.reg != nil && s.serviceName == "" {
|
||||
panic("service name is empty")
|
||||
}
|
||||
|
||||
s.ginEngine = gin.New()
|
||||
|
||||
if s.serverConf.Log {
|
||||
//s.unaryMiddlewares = append(s.unaryMiddlewares, s.requestLog())
|
||||
s.ginEngine.Use(s.requestLog())
|
||||
}
|
||||
s.ginEngine.Use(s.httpRecover())
|
||||
|
||||
s.ginEngine.GET("/health", func(ctx *gin.Context) {
|
||||
ctx.String(http.StatusOK, "ok")
|
||||
})
|
||||
|
||||
s.hs = &http.Server{
|
||||
Handler: s.ginEngine,
|
||||
ReadTimeout: time.Second * 5,
|
||||
WriteTimeout: time.Second * 10,
|
||||
IdleTimeout: time.Second * 180,
|
||||
}
|
||||
s.shutdownTimeout = time.Millisecond * 200
|
||||
|
||||
if conf.ReadTimeoutMs > 0 {
|
||||
s.hs.ReadTimeout = time.Millisecond * time.Duration(conf.ReadTimeoutMs)
|
||||
}
|
||||
|
||||
if conf.WriteTimeoutMs > 0 {
|
||||
s.hs.WriteTimeout = time.Millisecond * time.Duration(conf.WriteTimeoutMs)
|
||||
}
|
||||
|
||||
if conf.IdleTimeoutMs > 0 {
|
||||
s.hs.IdleTimeout = time.Millisecond * time.Duration(conf.IdleTimeoutMs)
|
||||
}
|
||||
|
||||
if conf.ShutdownTimeoutMs > 0 {
|
||||
s.shutdownTimeout = time.Millisecond * time.Duration(conf.ShutdownTimeoutMs)
|
||||
}
|
||||
|
||||
return s
|
||||
}
|
||||
|
||||
func (s *Server) Engine() *gin.Engine {
|
||||
return s.ginEngine
|
||||
}
|
||||
|
||||
func (s *Server) initServer() {
|
||||
|
||||
}
|
||||
|
||||
func (s *Server) Run(ctx context.Context) error {
|
||||
s.initServer()
|
||||
|
||||
// 端口如果=0, 监听随机端口
|
||||
addr0 := fmt.Sprintf("%s:%d", s.serverConf.Addr, s.serverConf.Port)
|
||||
lis, err := net.Listen("tcp", addr0)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 获取监听的端口
|
||||
port := lis.Addr().(*net.TCPAddr).Port
|
||||
|
||||
var svcIp = s.serverConf.Ip
|
||||
if svcIp == "" {
|
||||
svcIp = mycommon.GetOutboundIP()
|
||||
}
|
||||
|
||||
// 注册服务
|
||||
if s.reg != nil {
|
||||
s.serviceRegInfo = &myregistry.ServiceInfo{
|
||||
ServiceName: s.serviceName,
|
||||
Ip: svcIp,
|
||||
Port: port,
|
||||
}
|
||||
|
||||
err = s.reg.Register(s.serviceRegInfo)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
addr := fmt.Sprintf("%s:%d", s.serverConf.Addr, port)
|
||||
log.Printf("http server listen on %s", addr)
|
||||
|
||||
s.hs.Addr = addr
|
||||
|
||||
err = s.hs.Serve(lis)
|
||||
if err != nil {
|
||||
log.Printf("start http server err: %s", err)
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Server) Stop() {
|
||||
if s.reg != nil {
|
||||
err := s.reg.Deregister(s.serviceRegInfo)
|
||||
if err != nil {
|
||||
s.logger.Errorf("http server deregister err: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
// 如果使用k8s service, 关闭pod和往service注销ip是同时进行的, 如果退出服务比注销ip先完成, 可能有流量继续进来, 导致请求失败
|
||||
// 延迟一段时间, 确保服务已经注销ip, 再关闭服务
|
||||
|
||||
// 如何使用注册中心, 先从中心退出ip, 也延迟一段时间, 等上游网关更新ip完成(正常不会太久), 不会有流量进来旧服务, 再退出服务
|
||||
if s.delayStopMs > 0 {
|
||||
delayTime := time.Millisecond * time.Duration(s.delayStopMs)
|
||||
log.Printf("http server delay stop: %s", delayTime)
|
||||
time.Sleep(delayTime)
|
||||
}
|
||||
|
||||
tCtx, tCancel := context.WithTimeout(context.Background(), s.shutdownTimeout)
|
||||
defer tCancel()
|
||||
s.hs.Shutdown(tCtx)
|
||||
|
||||
log.Printf("http server stop")
|
||||
}
|
||||
|
||||
func (s *Server) httpRecover() gin.HandlerFunc {
|
||||
return func(ctx *gin.Context) {
|
||||
defer func() {
|
||||
if err := recover(); err != nil {
|
||||
log.Printf("%s - panic: %v\n%s", ctx.Request.RequestURI, err, debug.Stack())
|
||||
|
||||
ctx.JSON(http.StatusOK, gin.H{
|
||||
"code": 1,
|
||||
"message": fmt.Sprintf("server err: %s", err),
|
||||
})
|
||||
|
||||
}
|
||||
}()
|
||||
|
||||
ctx.Next()
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Server) requestLog() gin.HandlerFunc {
|
||||
return func(ctx *gin.Context) {
|
||||
start := time.Now()
|
||||
|
||||
ctx.Next()
|
||||
|
||||
s.logger.Infof(
|
||||
"%s - %d - %s - %s - %s",
|
||||
ctx.Request.Method, ctx.Writer.Status(), time.Since(start),
|
||||
ctx.ClientIP(), ctx.Request.RequestURI,
|
||||
)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user