mirror of
https://gitee.com/johng/gf
synced 2026-06-26 17:35:40 +08:00
改进ghttp包文件命名,增加gbase64示例,ghttp.Server热重启特性开发中
This commit is contained in:
@ -7,6 +7,7 @@
|
||||
package ghttp
|
||||
|
||||
import (
|
||||
"os"
|
||||
"sync"
|
||||
"errors"
|
||||
"strings"
|
||||
@ -31,36 +32,45 @@ const (
|
||||
gDEFAULT_SESSION_ID_NAME = "gfsessionid" // 默认存放Cookie中的SessionId名称
|
||||
)
|
||||
|
||||
// http server结构体
|
||||
// ghttp.Server结构体
|
||||
type Server struct {
|
||||
hmmu sync.RWMutex // handler互斥锁
|
||||
hhmu sync.RWMutex // hooks互斥锁
|
||||
hsmu sync.RWMutex // status handler互斥锁
|
||||
hmcmu sync.RWMutex // handlerCache互斥锁
|
||||
hhcmu sync.RWMutex // hooksCache互斥锁
|
||||
// 基本属性变量
|
||||
name string // 服务名称,方便识别
|
||||
config ServerConfig // 配置对象
|
||||
status int8 // 当前服务器状态(0:未启动,1:运行中)
|
||||
servers []*http.Server // 底层http.Server列表
|
||||
methodsMap map[string]bool // 所有支持的HTTP Method(初始化时自动填充)
|
||||
servedCount *gtype.Int // 已经服务的请求数(4-8字节,不考虑溢出情况),同时作为请求ID
|
||||
closeQueue *gqueue.Queue // 请求结束的关闭队列(存放的是需要异步关闭处理的*Request对象)
|
||||
signalQueue chan os.Signal // 终端命令行监听队列
|
||||
// 服务注册相关
|
||||
hmmu sync.RWMutex // handler互斥锁
|
||||
hmcmu sync.RWMutex // handlerCache互斥锁
|
||||
handlerMap HandlerMap // 所有注册的回调函数(静态匹配)
|
||||
statusHandlerMap map[string]HandlerFunc // 不同状态码下的注册处理方法(例如404状态时的处理方法)
|
||||
handlerTree map[string]interface{} // 所有注册的回调函数(动态匹配,树型+链表优先级匹配)
|
||||
hooksTree map[string]interface{} // 所有注册的事件回调函数(动态匹配,树型+链表优先级匹配)
|
||||
handlerCache *gcache.Cache // 服务注册路由内存缓存
|
||||
// 事件回调注册
|
||||
hhmu sync.RWMutex // hooks互斥锁
|
||||
hhcmu sync.RWMutex // hooksCache互斥锁
|
||||
hooksTree map[string]interface{} // 所有注册的事件回调函数(动态匹配,树型+链表优先级匹配)
|
||||
hooksCache *gcache.Cache // 回调事件注册路由内存缓存
|
||||
servedCount *gtype.Int // 已经服务的请求数(4-8字节,不考虑溢出情况)
|
||||
// 自定义状态码回调
|
||||
hsmu sync.RWMutex // status handler互斥锁
|
||||
statusHandlerMap map[string]HandlerFunc // 不同状态码下的注册处理方法(例如404状态时的处理方法)
|
||||
// COOKIE
|
||||
cookieMaxAge *gtype.Int // Cookie有效期
|
||||
cookies *gmap.IntInterfaceMap // 当前服务器正在服务(请求正在执行)的Cookie(每个请求一个Cookie对象)
|
||||
// SESSION
|
||||
sessionMaxAge *gtype.Int // Session有效期
|
||||
sessionIdName *gtype.String // SessionId名称
|
||||
cookies *gmap.IntInterfaceMap // 当前服务器正在服务(请求正在执行)的Cookie(每个请求一个Cookie对象)
|
||||
sessions *gcache.Cache // Session内存缓存
|
||||
closeQueue *gqueue.Queue // 请求结束的关闭队列(存放的是需要异步关闭处理的*Request对象)
|
||||
// 日志相关属性
|
||||
logPath *gtype.String // 存放日志的目录路径
|
||||
logHandler *gtype.Interface // 自定义日志处理回调方法
|
||||
errorLogEnabled *gtype.Bool // 是否开启error log
|
||||
accessLogEnabled *gtype.Bool // 是否开启access log
|
||||
accessLogger *glog.Logger // access log日志对象
|
||||
errorLogger *glog.Logger // error log日志对象
|
||||
logHandler *gtype.Interface // 自定义的日志处理回调方法
|
||||
}
|
||||
|
||||
// 域名、URI与回调函数的绑定记录表
|
||||
@ -100,6 +110,7 @@ func GetServer(name...interface{}) (*Server) {
|
||||
}
|
||||
s := &Server {
|
||||
name : sname,
|
||||
servers : make([]*http.Server, 0),
|
||||
methodsMap : make(map[string]bool),
|
||||
handlerMap : make(HandlerMap),
|
||||
statusHandlerMap : make(map[string]HandlerFunc),
|
||||
@ -114,6 +125,7 @@ func GetServer(name...interface{}) (*Server) {
|
||||
sessionIdName : gtype.NewString(gDEFAULT_SESSION_ID_NAME),
|
||||
servedCount : gtype.NewInt(),
|
||||
closeQueue : gqueue.New(),
|
||||
signalQueue : make(chan os.Signal),
|
||||
logPath : gtype.NewString(),
|
||||
accessLogEnabled : gtype.NewBool(),
|
||||
errorLogEnabled : gtype.NewBool(true),
|
||||
@ -145,7 +157,7 @@ func (s *Server) Run() error {
|
||||
s.config.Handler = http.HandlerFunc(s.defaultHttpHandle)
|
||||
}
|
||||
|
||||
// 开启异步处理队列处理循环
|
||||
// 开启异步关闭队列处理循环
|
||||
s.startCloseQueueLoop()
|
||||
|
||||
// 开始执行底层Web Server创建,端口监听
|
||||
@ -163,9 +175,12 @@ func (s *Server) Run() error {
|
||||
for _, v := range array {
|
||||
wg.Add(1)
|
||||
go func(addr string) {
|
||||
if err := s.newServer(addr).ListenAndServeTLS(s.config.HTTPSCertPath, s.config.HTTPSKeyPath); err != nil {
|
||||
server := s.newGracefulServer(addr)
|
||||
if err := server.ListenAndServeTLS(s.config.HTTPSCertPath, s.config.HTTPSKeyPath); err != nil {
|
||||
glog.Error(err)
|
||||
wg.Done()
|
||||
} else {
|
||||
//s.servers = append(s.servers, server)
|
||||
}
|
||||
}(v)
|
||||
}
|
||||
@ -178,9 +193,12 @@ func (s *Server) Run() error {
|
||||
for _, v := range array {
|
||||
wg.Add(1)
|
||||
go func(addr string) {
|
||||
if err := s.newServer(addr).ListenAndServe(); err != nil {
|
||||
server := s.newGracefulServer(addr)
|
||||
if err := server.ListenAndServe(); err != nil {
|
||||
glog.Error(err)
|
||||
wg.Done()
|
||||
} else {
|
||||
//s.servers = append(s.servers, server)
|
||||
}
|
||||
}(v)
|
||||
}
|
||||
189
g/net/ghttp/ghttp_server_graceful.go
Normal file
189
g/net/ghttp/ghttp_server_graceful.go
Normal file
@ -0,0 +1,189 @@
|
||||
// Copyright 2018 gf Author(https://gitee.com/johng/gf). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://gitee.com/johng/gf.
|
||||
|
||||
package ghttp
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"os"
|
||||
"syscall"
|
||||
"context"
|
||||
"net/http"
|
||||
"os/signal"
|
||||
"crypto/tls"
|
||||
"gitee.com/johng/gf/g/os/glog"
|
||||
)
|
||||
|
||||
const (
|
||||
gGF_WEB_SERVER_GRACEFUL_ENVIRON_KEY = "GF_WEB_SERVER_GRACEFUL"
|
||||
gGF_WEB_SERVER_GRACEFUL_ENVIRON_STRING = gGF_WEB_SERVER_GRACEFUL_ENVIRON_KEY + "=1"
|
||||
)
|
||||
|
||||
// 优雅的Web Server对象封装
|
||||
type gracefulServer struct {
|
||||
httpServer *http.Server
|
||||
listener net.Listener
|
||||
isGraceful bool
|
||||
signalChan chan os.Signal
|
||||
shutdownChan chan bool
|
||||
}
|
||||
|
||||
// 创建一个优雅的Http Server
|
||||
func (s *Server) newGracefulServer(addr string) *gracefulServer {
|
||||
isGraceful := false
|
||||
if os.Getenv(gGF_WEB_SERVER_GRACEFUL_ENVIRON_KEY) != "" {
|
||||
isGraceful = true
|
||||
}
|
||||
return &gracefulServer {
|
||||
httpServer : s.newServer(addr),
|
||||
isGraceful : isGraceful,
|
||||
signalChan : make(chan os.Signal),
|
||||
shutdownChan : make(chan bool),
|
||||
}
|
||||
}
|
||||
|
||||
// 执行HTTP监听
|
||||
func (s *gracefulServer) ListenAndServe() error {
|
||||
addr := s.httpServer.Addr
|
||||
ln, err := s.getNetListener(addr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
s.listener = ln
|
||||
return s.doServe()
|
||||
}
|
||||
|
||||
// 执行HTTPS监听
|
||||
func (s *gracefulServer) ListenAndServeTLS(certFile, keyFile string) error {
|
||||
addr := s.httpServer.Addr
|
||||
config := &tls.Config{}
|
||||
if s.httpServer.TLSConfig != nil {
|
||||
*config = *s.httpServer.TLSConfig
|
||||
}
|
||||
if config.NextProtos == nil {
|
||||
config.NextProtos = []string{"http/1.1"}
|
||||
}
|
||||
var err error
|
||||
config.Certificates = make([]tls.Certificate, 1)
|
||||
config.Certificates[0], err = tls.LoadX509KeyPair(certFile, keyFile)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
ln, err := s.getNetListener(addr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
s.listener = tls.NewListener(ln, config)
|
||||
return s.doServe()
|
||||
}
|
||||
|
||||
// 开始执行Web Server服务处理
|
||||
func (s *gracefulServer) doServe() error {
|
||||
go s.handleSignals()
|
||||
err := s.httpServer.Serve(s.listener)
|
||||
<-s.shutdownChan
|
||||
return err
|
||||
}
|
||||
|
||||
// 自定义的net.Listener
|
||||
func (s *gracefulServer) getNetListener(addr string) (net.Listener, error) {
|
||||
var ln net.Listener
|
||||
var err error
|
||||
if s.isGraceful {
|
||||
//path := fmt.Sprintf("%s%sgf.web.server.fd.%d", gfile.TempDir(), gfile.Separator,gtime.Nanosecond())
|
||||
//f, err := gfile.Open(path)
|
||||
//if err != nil {
|
||||
// return nil, err
|
||||
//}
|
||||
f := os.NewFile(3, "")
|
||||
ln, err = net.FileListener(f)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("net.FileListener error: %v", err)
|
||||
return nil, err
|
||||
}
|
||||
} else {
|
||||
ln, err = net.Listen("tcp", addr)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("net.Listen error: %v", err)
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return ln, nil
|
||||
}
|
||||
|
||||
// 处理终端信号指令监听
|
||||
func (s *gracefulServer) handleSignals() {
|
||||
var sig os.Signal
|
||||
|
||||
signal.Notify(
|
||||
s.signalChan,
|
||||
syscall.SIGTERM,
|
||||
syscall.SIGUSR2,
|
||||
)
|
||||
|
||||
for {
|
||||
sig = <-s.signalChan
|
||||
switch sig {
|
||||
case syscall.SIGTERM:
|
||||
glog.Println("received SIGTERM, graceful shutting down HTTP server")
|
||||
s.shutdown()
|
||||
|
||||
case syscall.SIGUSR2:
|
||||
glog.Println("received SIGUSR2, graceful restarting HTTP server")
|
||||
if pid, err := s.startNewProcess(); err != nil {
|
||||
glog.Printf("start new process failed: %v, continue serving\n", err)
|
||||
} else {
|
||||
glog.Printf("start new process successfully, the new pid is %d\n", pid)
|
||||
s.shutdown()
|
||||
}
|
||||
default:
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 执行请求优雅关闭
|
||||
func (s *gracefulServer) shutdown() {
|
||||
if err := s.httpServer.Shutdown(context.Background()); err != nil {
|
||||
glog.Errorf("server shutdown error: %v\n", err)
|
||||
} else {
|
||||
glog.Println("server shutdown success")
|
||||
s.shutdownChan <- true
|
||||
}
|
||||
}
|
||||
|
||||
// 创建子进程来监听并处理新的HTTP请求,与父进程使用的是同一个socket文件描述符
|
||||
func (s *gracefulServer) startNewProcess() (uintptr, error) {
|
||||
listenerFd, err := s.getTCPListenerFd()
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("failed to get socket file descriptor: %v", err)
|
||||
}
|
||||
// 构造子进程的环境变量,并增加环境变量参数以标识该进程是graceful子进程
|
||||
envs := []string{}
|
||||
for _, value := range os.Environ() {
|
||||
if value != gGF_WEB_SERVER_GRACEFUL_ENVIRON_STRING {
|
||||
envs = append(envs, value)
|
||||
}
|
||||
}
|
||||
envs = append(envs, gGF_WEB_SERVER_GRACEFUL_ENVIRON_STRING)
|
||||
fork, err := syscall.ForkExec(os.Args[0], os.Args, &syscall.ProcAttr {
|
||||
Env : os.Environ(),
|
||||
Files : []uintptr{os.Stdin.Fd(), os.Stdout.Fd(), os.Stderr.Fd(), listenerFd},
|
||||
})
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("failed to forkexec: %v", err)
|
||||
}
|
||||
return uintptr(fork), nil
|
||||
}
|
||||
|
||||
// 获得对应net.TCPListener的文件描述符文件ID
|
||||
func (s *gracefulServer) getTCPListenerFd() (uintptr, error) {
|
||||
file, err := s.listener.(*net.TCPListener).File()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return file.Fd(), nil
|
||||
}
|
||||
@ -462,4 +462,9 @@ func MainPkgPath() string {
|
||||
return p
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// 系统临时目录
|
||||
func TempDir() string {
|
||||
return os.TempDir()
|
||||
}
|
||||
18
geg/encoding/gbase64.go
Normal file
18
geg/encoding/gbase64.go
Normal file
@ -0,0 +1,18 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"gitee.com/johng/gf/g/encoding/gbase64"
|
||||
)
|
||||
|
||||
func main() {
|
||||
s := "john"
|
||||
b := gbase64.Encode(s)
|
||||
c, e := gbase64.Decode(b)
|
||||
fmt.Println(b)
|
||||
fmt.Println(c)
|
||||
fmt.Println(e)
|
||||
}
|
||||
|
||||
|
||||
|
||||
19
geg/other/graceful.go
Normal file
19
geg/other/graceful.go
Normal file
@ -0,0 +1,19 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"github.com/tabalt/gracehttp"
|
||||
)
|
||||
|
||||
func main() {
|
||||
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
|
||||
fmt.Fprintf(w, "hello world")
|
||||
})
|
||||
|
||||
err := gracehttp.ListenAndServe(":8888", nil)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
}
|
||||
@ -2,8 +2,18 @@ package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"github.com/tabalt/gracehttp"
|
||||
)
|
||||
|
||||
func main() {
|
||||
fmt.Println("我要加入gf框架开发团队!!")
|
||||
}
|
||||
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
|
||||
fmt.Fprintf(w, "hello world")
|
||||
})
|
||||
|
||||
err := gracehttp.ListenAndServe(":8888", nil)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user