2017-12-29 16:03:30 +08:00
|
|
|
|
// Copyright 2017 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.
|
|
|
|
|
|
2017-11-23 10:21:28 +08:00
|
|
|
|
package ghttp
|
|
|
|
|
|
|
|
|
|
import (
|
2018-05-06 22:35:08 +08:00
|
|
|
|
"os"
|
2017-12-07 14:57:16 +08:00
|
|
|
|
"sync"
|
|
|
|
|
"errors"
|
2018-04-10 10:32:37 +08:00
|
|
|
|
"strings"
|
2017-12-12 17:25:50 +08:00
|
|
|
|
"reflect"
|
2018-05-13 14:04:37 +08:00
|
|
|
|
"runtime"
|
2017-12-26 11:46:48 +08:00
|
|
|
|
"net/http"
|
2018-04-19 13:41:06 +08:00
|
|
|
|
"gitee.com/johng/gf/g/os/glog"
|
2018-05-11 12:57:05 +08:00
|
|
|
|
"gitee.com/johng/gf/g/os/gproc"
|
2018-04-11 16:06:45 +08:00
|
|
|
|
"gitee.com/johng/gf/g/os/gcache"
|
2018-04-20 23:23:42 +08:00
|
|
|
|
"gitee.com/johng/gf/g/util/gconv"
|
2018-01-02 15:52:32 +08:00
|
|
|
|
"gitee.com/johng/gf/g/container/gmap"
|
2018-04-09 17:55:46 +08:00
|
|
|
|
"gitee.com/johng/gf/g/container/gtype"
|
2018-04-11 16:06:45 +08:00
|
|
|
|
"gitee.com/johng/gf/g/container/gqueue"
|
2018-05-31 12:07:31 +08:00
|
|
|
|
"gitee.com/johng/gf/g/os/gspath"
|
2018-06-01 00:11:45 +08:00
|
|
|
|
"gitee.com/johng/gf/g/os/genv"
|
2018-10-22 11:13:00 +08:00
|
|
|
|
"gitee.com/johng/gf/third/github.com/gorilla/websocket"
|
2018-06-29 17:26:34 +08:00
|
|
|
|
"gitee.com/johng/gf/g/os/gtime"
|
|
|
|
|
"time"
|
2018-08-16 18:17:47 +08:00
|
|
|
|
"gitee.com/johng/gf/g/os/gfile"
|
2018-10-17 16:56:50 +08:00
|
|
|
|
"gitee.com/johng/gf/g/util/gregex"
|
|
|
|
|
"gitee.com/johng/gf/g/container/garray"
|
2018-10-22 11:13:00 +08:00
|
|
|
|
"gitee.com/johng/gf/third/github.com/olekukonko/tablewriter"
|
2018-10-17 16:56:50 +08:00
|
|
|
|
"bytes"
|
2018-10-17 10:09:42 +08:00
|
|
|
|
"fmt"
|
2017-11-23 10:21:28 +08:00
|
|
|
|
)
|
|
|
|
|
|
2018-08-01 13:04:15 +08:00
|
|
|
|
const (
|
|
|
|
|
SERVER_STATUS_STOPPED = 0 // Server状态:停止
|
|
|
|
|
SERVER_STATUS_RUNNING = 1 // Server状态:运行
|
2018-08-16 18:17:47 +08:00
|
|
|
|
HOOK_BEFORE_SERVE = "BeforeServe"
|
|
|
|
|
HOOK_AFTER_SERVE = "AfterServe"
|
|
|
|
|
HOOK_BEFORE_OUTPUT = "BeforeOutput"
|
|
|
|
|
HOOK_AFTER_OUTPUT = "AfterOutput"
|
|
|
|
|
HOOK_BEFORE_CLOSE = "BeforeClose"
|
|
|
|
|
HOOK_AFTER_CLOSE = "AfterClose"
|
2018-08-01 13:04:15 +08:00
|
|
|
|
)
|
2017-12-11 17:16:59 +08:00
|
|
|
|
const (
|
2018-05-08 23:38:09 +08:00
|
|
|
|
gHTTP_METHODS = "GET,PUT,POST,DELETE,PATCH,HEAD,CONNECT,OPTIONS,TRACE"
|
|
|
|
|
gDEFAULT_SERVER = "default"
|
|
|
|
|
gDEFAULT_DOMAIN = "default"
|
|
|
|
|
gDEFAULT_METHOD = "ALL"
|
2018-08-19 11:25:15 +08:00
|
|
|
|
gROUTE_REGISTER_HANDLER = 1
|
|
|
|
|
gROUTE_REGISTER_OBJECT = 2
|
|
|
|
|
gROUTE_REGISTER_CONTROLLER = 3
|
2017-12-11 17:16:59 +08:00
|
|
|
|
)
|
|
|
|
|
|
2018-05-06 22:35:08 +08:00
|
|
|
|
// ghttp.Server结构体
|
2017-12-07 14:57:16 +08:00
|
|
|
|
type Server struct {
|
2018-05-06 22:35:08 +08:00
|
|
|
|
// 基本属性变量
|
2018-10-17 10:09:42 +08:00
|
|
|
|
name string // 服务名称,方便识别
|
|
|
|
|
paths *gspath.SPath // 静态文件检索对象(类似nginx tryfile功能)
|
|
|
|
|
config ServerConfig // 配置对象
|
|
|
|
|
servers []*gracefulServer // 底层http.Server列表
|
|
|
|
|
methodsMap map[string]struct{} // 所有支持的HTTP Method(初始化时自动填充)
|
|
|
|
|
servedCount *gtype.Int // 已经服务的请求数(4-8字节,不考虑溢出情况),同时作为请求ID
|
|
|
|
|
closeQueue *gqueue.Queue // 请求结束的关闭队列(存放的是需要异步关闭处理的*Request对象)
|
2018-05-06 22:35:08 +08:00
|
|
|
|
// 服务注册相关
|
2018-10-17 10:09:42 +08:00
|
|
|
|
serveTree map[string]interface{} // 所有注册的服务回调函数(路由表,树型结构,哈希表+链表优先级匹配)
|
|
|
|
|
hooksTree map[string]interface{} // 所有注册的事件回调函数(路由表,树型结构,哈希表+链表优先级匹配)
|
|
|
|
|
serveCache *gcache.Cache // 服务注册路由内存缓存
|
|
|
|
|
hooksCache *gcache.Cache // 事件回调路由内存缓存
|
|
|
|
|
routesMap map[string]registeredRouteItem // 已经注册的路由及对应的注册方法文件地址(用以路由重复注册判断)
|
2018-05-06 22:35:08 +08:00
|
|
|
|
// 自定义状态码回调
|
2018-10-17 10:09:42 +08:00
|
|
|
|
hsmu sync.RWMutex // status handler互斥锁
|
|
|
|
|
statusHandlerMap map[string]HandlerFunc // 不同状态码下的注册处理方法(例如404状态时的处理方法)
|
2018-05-06 22:35:08 +08:00
|
|
|
|
// SESSION
|
2018-10-17 10:09:42 +08:00
|
|
|
|
sessions *gcache.Cache // Session内存缓存
|
2018-09-14 13:02:13 +08:00
|
|
|
|
// Logger
|
2018-10-17 10:09:42 +08:00
|
|
|
|
logger *glog.Logger // 日志管理对象
|
2017-12-07 14:57:16 +08:00
|
|
|
|
}
|
|
|
|
|
|
2018-04-22 22:17:20 +08:00
|
|
|
|
// 路由对象
|
|
|
|
|
type Router struct {
|
|
|
|
|
Uri string // 注册时的pattern - uri
|
|
|
|
|
Method string // 注册时的pattern - method
|
|
|
|
|
Domain string // 注册时的pattern - domain
|
2018-07-26 10:26:34 +08:00
|
|
|
|
RegRule string // 路由规则解析后对应的正则表达式
|
|
|
|
|
RegNames []string // 路由规则解析后对应的变量名称数组
|
2018-04-22 22:17:20 +08:00
|
|
|
|
Priority int // 优先级,用于链表排序,值越大优先级越高
|
|
|
|
|
}
|
|
|
|
|
|
2018-08-02 15:23:14 +08:00
|
|
|
|
// pattern与回调函数的绑定map
|
2018-07-29 22:01:29 +08:00
|
|
|
|
type handlerMap map[string]*handlerItem
|
|
|
|
|
|
2017-12-14 17:32:51 +08:00
|
|
|
|
// http回调函数注册信息
|
2018-07-29 22:01:29 +08:00
|
|
|
|
type handlerItem struct {
|
2018-10-17 10:09:42 +08:00
|
|
|
|
name string // 注册的方法名称信息
|
|
|
|
|
rtype int // 注册方式(执行对象/回调函数/控制器)
|
2018-07-29 22:01:29 +08:00
|
|
|
|
ctype reflect.Type // 控制器类型(反射类型)
|
2018-04-12 14:09:33 +08:00
|
|
|
|
fname string // 回调方法名称
|
|
|
|
|
faddr HandlerFunc // 准确的执行方法内存地址(与以上两个参数二选一)
|
2018-08-17 19:01:49 +08:00
|
|
|
|
finit HandlerFunc // 初始化请求回调方法(执行对象注册方式下有效)
|
|
|
|
|
fshut HandlerFunc // 完成请求回调方法(执行对象注册方式下有效)
|
2018-08-02 15:23:14 +08:00
|
|
|
|
router *Router // 注册时绑定的路由对象
|
2018-07-29 22:01:29 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 根据特定URL.Path解析后的路由检索结果项
|
|
|
|
|
type handlerParsedItem struct {
|
2018-08-03 15:22:31 +08:00
|
|
|
|
handler *handlerItem // 路由注册项
|
2018-08-02 15:23:14 +08:00
|
|
|
|
values map[string][]string // 特定URL.Path的Router解析参数
|
2018-07-29 22:01:29 +08:00
|
|
|
|
}
|
|
|
|
|
|
2018-10-17 10:09:42 +08:00
|
|
|
|
// 已注册的路由项
|
|
|
|
|
type registeredRouteItem struct {
|
|
|
|
|
file string // 文件路径及行数地址
|
|
|
|
|
handler *handlerItem // 路由注册项
|
|
|
|
|
}
|
|
|
|
|
|
2018-06-02 23:47:16 +08:00
|
|
|
|
// HTTP注册函数
|
2018-05-04 16:50:54 +08:00
|
|
|
|
type HandlerFunc func(r *Request)
|
2017-12-26 10:13:49 +08:00
|
|
|
|
|
2018-05-10 23:52:09 +08:00
|
|
|
|
// 文件描述符map
|
|
|
|
|
type listenerFdMap map[string]string
|
|
|
|
|
|
2018-07-29 22:01:29 +08:00
|
|
|
|
|
2017-12-08 12:03:21 +08:00
|
|
|
|
// Server表,用以存储和检索名称与Server对象之间的关联关系
|
2018-07-31 22:55:54 +08:00
|
|
|
|
var serverMapping = gmap.NewStringInterfaceMap()
|
2017-12-07 17:34:51 +08:00
|
|
|
|
|
2018-08-01 13:04:15 +08:00
|
|
|
|
// 正常运行的Server数量,如果没有运行、失败或者全部退出,那么该值为0
|
|
|
|
|
var serverRunning = gtype.NewInt()
|
|
|
|
|
|
2018-06-02 23:47:16 +08:00
|
|
|
|
// Web Socket默认配置
|
2018-07-31 22:55:54 +08:00
|
|
|
|
var wsUpgrader = websocket.Upgrader{}
|
2018-05-10 23:52:09 +08:00
|
|
|
|
|
2018-05-13 14:04:37 +08:00
|
|
|
|
// Web Server已完成服务事件通道,当有事件时表示服务完成,当前进程退出
|
2018-07-31 22:55:54 +08:00
|
|
|
|
var doneChan = make(chan struct{}, 1000)
|
2018-05-10 23:52:09 +08:00
|
|
|
|
|
2018-07-31 22:55:54 +08:00
|
|
|
|
// 用于服务进程初始化,只能初始化一次,采用“懒初始化”(在server运行时才初始化)
|
|
|
|
|
var serverProcInited = gtype.NewBool()
|
|
|
|
|
|
|
|
|
|
// Web Server进程初始化.
|
|
|
|
|
// 注意该方法不能放置于包初始化方法init中,不使用ghttp.Server的功能便不能初始化对应的协程goroutine逻辑.
|
|
|
|
|
func serverProcInit() {
|
|
|
|
|
if serverProcInited.Val() {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
serverProcInited.Set(true)
|
2018-06-01 18:19:54 +08:00
|
|
|
|
// 如果是完整重启,那么需要等待主进程销毁后,才开始执行监听,防止端口冲突
|
|
|
|
|
if genv.Get(gADMIN_ACTION_RESTART_ENVKEY) != "" {
|
|
|
|
|
if p, e := os.FindProcess(gproc.PPid()); e == nil {
|
|
|
|
|
p.Kill()
|
|
|
|
|
p.Wait()
|
|
|
|
|
} else {
|
|
|
|
|
glog.Error(e)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-06-01 00:11:45 +08:00
|
|
|
|
// 信号量管理操作监听
|
|
|
|
|
go handleProcessSignal()
|
2018-06-29 17:26:34 +08:00
|
|
|
|
// 异步监听进程间消息
|
|
|
|
|
go handleProcessMessage()
|
2018-05-10 23:52:09 +08:00
|
|
|
|
}
|
|
|
|
|
|
2017-12-18 10:42:59 +08:00
|
|
|
|
// 获取/创建一个默认配置的HTTP Server(默认监听端口是80)
|
|
|
|
|
// 单例模式,请保证name的唯一性
|
2018-04-20 23:23:42 +08:00
|
|
|
|
func GetServer(name...interface{}) (*Server) {
|
|
|
|
|
sname := gDEFAULT_SERVER
|
|
|
|
|
if len(name) > 0 {
|
|
|
|
|
sname = gconv.String(name[0])
|
2017-12-30 18:35:24 +08:00
|
|
|
|
}
|
2018-04-20 23:23:42 +08:00
|
|
|
|
if s := serverMapping.Get(sname); s != nil {
|
2017-12-08 12:03:21 +08:00
|
|
|
|
return s.(*Server)
|
2017-12-07 17:34:51 +08:00
|
|
|
|
}
|
2018-04-11 16:06:45 +08:00
|
|
|
|
s := &Server {
|
2018-04-20 23:23:42 +08:00
|
|
|
|
name : sname,
|
2018-05-31 12:07:31 +08:00
|
|
|
|
paths : gspath.New(),
|
2018-05-08 18:41:29 +08:00
|
|
|
|
servers : make([]*gracefulServer, 0),
|
2018-08-22 19:56:01 +08:00
|
|
|
|
methodsMap : make(map[string]struct{}),
|
2018-05-04 14:35:20 +08:00
|
|
|
|
statusHandlerMap : make(map[string]HandlerFunc),
|
2018-08-02 15:23:14 +08:00
|
|
|
|
serveTree : make(map[string]interface{}),
|
|
|
|
|
hooksTree : make(map[string]interface{}),
|
|
|
|
|
serveCache : gcache.New(),
|
2018-04-19 19:11:10 +08:00
|
|
|
|
hooksCache : gcache.New(),
|
2018-10-17 10:09:42 +08:00
|
|
|
|
routesMap : make(map[string]registeredRouteItem),
|
2018-04-19 19:11:10 +08:00
|
|
|
|
sessions : gcache.New(),
|
|
|
|
|
servedCount : gtype.NewInt(),
|
|
|
|
|
closeQueue : gqueue.New(),
|
2018-09-14 13:02:13 +08:00
|
|
|
|
logger : glog.New(),
|
2017-12-30 17:09:00 +08:00
|
|
|
|
}
|
2018-10-10 18:39:57 +08:00
|
|
|
|
// 日志的标准输出默认关闭,但是错误信息会特殊处理
|
2018-09-18 21:29:31 +08:00
|
|
|
|
s.logger.SetStdPrint(false)
|
2017-12-26 11:46:48 +08:00
|
|
|
|
for _, v := range strings.Split(gHTTP_METHODS, ",") {
|
2018-08-22 19:56:01 +08:00
|
|
|
|
s.methodsMap[v] = struct{}{}
|
2017-12-26 11:46:48 +08:00
|
|
|
|
}
|
2018-08-12 12:22:02 +08:00
|
|
|
|
// 初始化时使用默认配置
|
2017-12-08 12:03:21 +08:00
|
|
|
|
s.SetConfig(defaultServerConfig)
|
2018-08-12 12:22:02 +08:00
|
|
|
|
// 记录到全局ServerMap中
|
2018-04-20 23:23:42 +08:00
|
|
|
|
serverMapping.Set(sname, s)
|
2017-12-08 12:03:21 +08:00
|
|
|
|
return s
|
2017-12-07 14:57:16 +08:00
|
|
|
|
}
|
|
|
|
|
|
2018-05-13 14:04:37 +08:00
|
|
|
|
// 作为守护协程异步执行(当同一进程中存在多个Web Server时,需要采用这种方式执行)
|
|
|
|
|
// 需要结合Wait方式一起使用
|
|
|
|
|
func (s *Server) Start() error {
|
2018-08-16 18:17:47 +08:00
|
|
|
|
// 服务进程初始化,只会初始化一次
|
2018-07-31 22:55:54 +08:00
|
|
|
|
serverProcInit()
|
|
|
|
|
|
2018-08-16 18:17:47 +08:00
|
|
|
|
// 当前Web Server状态判断
|
|
|
|
|
if s.Status() == SERVER_STATUS_RUNNING {
|
|
|
|
|
return errors.New("server is already running")
|
|
|
|
|
}
|
|
|
|
|
|
2018-08-26 19:42:30 +08:00
|
|
|
|
// 如果设置了静态文件目录,那么优先按照静态文件目录进行检索,其次是当前可执行文件工作目录;
|
|
|
|
|
// 并且如果是开发环境,默认也会添加main包的源码目录路径做为二级检索。
|
2018-05-31 12:07:31 +08:00
|
|
|
|
if s.config.ServerRoot != "" {
|
|
|
|
|
s.paths.Set(s.config.ServerRoot)
|
2018-08-26 19:42:30 +08:00
|
|
|
|
}
|
|
|
|
|
s.paths.Add(gfile.SelfDir())
|
2018-10-18 13:43:00 +08:00
|
|
|
|
if p := gfile.MainPkgPath(); p != "" && gfile.Exists(p) {
|
2018-08-26 19:42:30 +08:00
|
|
|
|
s.paths.Add(p)
|
2018-05-31 12:07:31 +08:00
|
|
|
|
}
|
|
|
|
|
|
2017-11-23 10:21:28 +08:00
|
|
|
|
// 底层http server配置
|
|
|
|
|
if s.config.Handler == nil {
|
|
|
|
|
s.config.Handler = http.HandlerFunc(s.defaultHttpHandle)
|
|
|
|
|
}
|
2018-08-16 18:17:47 +08:00
|
|
|
|
// 不允许访问的路由注册
|
|
|
|
|
if s.config.DenyRoutes != nil {
|
|
|
|
|
for _, v := range s.config.DenyRoutes {
|
|
|
|
|
s.BindHookHandler(v, HOOK_BEFORE_SERVE, func(r *Request) {
|
|
|
|
|
r.Response.WriteStatus(403)
|
|
|
|
|
r.Exit()
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
}
|
2018-08-22 19:56:01 +08:00
|
|
|
|
// gzip压缩文件类型
|
2018-09-14 13:02:13 +08:00
|
|
|
|
//if s.config.GzipContentTypes != nil {
|
|
|
|
|
// for _, v := range s.config.GzipContentTypes {
|
|
|
|
|
// s.gzipMimesMap[v] = struct{}{}
|
|
|
|
|
// }
|
|
|
|
|
//}
|
2018-06-01 00:11:45 +08:00
|
|
|
|
|
|
|
|
|
// 启动http server
|
2018-06-01 18:19:54 +08:00
|
|
|
|
reloaded := false
|
2018-06-01 00:11:45 +08:00
|
|
|
|
fdMapStr := genv.Get(gADMIN_ACTION_RELOAD_ENVKEY)
|
|
|
|
|
if len(fdMapStr) > 0 {
|
|
|
|
|
sfm := bufferToServerFdMap([]byte(fdMapStr))
|
2018-06-01 18:19:54 +08:00
|
|
|
|
if v, ok := sfm[s.name]; ok {
|
|
|
|
|
s.startServer(v)
|
|
|
|
|
reloaded = true
|
2018-06-01 00:11:45 +08:00
|
|
|
|
}
|
2018-06-01 18:19:54 +08:00
|
|
|
|
}
|
|
|
|
|
if !reloaded {
|
|
|
|
|
s.startServer(nil)
|
2018-06-01 00:11:45 +08:00
|
|
|
|
}
|
|
|
|
|
|
2018-06-29 17:26:34 +08:00
|
|
|
|
// 如果是子进程,那么服务开启后通知父进程销毁
|
|
|
|
|
if gproc.IsChild() {
|
|
|
|
|
gtime.SetTimeout(2*time.Second, func() {
|
2018-07-04 19:32:51 +08:00
|
|
|
|
gproc.Send(gproc.PPid(), []byte("exit"), gADMIN_GPROC_COMM_GROUP)
|
2018-06-29 17:26:34 +08:00
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
2018-05-06 22:35:08 +08:00
|
|
|
|
// 开启异步关闭队列处理循环
|
2018-03-13 14:41:49 +08:00
|
|
|
|
s.startCloseQueueLoop()
|
2018-10-17 10:09:42 +08:00
|
|
|
|
|
|
|
|
|
// 打印展示路由表
|
|
|
|
|
s.DumpRoutesMap()
|
2018-05-13 14:04:37 +08:00
|
|
|
|
return nil
|
|
|
|
|
}
|
2018-04-27 22:12:11 +08:00
|
|
|
|
|
2018-10-17 10:09:42 +08:00
|
|
|
|
// 打印展示路由表
|
|
|
|
|
func (s *Server) DumpRoutesMap() {
|
2018-10-18 13:43:00 +08:00
|
|
|
|
if s.config.DumpRouteMap {
|
|
|
|
|
// (等待一定时间后)当所有框架初始化信息打印完毕之后才打印路由表信息
|
|
|
|
|
gtime.SetTimeout(50*time.Millisecond, func() {
|
|
|
|
|
glog.Header(false).Println(fmt.Sprintf("\n%s\n", s.GetRouteMap()))
|
|
|
|
|
})
|
|
|
|
|
}
|
2018-10-17 16:56:50 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 获得路由表(格式化字符串)
|
2018-10-18 13:43:00 +08:00
|
|
|
|
func (s *Server) GetRouteMap() string {
|
2018-10-17 16:56:50 +08:00
|
|
|
|
type tableItem struct {
|
|
|
|
|
hook string
|
|
|
|
|
domain string
|
|
|
|
|
method string
|
|
|
|
|
route string
|
|
|
|
|
handler string
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
buf := bytes.NewBuffer(nil)
|
|
|
|
|
table := tablewriter.NewWriter(buf)
|
2018-10-18 13:43:00 +08:00
|
|
|
|
table.SetHeader([]string{"SERVER", "ADDRESS", "DOMAIN", "METHOD", "ROUTE", "HANDLER", "HOOK"})
|
2018-10-17 16:56:50 +08:00
|
|
|
|
table.SetRowLine(true)
|
|
|
|
|
table.SetBorder(false)
|
|
|
|
|
table.SetCenterSeparator("|")
|
|
|
|
|
|
|
|
|
|
m := make(map[string]*garray.SortedArray)
|
|
|
|
|
for k, v := range s.routesMap {
|
|
|
|
|
array, _ := gregex.MatchString(`(.*?)%([A-Z]+):(.+)@(.+)`, k)
|
|
|
|
|
item := &tableItem{
|
|
|
|
|
hook : array[1],
|
|
|
|
|
domain : array[4],
|
|
|
|
|
method : array[2],
|
|
|
|
|
route : array[3],
|
|
|
|
|
handler : v.handler.name,
|
|
|
|
|
}
|
|
|
|
|
if _, ok := m[item.domain]; !ok {
|
|
|
|
|
m[item.domain] = garray.NewSortedArray(100, func(v1, v2 interface{}) int {
|
|
|
|
|
item1 := v1.(*tableItem)
|
|
|
|
|
item2 := v2.(*tableItem)
|
|
|
|
|
r := 0
|
|
|
|
|
if r = strings.Compare(item1.domain, item2.domain); r == 0 {
|
|
|
|
|
if r = strings.Compare(item1.route, item2.route); r == 0 {
|
|
|
|
|
if r = strings.Compare(item1.method, item2.method); r == 0 {
|
|
|
|
|
r = strings.Compare(item1.hook, item2.hook)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return r
|
|
|
|
|
}, false)
|
|
|
|
|
}
|
|
|
|
|
m[item.domain].Add(item)
|
2018-10-17 10:09:42 +08:00
|
|
|
|
}
|
2018-10-17 16:56:50 +08:00
|
|
|
|
for _, a := range m {
|
2018-10-18 13:43:00 +08:00
|
|
|
|
data := make([]string, 7)
|
2018-10-17 16:56:50 +08:00
|
|
|
|
for _, v := range a.Slice() {
|
|
|
|
|
item := v.(*tableItem)
|
2018-10-18 13:43:00 +08:00
|
|
|
|
data[0] = s.name
|
|
|
|
|
data[1] = s.config.Addr
|
|
|
|
|
data[2] = item.domain
|
|
|
|
|
data[3] = item.method
|
|
|
|
|
data[4] = item.route
|
|
|
|
|
data[5] = item.handler
|
|
|
|
|
data[6] = item.hook
|
|
|
|
|
table.Append(data)
|
2018-10-17 16:56:50 +08:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
table.Render()
|
|
|
|
|
|
|
|
|
|
return buf.String()
|
2018-10-17 10:09:42 +08:00
|
|
|
|
}
|
|
|
|
|
|
2018-05-13 14:04:37 +08:00
|
|
|
|
// 阻塞执行监听
|
|
|
|
|
func (s *Server) Run() error {
|
|
|
|
|
if err := s.Start(); err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
2018-05-11 12:57:05 +08:00
|
|
|
|
// 阻塞等待服务执行完成
|
2018-05-10 23:52:09 +08:00
|
|
|
|
<- doneChan
|
2018-06-01 00:11:45 +08:00
|
|
|
|
|
|
|
|
|
glog.Printfln("%d: all servers shutdown", gproc.Pid())
|
2018-05-10 19:16:41 +08:00
|
|
|
|
return nil
|
|
|
|
|
}
|
2018-05-10 17:48:47 +08:00
|
|
|
|
|
2018-05-13 14:04:37 +08:00
|
|
|
|
|
|
|
|
|
// 阻塞等待所有Web Server停止,常用于多Web Server场景,以及需要将Web Server异步运行的场景
|
|
|
|
|
// 这是一个与进程相关的方法
|
|
|
|
|
func Wait() {
|
2018-06-01 00:11:45 +08:00
|
|
|
|
// 阻塞等待服务执行完成
|
2018-05-13 14:04:37 +08:00
|
|
|
|
<- doneChan
|
2018-06-01 00:11:45 +08:00
|
|
|
|
|
|
|
|
|
glog.Printfln("%d: all servers shutdown", gproc.Pid())
|
2018-05-13 14:04:37 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2018-05-10 19:16:41 +08:00
|
|
|
|
// 开启底层Web Server执行
|
2018-05-10 23:52:09 +08:00
|
|
|
|
func (s *Server) startServer(fdMap listenerFdMap) {
|
2018-05-13 22:00:10 +08:00
|
|
|
|
var httpsEnabled bool
|
2018-04-23 19:22:59 +08:00
|
|
|
|
if len(s.config.HTTPSCertPath) > 0 && len(s.config.HTTPSKeyPath) > 0 {
|
2018-05-13 22:00:10 +08:00
|
|
|
|
// ================
|
2018-04-23 19:22:59 +08:00
|
|
|
|
// HTTPS
|
2018-05-13 22:00:10 +08:00
|
|
|
|
// ================
|
2018-04-27 22:12:11 +08:00
|
|
|
|
if len(s.config.HTTPSAddr) == 0 {
|
|
|
|
|
if len(s.config.Addr) > 0 {
|
|
|
|
|
s.config.HTTPSAddr = s.config.Addr
|
2018-05-13 22:00:10 +08:00
|
|
|
|
s.config.Addr = ""
|
2018-04-27 22:12:11 +08:00
|
|
|
|
} else {
|
|
|
|
|
s.config.HTTPSAddr = gDEFAULT_HTTPS_ADDR
|
|
|
|
|
}
|
2018-04-23 19:22:59 +08:00
|
|
|
|
}
|
2018-05-13 22:00:10 +08:00
|
|
|
|
httpsEnabled = len(s.config.HTTPSAddr) > 0
|
2018-05-10 19:16:41 +08:00
|
|
|
|
var array []string
|
|
|
|
|
if v, ok := fdMap["https"]; ok && len(v) > 0 {
|
|
|
|
|
array = strings.Split(v, ",")
|
|
|
|
|
} else {
|
|
|
|
|
array = strings.Split(s.config.HTTPSAddr, ",")
|
|
|
|
|
}
|
2018-04-29 16:52:44 +08:00
|
|
|
|
for _, v := range array {
|
2018-05-13 22:00:10 +08:00
|
|
|
|
if len(v) == 0 {
|
|
|
|
|
continue
|
|
|
|
|
}
|
2018-05-15 18:34:00 +08:00
|
|
|
|
fd := 0
|
|
|
|
|
addr := v
|
|
|
|
|
array := strings.Split(v, "#")
|
|
|
|
|
if len(array) > 1 {
|
|
|
|
|
addr = array[0]
|
|
|
|
|
// windows系统不支持文件描述符传递socket通信平滑交接,因此只能完整重启
|
|
|
|
|
if runtime.GOOS != "windows" {
|
|
|
|
|
fd = gconv.Int(array[1])
|
2018-04-27 22:12:11 +08:00
|
|
|
|
}
|
2018-05-15 18:34:00 +08:00
|
|
|
|
}
|
|
|
|
|
if fd > 0 {
|
|
|
|
|
s.servers = append(s.servers, s.newGracefulServer(addr, fd))
|
|
|
|
|
} else {
|
|
|
|
|
s.servers = append(s.servers, s.newGracefulServer(addr))
|
|
|
|
|
}
|
2018-05-16 21:27:27 +08:00
|
|
|
|
s.servers[len(s.servers) - 1].isHttps = true
|
2018-04-23 19:22:59 +08:00
|
|
|
|
}
|
2018-04-27 22:12:11 +08:00
|
|
|
|
}
|
2018-05-13 22:00:10 +08:00
|
|
|
|
// ================
|
2018-04-27 22:12:11 +08:00
|
|
|
|
// HTTP
|
2018-05-13 22:00:10 +08:00
|
|
|
|
// ================
|
|
|
|
|
// 当HTTPS服务未启用时,默认HTTP地址才会生效
|
|
|
|
|
if !httpsEnabled && len(s.config.Addr) == 0 {
|
2018-04-27 22:12:11 +08:00
|
|
|
|
s.config.Addr = gDEFAULT_HTTP_ADDR
|
|
|
|
|
}
|
2018-05-10 19:16:41 +08:00
|
|
|
|
var array []string
|
|
|
|
|
if v, ok := fdMap["http"]; ok && len(v) > 0 {
|
|
|
|
|
array = strings.Split(v, ",")
|
|
|
|
|
} else {
|
|
|
|
|
array = strings.Split(s.config.Addr, ",")
|
|
|
|
|
}
|
2018-04-29 16:52:44 +08:00
|
|
|
|
for _, v := range array {
|
2018-05-13 22:00:10 +08:00
|
|
|
|
if len(v) == 0 {
|
|
|
|
|
continue
|
|
|
|
|
}
|
2018-05-15 18:34:00 +08:00
|
|
|
|
fd := 0
|
|
|
|
|
addr := v
|
|
|
|
|
array := strings.Split(v, "#")
|
|
|
|
|
if len(array) > 1 {
|
|
|
|
|
addr = array[0]
|
|
|
|
|
// windows系统不支持文件描述符传递socket通信平滑交接,因此只能完整重启
|
|
|
|
|
if runtime.GOOS != "windows" {
|
|
|
|
|
fd = gconv.Int(array[1])
|
2018-05-14 17:35:54 +08:00
|
|
|
|
}
|
2018-05-15 18:34:00 +08:00
|
|
|
|
}
|
|
|
|
|
if fd > 0 {
|
|
|
|
|
s.servers = append(s.servers, s.newGracefulServer(addr, fd))
|
|
|
|
|
} else {
|
|
|
|
|
s.servers = append(s.servers, s.newGracefulServer(addr))
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
// 开始执行异步监听
|
|
|
|
|
for _, v := range s.servers {
|
|
|
|
|
go func(server *gracefulServer) {
|
2018-08-01 13:04:15 +08:00
|
|
|
|
serverRunning.Add(1)
|
|
|
|
|
err := (error)(nil)
|
2018-05-15 18:34:00 +08:00
|
|
|
|
if server.isHttps {
|
|
|
|
|
err = server.ListenAndServeTLS(s.config.HTTPSCertPath, s.config.HTTPSKeyPath)
|
2018-05-08 18:41:29 +08:00
|
|
|
|
} else {
|
2018-05-15 18:34:00 +08:00
|
|
|
|
err = server.ListenAndServe()
|
2018-05-08 18:41:29 +08:00
|
|
|
|
}
|
2018-08-01 13:04:15 +08:00
|
|
|
|
serverRunning.Add(-1)
|
2018-05-15 18:34:00 +08:00
|
|
|
|
// 如果非关闭错误,那么提示报错,否则认为是正常的服务关闭操作
|
|
|
|
|
if err != nil && !strings.EqualFold(http.ErrServerClosed.Error(), err.Error()) {
|
|
|
|
|
glog.Error(err)
|
2018-04-27 22:12:11 +08:00
|
|
|
|
}
|
2018-08-27 22:14:06 +08:00
|
|
|
|
// 如果所有异步的Server都已经停止,并且没有在管理操作(重启/关闭)进行中,那么主Server就可以退出了
|
|
|
|
|
if serverRunning.Val() < 1 && serverProcessStatus.Val() == 0 {
|
2018-08-01 13:04:15 +08:00
|
|
|
|
doneChan <- struct{}{}
|
|
|
|
|
}
|
2018-04-29 16:52:44 +08:00
|
|
|
|
}(v)
|
2017-11-23 10:21:28 +08:00
|
|
|
|
}
|
2018-08-01 13:04:15 +08:00
|
|
|
|
}
|
2018-04-23 19:22:59 +08:00
|
|
|
|
|
2018-08-01 13:04:15 +08:00
|
|
|
|
// 获取当前服务器的状态
|
|
|
|
|
func (s *Server) Status() int {
|
2018-09-04 18:54:16 +08:00
|
|
|
|
// 当全局运行的Web Server数量为0时表示所有Server都是停止状态
|
|
|
|
|
if serverRunning.Val() == 0 {
|
|
|
|
|
return SERVER_STATUS_STOPPED
|
|
|
|
|
}
|
|
|
|
|
// 只要有一个Server处于运行状态,那么都表示运行状态
|
|
|
|
|
for _, v := range s.servers {
|
|
|
|
|
if v.status == SERVER_STATUS_RUNNING {
|
|
|
|
|
return SERVER_STATUS_RUNNING
|
|
|
|
|
}
|
2018-08-01 13:04:15 +08:00
|
|
|
|
}
|
|
|
|
|
return SERVER_STATUS_STOPPED
|
2018-05-08 23:38:09 +08:00
|
|
|
|
}
|
|
|
|
|
|
2018-05-10 19:16:41 +08:00
|
|
|
|
// 获取当前监听的文件描述符信息,构造成map返回
|
2018-05-10 23:52:09 +08:00
|
|
|
|
func (s *Server) getListenerFdMap() map[string]string {
|
2018-05-11 12:57:05 +08:00
|
|
|
|
m := map[string]string {
|
2018-05-10 19:16:41 +08:00
|
|
|
|
"https" : "",
|
2018-05-14 17:35:54 +08:00
|
|
|
|
"http" : "",
|
2018-05-10 19:16:41 +08:00
|
|
|
|
}
|
2018-05-14 17:35:54 +08:00
|
|
|
|
// s.servers是从HTTPS到HTTP优先级遍历,解析的时候也应当按照这个顺序读取fd
|
2018-05-10 19:16:41 +08:00
|
|
|
|
for _, v := range s.servers {
|
2018-05-14 17:35:54 +08:00
|
|
|
|
str := v.addr + "#" + gconv.String(v.Fd()) + ","
|
|
|
|
|
if v.isHttps {
|
|
|
|
|
m["https"] += str
|
2018-05-10 19:16:41 +08:00
|
|
|
|
} else {
|
2018-05-14 17:35:54 +08:00
|
|
|
|
m["http"] += str
|
2018-05-10 19:16:41 +08:00
|
|
|
|
}
|
|
|
|
|
}
|
2018-05-14 17:35:54 +08:00
|
|
|
|
// 去掉末尾的","号
|
2018-05-10 19:16:41 +08:00
|
|
|
|
if len(m["https"]) > 0 {
|
|
|
|
|
m["https"] = m["https"][0 : len(m["https"]) - 1]
|
|
|
|
|
}
|
2018-05-14 17:35:54 +08:00
|
|
|
|
if len(m["http"]) > 0 {
|
|
|
|
|
m["http"] = m["http"][0 : len(m["http"]) - 1]
|
|
|
|
|
}
|
|
|
|
|
|
2018-05-10 19:16:41 +08:00
|
|
|
|
return m
|
|
|
|
|
}
|