github.com/zhongdalu/gf@v1.0.0/g/net/ghttp/ghttp_server.go (about)

     1  // Copyright 2017 gf Author(https://github.com/zhongdalu/gf). All Rights Reserved.
     2  //
     3  // This Source Code Form is subject to the terms of the MIT License.
     4  // If a copy of the MIT was not distributed with this file,
     5  // You can obtain one at https://github.com/zhongdalu/gf.
     6  
     7  package ghttp
     8  
     9  import (
    10  	"bytes"
    11  	"errors"
    12  	"fmt"
    13  	"net/http"
    14  	"os"
    15  	"reflect"
    16  	"runtime"
    17  	"strings"
    18  	"sync"
    19  	"time"
    20  
    21  	"github.com/zhongdalu/gf/g/container/garray"
    22  	"github.com/zhongdalu/gf/g/container/gmap"
    23  	"github.com/zhongdalu/gf/g/container/gtype"
    24  	"github.com/zhongdalu/gf/g/os/gcache"
    25  	"github.com/zhongdalu/gf/g/os/genv"
    26  	"github.com/zhongdalu/gf/g/os/gfile"
    27  	"github.com/zhongdalu/gf/g/os/glog"
    28  	"github.com/zhongdalu/gf/g/os/gproc"
    29  	"github.com/zhongdalu/gf/g/os/gtimer"
    30  	"github.com/zhongdalu/gf/g/text/gregex"
    31  	"github.com/zhongdalu/gf/g/util/gconv"
    32  	"github.com/zhongdalu/gf/third/github.com/gorilla/websocket"
    33  	"github.com/zhongdalu/gf/third/github.com/olekukonko/tablewriter"
    34  )
    35  
    36  type (
    37  	// Server结构体
    38  	Server struct {
    39  		name             string                           // 服务名称
    40  		config           ServerConfig                     // 配置对象
    41  		servers          []*gracefulServer                // 底层http.Server列表
    42  		serverCount      *gtype.Int                       // 底层http.Server数量
    43  		closeChan        chan struct{}                    // 用以关闭事件通知的通道
    44  		servedCount      *gtype.Int                       // 已经服务的请求数(4-8字节,不考虑溢出情况),同时作为请求ID
    45  		serveTree        map[string]interface{}           // 所有注册的服务回调函数(路由表,树型结构,哈希表+链表优先级匹配)
    46  		hooksTree        map[string]interface{}           // 所有注册的事件回调函数(路由表,树型结构,哈希表+链表优先级匹配)
    47  		serveCache       *gcache.Cache                    // 服务注册路由内存缓存
    48  		hooksCache       *gcache.Cache                    // 事件回调路由内存缓存
    49  		routesMap        map[string][]registeredRouteItem // 已经注册的路由及对应的注册方法文件地址(用以路由重复注册判断)
    50  		statusHandlerMu  sync.RWMutex                     // status handler互斥锁
    51  		statusHandlerMap map[string]HandlerFunc           // 不同状态码下的注册处理方法(例如404状态时的处理方法)
    52  		sessions         *gcache.Cache                    // Session内存缓存
    53  		logger           *glog.Logger                     // 日志管理对象
    54  	}
    55  
    56  	// 路由对象
    57  	Router struct {
    58  		Uri      string   // 注册时的pattern - uri
    59  		Method   string   // 注册时的pattern - method
    60  		Domain   string   // 注册时的pattern - domain
    61  		RegRule  string   // 路由规则解析后对应的正则表达式
    62  		RegNames []string // 路由规则解析后对应的变量名称数组
    63  		Priority int      // 优先级,用于链表排序,值越大优先级越高
    64  	}
    65  
    66  	// http回调函数注册信息
    67  	handlerItem struct {
    68  		name   string       // 注册的方法名称信息
    69  		rtype  int          // 注册方式(执行对象/回调函数/控制器)
    70  		ctype  reflect.Type // 控制器类型(反射类型)
    71  		fname  string       // 回调方法名称
    72  		faddr  HandlerFunc  // 准确的执行方法内存地址(与以上两个参数二选一)
    73  		finit  HandlerFunc  // 初始化请求回调方法(执行对象注册方式下有效)
    74  		fshut  HandlerFunc  // 完成请求回调方法(执行对象注册方式下有效)
    75  		router *Router      // 注册时绑定的路由对象
    76  	}
    77  
    78  	// 根据特定URL.Path解析后的路由检索结果项
    79  	handlerParsedItem struct {
    80  		handler *handlerItem        // 路由注册项
    81  		values  map[string][]string // 特定URL.Path的Router解析参数
    82  	}
    83  
    84  	// 已注册的路由项
    85  	registeredRouteItem struct {
    86  		file    string       // 文件路径及行数地址
    87  		handler *handlerItem // 路由注册项
    88  	}
    89  
    90  	// pattern与回调函数的绑定map
    91  	handlerMap = map[string]*handlerItem
    92  
    93  	// HTTP注册函数
    94  	HandlerFunc = func(r *Request)
    95  
    96  	// 文件描述符map
    97  	listenerFdMap = map[string]string
    98  )
    99  
   100  const (
   101  	SERVER_STATUS_STOPPED = 0              // Server状态:停止
   102  	SERVER_STATUS_RUNNING = 1              // Server状态:运行
   103  	HOOK_BEFORE_SERVE     = "BeforeServe"  // 回调事件,在执行服务前
   104  	HOOK_AFTER_SERVE      = "AfterServe"   // 回调事件,在执行服务后
   105  	HOOK_BEFORE_OUTPUT    = "BeforeOutput" // 回调事件,在输出结果前
   106  	HOOK_AFTER_OUTPUT     = "AfterOutput"  // 回调事件,在输出结果后
   107  	HOOK_BEFORE_CLOSE     = "BeforeClose"  // Deprecated.
   108  	HOOK_AFTER_CLOSE      = "AfterClose"   // Deprecated.
   109  
   110  	HTTP_METHODS               = "GET,PUT,POST,DELETE,PATCH,HEAD,CONNECT,OPTIONS,TRACE"
   111  	gDEFAULT_SERVER            = "default"
   112  	gDEFAULT_DOMAIN            = "default"
   113  	gDEFAULT_METHOD            = "ALL"
   114  	gROUTE_REGISTER_HANDLER    = 1
   115  	gROUTE_REGISTER_OBJECT     = 2
   116  	gROUTE_REGISTER_CONTROLLER = 3
   117  	gEXCEPTION_EXIT            = "exit"
   118  	gEXCEPTION_EXIT_ALL        = "exit_all"
   119  	gEXCEPTION_EXIT_HOOK       = "exit_hook"
   120  )
   121  
   122  var (
   123  	// 所有支持的HTTP Method Map(初始化时自动填充),
   124  	// 用于快速检索需要
   125  	methodsMap = make(map[string]struct{})
   126  
   127  	// WebServer表,用以存储和检索名称与Server对象之间的关联关系
   128  	serverMapping = gmap.NewStrAnyMap()
   129  
   130  	// 正常运行的WebServer数量,如果没有运行、失败或者全部退出,那么该值为0
   131  	serverRunning = gtype.NewInt()
   132  
   133  	// WebSocket默认配置
   134  	wsUpgrader = websocket.Upgrader{
   135  		// 默认允许WebSocket请求跨域,权限控制可以由业务层自己负责,灵活度更高
   136  		CheckOrigin: func(r *http.Request) bool {
   137  			return true
   138  		},
   139  	}
   140  	// WebServer已完成服务事件通道,当有事件时表示服务完成,当前进程退出
   141  	allDoneChan = make(chan struct{}, 1000)
   142  
   143  	// 用于服务进程初始化,只能初始化一次,采用“懒初始化”(在server运行时才初始化)
   144  	serverProcessInited = gtype.NewBool()
   145  
   146  	// 是否开启WebServer平滑重启特性, 会开启额外的本地端口监听,用于进程管理通信(默认开启)
   147  	gracefulEnabled = true
   148  )
   149  
   150  func init() {
   151  	for _, v := range strings.Split(HTTP_METHODS, ",") {
   152  		methodsMap[v] = struct{}{}
   153  	}
   154  }
   155  
   156  // 是否开启平滑重启特性
   157  func SetGraceful(enabled bool) {
   158  	gracefulEnabled = enabled
   159  }
   160  
   161  // Web Server进程初始化.
   162  // 注意该方法不能放置于包初始化方法init中,不使用ghttp.Server的功能便不能初始化对应的协程goroutine逻辑.
   163  func serverProcessInit() {
   164  	if serverProcessInited.Val() {
   165  		return
   166  	}
   167  	serverProcessInited.Set(true)
   168  	// 如果是完整重启,那么需要等待主进程销毁后,才开始执行监听,防止端口冲突
   169  	if genv.Get(gADMIN_ACTION_RESTART_ENVKEY) != "" {
   170  		if p, e := os.FindProcess(gproc.PPid()); e == nil {
   171  			p.Kill()
   172  			p.Wait()
   173  		} else {
   174  			glog.Error(e)
   175  		}
   176  	}
   177  
   178  	// 信号量管理操作监听
   179  	go handleProcessSignal()
   180  	// 异步监听进程间消息
   181  	if gracefulEnabled {
   182  		go handleProcessMessage()
   183  	}
   184  
   185  	// 是否处于开发环境,这里调用该方法初始化main包路径值,
   186  	// 防止异步服务goroutine获取main包路径失败,
   187  	// 该方法只有在main协程中才会执行。
   188  	gfile.MainPkgPath()
   189  }
   190  
   191  // 获取/创建一个默认配置的HTTP Server(默认监听端口是80)
   192  // 单例模式,请保证name的唯一性
   193  func GetServer(name ...interface{}) *Server {
   194  	sname := gDEFAULT_SERVER
   195  	if len(name) > 0 {
   196  		sname = gconv.String(name[0])
   197  	}
   198  	if s := serverMapping.Get(sname); s != nil {
   199  		return s.(*Server)
   200  	}
   201  	s := &Server{
   202  		name:             sname,
   203  		servers:          make([]*gracefulServer, 0),
   204  		closeChan:        make(chan struct{}, 100),
   205  		serverCount:      gtype.NewInt(),
   206  		statusHandlerMap: make(map[string]HandlerFunc),
   207  		serveTree:        make(map[string]interface{}),
   208  		hooksTree:        make(map[string]interface{}),
   209  		serveCache:       gcache.New(),
   210  		hooksCache:       gcache.New(),
   211  		routesMap:        make(map[string][]registeredRouteItem),
   212  		sessions:         gcache.New(),
   213  		servedCount:      gtype.NewInt(),
   214  		logger:           glog.New(),
   215  	}
   216  	// 初始化时使用默认配置
   217  	s.SetConfig(defaultServerConfig)
   218  	// 记录到全局ServerMap中
   219  	serverMapping.Set(sname, s)
   220  	return s
   221  }
   222  
   223  // 作为守护协程异步执行(当同一进程中存在多个Web Server时,需要采用这种方式执行),
   224  // 需要结合Wait方式一起使用.
   225  func (s *Server) Start() error {
   226  	// 服务进程初始化,只会初始化一次
   227  	serverProcessInit()
   228  
   229  	// 当前Web Server状态判断
   230  	if s.Status() == SERVER_STATUS_RUNNING {
   231  		return errors.New("server is already running")
   232  	}
   233  
   234  	// 没有注册任何路由,且没有开启文件服务,那么提示错误
   235  	if len(s.routesMap) == 0 && !s.config.FileServerEnabled {
   236  		glog.Fatal("[ghttp] no router set or static feature enabled, did you forget import the router?")
   237  	}
   238  
   239  	// 底层http server配置
   240  	if s.config.Handler == nil {
   241  		s.config.Handler = http.HandlerFunc(s.defaultHttpHandle)
   242  	}
   243  	// 不允许访问的路由注册(使用HOOK实现)
   244  	// TODO 去掉HOOK的实现方式
   245  	if s.config.DenyRoutes != nil {
   246  		for _, v := range s.config.DenyRoutes {
   247  			s.BindHookHandler(v, HOOK_BEFORE_SERVE, func(r *Request) {
   248  				r.Response.WriteStatus(403)
   249  				r.ExitAll()
   250  			})
   251  		}
   252  	}
   253  
   254  	// gzip压缩文件类型
   255  	//if s.config.GzipContentTypes != nil {
   256  	//    for _, v := range s.config.GzipContentTypes {
   257  	//        s.gzipMimesMap[v] = struct{}{}
   258  	//    }
   259  	//}
   260  
   261  	// 启动http server
   262  	reloaded := false
   263  	fdMapStr := genv.Get(gADMIN_ACTION_RELOAD_ENVKEY)
   264  	if len(fdMapStr) > 0 {
   265  		sfm := bufferToServerFdMap([]byte(fdMapStr))
   266  		if v, ok := sfm[s.name]; ok {
   267  			s.startServer(v)
   268  			reloaded = true
   269  		}
   270  	}
   271  	if !reloaded {
   272  		s.startServer(nil)
   273  	}
   274  
   275  	// 如果是子进程,那么服务开启后通知父进程销毁
   276  	if gproc.IsChild() {
   277  		gtimer.SetTimeout(2*time.Second, func() {
   278  			if err := gproc.Send(gproc.PPid(), []byte("exit"), gADMIN_GPROC_COMM_GROUP); err != nil {
   279  				glog.Error("[ghttp] server error in process communication:", err)
   280  			}
   281  		})
   282  	}
   283  
   284  	// 打印展示路由表
   285  	s.DumpRoutesMap()
   286  	return nil
   287  }
   288  
   289  // 打印展示路由表
   290  func (s *Server) DumpRoutesMap() {
   291  	if s.config.DumpRouteMap && len(s.routesMap) > 0 {
   292  		// (等待一定时间后)当所有框架初始化信息打印完毕之后才打印路由表信息
   293  		gtimer.SetTimeout(50*time.Millisecond, func() {
   294  			glog.Header(false).Println(fmt.Sprintf("\n%s", s.GetRouteMap()))
   295  		})
   296  	}
   297  }
   298  
   299  // 获得路由表(格式化字符串)
   300  func (s *Server) GetRouteMap() string {
   301  	type tableItem struct {
   302  		hook     string
   303  		domain   string
   304  		method   string
   305  		route    string
   306  		handler  string
   307  		priority int
   308  	}
   309  
   310  	buf := bytes.NewBuffer(nil)
   311  	table := tablewriter.NewWriter(buf)
   312  	table.SetHeader([]string{"SERVER", "ADDRESS", "DOMAIN", "METHOD", "P", "ROUTE", "HANDLER", "HOOK"})
   313  	table.SetRowLine(true)
   314  	table.SetBorder(false)
   315  	table.SetCenterSeparator("|")
   316  
   317  	m := make(map[string]*garray.SortedArray)
   318  	for k, registeredItems := range s.routesMap {
   319  		array, _ := gregex.MatchString(`(.*?)%([A-Z]+):(.+)@(.+)`, k)
   320  		for index, registeredItem := range registeredItems {
   321  			item := &tableItem{
   322  				hook:     array[1],
   323  				domain:   array[4],
   324  				method:   array[2],
   325  				route:    array[3],
   326  				handler:  registeredItem.handler.name,
   327  				priority: len(registeredItems) - index - 1,
   328  			}
   329  			if _, ok := m[item.domain]; !ok {
   330  				// 注意排序函数的逻辑
   331  				m[item.domain] = garray.NewSortedArraySize(100, func(v1, v2 interface{}) int {
   332  					item1 := v1.(*tableItem)
   333  					item2 := v2.(*tableItem)
   334  					r := 0
   335  					if r = strings.Compare(item1.domain, item2.domain); r == 0 {
   336  						if r = strings.Compare(item1.route, item2.route); r == 0 {
   337  							if r = strings.Compare(item1.method, item2.method); r == 0 {
   338  								if r = strings.Compare(item1.hook, item2.hook); r == 0 {
   339  									r = item2.priority - item1.priority
   340  								}
   341  							}
   342  						}
   343  					}
   344  					return r
   345  				}, false)
   346  			}
   347  			m[item.domain].Add(item)
   348  		}
   349  	}
   350  	addr := s.config.Addr
   351  	if s.config.HTTPSAddr != "" {
   352  		if len(addr) > 0 {
   353  			addr += ","
   354  		}
   355  		addr += "tls" + s.config.HTTPSAddr
   356  	}
   357  	for _, a := range m {
   358  		data := make([]string, 8)
   359  		for _, v := range a.Slice() {
   360  			item := v.(*tableItem)
   361  			data[0] = s.name
   362  			data[1] = addr
   363  			data[2] = item.domain
   364  			data[3] = item.method
   365  			data[4] = gconv.String(len(strings.Split(item.route, "/")) - 1 + item.priority)
   366  			data[5] = item.route
   367  			data[6] = item.handler
   368  			data[7] = item.hook
   369  			table.Append(data)
   370  		}
   371  	}
   372  	table.Render()
   373  
   374  	return buf.String()
   375  }
   376  
   377  // 阻塞执行监听
   378  func (s *Server) Run() {
   379  	if err := s.Start(); err != nil {
   380  		glog.Fatal(err)
   381  	}
   382  	// 阻塞等待服务执行完成
   383  	<-s.closeChan
   384  
   385  	glog.Printf("%d: all servers shutdown", gproc.Pid())
   386  }
   387  
   388  // 阻塞等待所有Web Server停止,常用于多Web Server场景,以及需要将Web Server异步运行的场景
   389  // 这是一个与进程相关的方法
   390  func Wait() {
   391  	// 阻塞等待服务执行完成
   392  	<-allDoneChan
   393  
   394  	glog.Printf("%d: all servers shutdown", gproc.Pid())
   395  }
   396  
   397  // 开启底层Web Server执行
   398  func (s *Server) startServer(fdMap listenerFdMap) {
   399  	var httpsEnabled bool
   400  	// 判断是否启用HTTPS
   401  	if len(s.config.TLSConfig.Certificates) > 0 || (len(s.config.HTTPSCertPath) > 0 && len(s.config.HTTPSKeyPath) > 0) {
   402  		// ================
   403  		// HTTPS
   404  		// ================
   405  		if len(s.config.HTTPSAddr) == 0 {
   406  			if len(s.config.Addr) > 0 {
   407  				s.config.HTTPSAddr = s.config.Addr
   408  				s.config.Addr = ""
   409  			} else {
   410  				s.config.HTTPSAddr = gDEFAULT_HTTPS_ADDR
   411  			}
   412  		}
   413  		httpsEnabled = len(s.config.HTTPSAddr) > 0
   414  		var array []string
   415  		if v, ok := fdMap["https"]; ok && len(v) > 0 {
   416  			array = strings.Split(v, ",")
   417  		} else {
   418  			array = strings.Split(s.config.HTTPSAddr, ",")
   419  		}
   420  		for _, v := range array {
   421  			if len(v) == 0 {
   422  				continue
   423  			}
   424  			fd := 0
   425  			addr := v
   426  			array := strings.Split(v, "#")
   427  			if len(array) > 1 {
   428  				addr = array[0]
   429  				// windows系统不支持文件描述符传递socket通信平滑交接,因此只能完整重启
   430  				if runtime.GOOS != "windows" {
   431  					fd = gconv.Int(array[1])
   432  				}
   433  			}
   434  			if fd > 0 {
   435  				s.servers = append(s.servers, s.newGracefulServer(addr, fd))
   436  			} else {
   437  				s.servers = append(s.servers, s.newGracefulServer(addr))
   438  			}
   439  			s.servers[len(s.servers)-1].isHttps = true
   440  		}
   441  	}
   442  	// ================
   443  	// HTTP
   444  	// ================
   445  	// 当HTTPS服务未启用时,默认HTTP地址才会生效
   446  	if !httpsEnabled && len(s.config.Addr) == 0 {
   447  		s.config.Addr = gDEFAULT_HTTP_ADDR
   448  	}
   449  	var array []string
   450  	if v, ok := fdMap["http"]; ok && len(v) > 0 {
   451  		array = strings.Split(v, ",")
   452  	} else {
   453  		array = strings.Split(s.config.Addr, ",")
   454  	}
   455  	for _, v := range array {
   456  		if len(v) == 0 {
   457  			continue
   458  		}
   459  		fd := 0
   460  		addr := v
   461  		array := strings.Split(v, "#")
   462  		if len(array) > 1 {
   463  			addr = array[0]
   464  			// windows系统不支持文件描述符传递socket通信平滑交接,因此只能完整重启
   465  			if runtime.GOOS != "windows" {
   466  				fd = gconv.Int(array[1])
   467  			}
   468  		}
   469  		if fd > 0 {
   470  			s.servers = append(s.servers, s.newGracefulServer(addr, fd))
   471  		} else {
   472  			s.servers = append(s.servers, s.newGracefulServer(addr))
   473  		}
   474  	}
   475  	// 开始执行异步监听
   476  	serverRunning.Add(1)
   477  	for _, v := range s.servers {
   478  		go func(server *gracefulServer) {
   479  			s.serverCount.Add(1)
   480  			err := (error)(nil)
   481  			if server.isHttps {
   482  				err = server.ListenAndServeTLS(s.config.HTTPSCertPath, s.config.HTTPSKeyPath, &s.config.TLSConfig)
   483  			} else {
   484  				err = server.ListenAndServe()
   485  			}
   486  			// 如果非关闭错误,那么提示报错,否则认为是正常的服务关闭操作
   487  			if err != nil && !strings.EqualFold(http.ErrServerClosed.Error(), err.Error()) {
   488  				glog.Fatal(err)
   489  			}
   490  			// 如果所有异步的http.Server都已经停止,那么WebServer就可以退出了
   491  			if s.serverCount.Add(-1) < 1 {
   492  				s.closeChan <- struct{}{}
   493  				// 如果所有WebServer都退出,那么退出Wait等待
   494  				if serverRunning.Add(-1) < 1 {
   495  					serverMapping.Remove(s.name)
   496  					allDoneChan <- struct{}{}
   497  				}
   498  			}
   499  		}(v)
   500  	}
   501  }
   502  
   503  // 获取当前服务器的状态
   504  func (s *Server) Status() int {
   505  	// 当全局运行的Web Server数量为0时表示所有Server都是停止状态
   506  	if serverRunning.Val() == 0 {
   507  		return SERVER_STATUS_STOPPED
   508  	}
   509  	// 只要有一个Server处于运行状态,那么都表示运行状态
   510  	for _, v := range s.servers {
   511  		if v.status == SERVER_STATUS_RUNNING {
   512  			return SERVER_STATUS_RUNNING
   513  		}
   514  	}
   515  	return SERVER_STATUS_STOPPED
   516  }
   517  
   518  // 获取当前监听的文件描述符信息,构造成map返回
   519  func (s *Server) getListenerFdMap() map[string]string {
   520  	m := map[string]string{
   521  		"https": "",
   522  		"http":  "",
   523  	}
   524  	// s.servers是从HTTPS到HTTP优先级遍历,解析的时候也应当按照这个顺序读取fd
   525  	for _, v := range s.servers {
   526  		str := v.addr + "#" + gconv.String(v.Fd()) + ","
   527  		if v.isHttps {
   528  			m["https"] += str
   529  		} else {
   530  			m["http"] += str
   531  		}
   532  	}
   533  	// 去掉末尾的","号
   534  	if len(m["https"]) > 0 {
   535  		m["https"] = m["https"][0 : len(m["https"])-1]
   536  	}
   537  	if len(m["http"]) > 0 {
   538  		m["http"] = m["http"][0 : len(m["http"])-1]
   539  	}
   540  
   541  	return m
   542  }