Skip to content

Commit

Permalink
#### Version 1.3.2
Browse files Browse the repository at this point in the history
* 主要新增设置启用详细请求数据控制,新增MethodNotAllowedHandler自定义能力,完善数据统计逻辑
* 新增设置启用详细请求数据控制:
* 1) ServerStateInfo增加EnabledDetailRequestData,用于控制是否启用详细请求数据统计
* 2) config.ServerConfig增加EnabledDetailRequestData设置,支持配置文件控制
* 3) HttpServer增加SetEnabledDetailRequestData函数,用于代码设置是否启用详细请求数据统计
* 4) fixed devfeel#63 状态数据,当url较多时,导致内存占用过大
* 新增MethodNotAllowedHandler自定义能力
* 1) dotweb增加DefaultMethodNotAllowedHandler、SetMethodNotAllowedHandle函数
* 完善数据统计逻辑
* 1) 404请求不计入详细请求数据统计
* 2017-10-13 12:00
  • Loading branch information
devfeel committed Oct 13, 2017
1 parent 8a14868 commit 671380e
Show file tree
Hide file tree
Showing 9 changed files with 227 additions and 190 deletions.
21 changes: 11 additions & 10 deletions config/configs.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,16 +41,17 @@ type (
}

ServerNode struct {
EnabledListDir bool `xml:"enabledlistdir,attr"` //设置是否启用目录浏览,仅对Router.ServerFile有效,若设置该项,则可以浏览目录文件,默认不开启
EnabledGzip bool `xml:"enabledgzip,attr"` //是否启用gzip
EnabledAutoHEAD bool `xml:"enabledautohead,attr"` //设置是否自动启用Head路由,若设置该项,则会为除Websocket\HEAD外所有路由方式默认添加HEAD路由,默认不开启
EnabledAutoCORS bool `xml:"enabledautocors,attr"` //设置是否自动跨域支持,若设置,默认“GET, POST, PUT, DELETE, OPTIONS”全部请求均支持跨域
EnabledIgnoreFavicon bool `xml:"enabledignorefavicon,attr"` //设置是否忽略favicon.ico请求,若设置,网站将把所有favicon.ico请求直接空返回
Port int `xml:"port,attr"` //端口
EnabledTLS bool `xml:"enabledtls,attr"` //是否启用TLS模式
TLSCertFile string `xml:"tlscertfile,attr"` //TLS模式下Certificate证书文件地址
TLSKeyFile string `xml:"tlskeyfile,attr"` //TLS模式下秘钥文件地址
IndexPage string `xml:"indexpage,attr"` //默认index页面
EnabledListDir bool `xml:"enabledlistdir,attr"` //设置是否启用目录浏览,仅对Router.ServerFile有效,若设置该项,则可以浏览目录文件,默认不开启
EnabledGzip bool `xml:"enabledgzip,attr"` //是否启用gzip
EnabledAutoHEAD bool `xml:"enabledautohead,attr"` //设置是否自动启用Head路由,若设置该项,则会为除Websocket\HEAD外所有路由方式默认添加HEAD路由,默认不开启
EnabledAutoCORS bool `xml:"enabledautocors,attr"` //设置是否自动跨域支持,若设置,默认“GET, POST, PUT, DELETE, OPTIONS”全部请求均支持跨域
EnabledIgnoreFavicon bool `xml:"enabledignorefavicon,attr"` //设置是否忽略favicon.ico请求,若设置,网站将把所有favicon.ico请求直接空返回
Port int `xml:"port,attr"` //端口
EnabledTLS bool `xml:"enabledtls,attr"` //是否启用TLS模式
TLSCertFile string `xml:"tlscertfile,attr"` //TLS模式下Certificate证书文件地址
TLSKeyFile string `xml:"tlskeyfile,attr"` //TLS模式下秘钥文件地址
IndexPage string `xml:"indexpage,attr"` //默认index页面
EnabledDetailRequestData bool `xml:"enableddetailrequestdata,attr"` //设置状态数据是否启用详细页面统计,默认不启用,请特别对待,如果站点url过多,会导致数据量过大
}

SessionNode struct {
Expand Down
2 changes: 1 addition & 1 deletion context.go
Original file line number Diff line number Diff line change
Expand Up @@ -254,7 +254,7 @@ func (ctx *HttpContext) Session() (state *session.SessionState) {
//return nil, errors.New("no effective http-server")
panic("no effective http-server")
}
if !ctx.httpServer.SessionConfig.EnabledSession {
if !ctx.httpServer.SessionConfig().EnabledSession {
//return nil, errors.New("http-server not enabled session")
panic("http-server not enabled session")
}
Expand Down
75 changes: 39 additions & 36 deletions core/state.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package core

import (
"github.com/devfeel/dotweb/framework/json"
"net/http"
"strconv"
"strings"
"sync"
Expand All @@ -20,18 +21,18 @@ const (

func init() {
GlobalState = &ServerStateInfo{
ServerStartTime: time.Now(),
TotalRequestCount: 0,
TotalErrorCount: 0,
IntervalRequestData: NewItemContext(),
DetailRequestPageData: NewItemContext(),
IntervalErrorData: NewItemContext(),
DetailErrorPageData: NewItemContext(),
DetailErrorData: NewItemContext(),
DetailHTTPCodeData: NewItemContext(),
dataChan_Request: make(chan *RequestInfo, 1000),
dataChan_Error: make(chan *ErrorInfo, 1000),
dataChan_HttpCode: make(chan *HttpCodeInfo, 1000),
ServerStartTime: time.Now(),
TotalRequestCount: 0,
TotalErrorCount: 0,
IntervalRequestData: NewItemContext(),
DetailRequestUrlData: NewItemContext(),
IntervalErrorData: NewItemContext(),
DetailErrorPageData: NewItemContext(),
DetailErrorData: NewItemContext(),
DetailHTTPCodeData: NewItemContext(),
dataChan_Request: make(chan *RequestInfo, 1000),
dataChan_Error: make(chan *ErrorInfo, 1000),
dataChan_HttpCode: make(chan *HttpCodeInfo, 1000),
infoPool: &pool{
requestInfo: sync.Pool{
New: func() interface{} {
Expand Down Expand Up @@ -63,8 +64,9 @@ type pool struct {

//http request count info
type RequestInfo struct {
Url string
Num uint64
Url string
Code int
Num uint64
}

//error count info
Expand All @@ -85,12 +87,14 @@ type HttpCodeInfo struct {
type ServerStateInfo struct {
//服务启动时间
ServerStartTime time.Time
//是否启用详细请求数据统计 fixed #63 状态数据,当url较多时,导致内存占用过大
EnabledDetailRequestData bool
//该运行期间总访问次数
TotalRequestCount uint64
//单位时间内请求数据 - 按分钟为单位
IntervalRequestData *ItemContext
//明细请求页面数据 - 以不带参数的访问url为key
DetailRequestPageData *ItemContext
DetailRequestUrlData *ItemContext
//该运行期间异常次数
TotalErrorCount uint64
//单位时间内异常次数 - 按分钟为单位
Expand Down Expand Up @@ -122,9 +126,9 @@ func (state *ServerStateInfo) ShowHtmlData() string {
data += "IntervalRequestData : " + jsonutil.GetJsonString(state.IntervalRequestData.GetCurrentMap())
state.IntervalRequestData.RUnlock()
data += "<br>"
state.DetailRequestPageData.RLock()
data += "DetailRequestPageData : " + jsonutil.GetJsonString(state.DetailRequestPageData.GetCurrentMap())
state.DetailRequestPageData.RUnlock()
state.DetailRequestUrlData.RLock()
data += "DetailRequestUrlData : " + jsonutil.GetJsonString(state.DetailRequestUrlData.GetCurrentMap())
state.DetailRequestUrlData.RUnlock()
data += "<br>"
state.IntervalErrorData.RLock()
data += "IntervalErrorData : " + jsonutil.GetJsonString(state.IntervalErrorData.GetCurrentMap())
Expand Down Expand Up @@ -156,20 +160,13 @@ func (state *ServerStateInfo) QueryIntervalErrorData(queryKey string) uint64 {
}

//AddRequestCount 增加请求数
func (state *ServerStateInfo) AddRequestCount(page string, num uint64) uint64 {
func (state *ServerStateInfo) AddRequestCount(page string, code int, num uint64) uint64 {
if strings.Index(page, "/dotweb/") != 0 {
atomic.AddUint64(&state.TotalRequestCount, num)
state.addRequestData(page, num)
}
return state.TotalRequestCount
}

//AddHttpCodeCount 增加Http状态码数据
func (state *ServerStateInfo) AddTTPCodeCount(page string, code int, num uint64) uint64 {
if strings.Index(page, "/dotweb/") != 0 {
state.addRequestData(page, code, num)
state.addHTTPCodeData(page, code, num)
}
return state.TotalErrorCount
return state.TotalRequestCount
}

//AddErrorCount 增加错误数
Expand All @@ -179,10 +176,11 @@ func (state *ServerStateInfo) AddErrorCount(page string, err error, num uint64)
return state.TotalErrorCount
}

func (state *ServerStateInfo) addRequestData(page string, num uint64) {
func (state *ServerStateInfo) addRequestData(page string, code int, num uint64) {
//get from pool
info := state.infoPool.requestInfo.Get().(*RequestInfo)
info.Url = page
info.Code = code
info.Num = num
state.dataChan_Request <- info
}
Expand Down Expand Up @@ -211,14 +209,19 @@ func (state *ServerStateInfo) handleInfo() {
select {
case info := <-state.dataChan_Request:
{
//set detail page data
key := strings.ToLower(info.Url)
val := state.DetailRequestPageData.GetUInt64(key)
state.DetailRequestPageData.Set(key, val+info.Num)

//fixed #63 状态数据,当url较多时,导致内存占用过大
if state.EnabledDetailRequestData {
//ignore 404 request
if info.Code != http.StatusNotFound {
//set detail url data
key := strings.ToLower(info.Url)
val := state.DetailRequestUrlData.GetUInt64(key)
state.DetailRequestUrlData.Set(key, val+info.Num)
}
}
//set interval data
key = time.Now().Format(minuteTimeLayout)
val = state.IntervalRequestData.GetUInt64(key)
key := time.Now().Format(minuteTimeLayout)
val := state.IntervalRequestData.GetUInt64(key)
state.IntervalRequestData.Set(key, val+info.Num)

//put info obj
Expand Down
160 changes: 100 additions & 60 deletions dotweb.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,20 +24,23 @@ import (

type (
DotWeb struct {
HttpServer *HttpServer
cache cache.Cache
OfflineServer servers.Server
Config *config.Config
Middlewares []Middleware
ExceptionHandler ExceptionHandle
NotFoundHandler NotFoundHandle
AppContext *core.ItemContext
middlewareMap map[string]MiddlewareFunc
middlewareMutex *sync.RWMutex
HttpServer *HttpServer
cache cache.Cache
OfflineServer servers.Server
Config *config.Config
Middlewares []Middleware
ExceptionHandler ExceptionHandle
NotFoundHandler NotFoundHandle
MethodNotAllowedHandler MethodNotAllowedHandle
AppContext *core.ItemContext
middlewareMap map[string]MiddlewareFunc
middlewareMutex *sync.RWMutex
}

ExceptionHandle func(Context, error)
NotFoundHandle func(http.ResponseWriter, *http.Request)
//fixed for #64 增加MethodNotAllowed自定义处理
MethodNotAllowedHandle func(Context)
NotFoundHandle func(Context)

// Handle is a function that can be registered to a route to handle HTTP
// requests. Like http.HandlerFunc, but has a special parameter Context contain all request and response data.
Expand Down Expand Up @@ -153,6 +156,11 @@ func (app *DotWeb) SetNotFoundHandle(handler NotFoundHandle) {
app.NotFoundHandler = handler
}

// SetMethodNotAllowedHandle set custom 405 handler
func (app *DotWeb) SetMethodNotAllowedHandle(handler MethodNotAllowedHandle) {
app.MethodNotAllowedHandler = handler
}

// SetPProfConfig set pprofserver config, default is disable
// and don't use same port with StartServer
func (app *DotWeb) SetPProfConfig(enabledPProf bool, httpport int) {
Expand Down Expand Up @@ -180,6 +188,59 @@ func (app *DotWeb) SetEnabledLog(enabledLog bool) {
func (app *DotWeb) SetConfig(config *config.Config) error {
app.Config = config

return nil
}

// StartServer start server with http port
// if config the pprof, will be start pprof server
func (app *DotWeb) StartServer(httpport int) error {
addr := ":" + strconv.Itoa(httpport)
return app.ListenAndServe(addr)
}

// Start start app server with set config
// If an exception occurs, will be return it
// if no set Server.Port, will be use DefaultHttpPort
func (app *DotWeb) Start() error {
if app.Config == nil {
return errors.New("no config set!")
}
//start server
port := app.Config.Server.Port
if port <= 0 {
port = DefaultHttpPort
}
return app.StartServer(port)
}

// MustStart start app server with set config
// If an exception occurs, will be panic it
// if no set Server.Port, will be use DefaultHttpPort
func (app *DotWeb) MustStart() {
err := app.Start()
if err != nil {
panic(err)
}
}

// ListenAndServe start server with addr
// not support pprof server auto start
func (app *DotWeb) ListenAndServe(addr string) error {
app.initAppConfig()
app.initServerEnvironment()
app.initInnerRouter()
if app.HttpServer.ServerConfig().EnabledTLS {
err := app.HttpServer.ListenAndServeTLS(addr, app.HttpServer.ServerConfig().TLSCertFile, app.HttpServer.ServerConfig().TLSKeyFile)
return err
}
err := app.HttpServer.ListenAndServe(addr)
return err

}

// init App Config
func (app *DotWeb) initAppConfig() {
config := app.Config
//log config
if config.App.LogPath != "" {
logger.SetLogPath(config.App.LogPath)
Expand Down Expand Up @@ -212,6 +273,11 @@ func (app *DotWeb) SetConfig(config *config.Config) error {
app.HttpServer.SetSessionConfig(session.NewStoreConfig(config.Session.SessionMode, config.Session.Timeout, config.Session.ServerIP))
}

//设置启用详细请求数据统计
if config.Server.EnabledDetailRequestData {
core.GlobalState.EnabledDetailRequestData = config.Server.EnabledDetailRequestData
}

//register app's middleware
for _, m := range config.Middlewares {
if m.IsUse {
Expand Down Expand Up @@ -265,53 +331,6 @@ func (app *DotWeb) SetConfig(config *config.Config) error {
}
}
}
return nil
}

// StartServer start server with http port
// if config the pprof, will be start pprof server
func (app *DotWeb) StartServer(httpport int) error {
addr := ":" + strconv.Itoa(httpport)
return app.ListenAndServe(addr)
}

// Start start app server with set config
// If an exception occurs, will be return it
// if no set Server.Port, will be use DefaultHttpPort
func (app *DotWeb) Start() error {
if app.Config == nil {
return errors.New("no config set!")
}
//start server
port := app.Config.Server.Port
if port <= 0 {
port = DefaultHttpPort
}
return app.StartServer(port)
}

// MustStart start app server with set config
// If an exception occurs, will be panic it
// if no set Server.Port, will be use DefaultHttpPort
func (app *DotWeb) MustStart() {
err := app.Start()
if err != nil {
panic(err)
}
}

// ListenAndServe start server with addr
// not support pprof server auto start
func (app *DotWeb) ListenAndServe(addr string) error {
app.initServerEnvironment()
app.initInnerRouter()
if app.HttpServer.ServerConfig.EnabledTLS {
err := app.HttpServer.ListenAndServeTLS(addr, app.HttpServer.ServerConfig.TLSCertFile, app.HttpServer.ServerConfig.TLSKeyFile)
return err
}
err := app.HttpServer.ListenAndServe(addr)
return err

}

// init inner routers
Expand All @@ -325,14 +344,23 @@ func (app *DotWeb) initInnerRouter() {
gInner.GET("/query/:key", showQuery)
}

// init Server Environment
func (app *DotWeb) initServerEnvironment() {
if app.ExceptionHandler == nil {
app.SetExceptionHandle(app.DefaultHTTPErrorHandler)
}

if app.NotFoundHandler == nil {
app.SetNotFoundHandle(app.DefaultNotFoundHandler)
}

if app.MethodNotAllowedHandler == nil {
app.SetMethodNotAllowedHandle(app.DefaultMethodNotAllowedHandler)
}

//init session manager
if app.HttpServer.SessionConfig.EnabledSession {
if app.HttpServer.SessionConfig.SessionMode == "" {
if app.HttpServer.SessionConfig().EnabledSession {
if app.HttpServer.SessionConfig().SessionMode == "" {
//panic("no set SessionConfig, but set enabledsession true")
logger.Logger().Warn("not set SessionMode, but set enabledsession true, now will use default runtime session", LogTarget_HttpServer)
app.HttpServer.SetSessionConfig(session.NewDefaultRuntimeConfig())
Expand Down Expand Up @@ -380,6 +408,18 @@ func (app *DotWeb) DefaultHTTPErrorHandler(ctx Context, err error) {
}
}

// DefaultNotFoundHandler default exception handler
func (app *DotWeb) DefaultNotFoundHandler(ctx Context) {
ctx.Response().Header().Set(HeaderContentType, CharsetUTF8)
ctx.WriteStringC(http.StatusNotFound, http.StatusText(http.StatusNotFound))
}

// DefaultNotFoundHandler default exception handler
func (app *DotWeb) DefaultMethodNotAllowedHandler(ctx Context) {
ctx.Response().Header().Set(HeaderContentType, CharsetUTF8)
ctx.WriteStringC(http.StatusMethodNotAllowed, http.StatusText(http.StatusMethodNotAllowed))
}

// Close immediately stops the server.
// It internally calls `http.Server#Close()`.
func (app *DotWeb) Close() error {
Expand Down
Loading

0 comments on commit 671380e

Please sign in to comment.