1
0
mirror of https://github.com/astaxie/beego.git synced 2024-11-05 01:00:55 +00:00

Merge pull request #1808 from JessonChan/gzip_improve

Gzip improve
This commit is contained in:
astaxie 2016-03-22 17:13:20 +08:00
commit d3b54c46e3
4 changed files with 75 additions and 28 deletions

View File

@ -51,6 +51,7 @@ func AddAPPStartHook(hf hookfunc) {
// beego.Run(":8089") // beego.Run(":8089")
// beego.Run("127.0.0.1:8089") // beego.Run("127.0.0.1:8089")
func Run(params ...string) { func Run(params ...string) {
initBeforeHTTPRun() initBeforeHTTPRun()
if len(params) > 0 && params[0] != "" { if len(params) > 0 && params[0] != "" {
@ -74,6 +75,7 @@ func initBeforeHTTPRun() {
AddAPPStartHook(registerDocs) AddAPPStartHook(registerDocs)
AddAPPStartHook(registerTemplate) AddAPPStartHook(registerTemplate)
AddAPPStartHook(registerAdmin) AddAPPStartHook(registerAdmin)
AddAPPStartHook(registerGzip)
for _, hk := range hooks { for _, hk := range hooks {
if err := hk(); err != nil { if err := hk(); err != nil {

View File

@ -353,7 +353,7 @@ func (b *beegoAppConfig) String(key string) string {
} }
func (b *beegoAppConfig) Strings(key string) []string { func (b *beegoAppConfig) Strings(key string) []string {
if v := b.innerConfig.Strings(BConfig.RunMode + "::" + key); v[0] != "" { if v := b.innerConfig.Strings(BConfig.RunMode + "::" + key); len(v) > 0 {
return v return v
} }
return b.innerConfig.Strings(key) return b.innerConfig.Strings(key)

View File

@ -27,6 +27,33 @@ import (
"sync" "sync"
) )
var (
//Default size==20B same as nginx
defaultGzipMinLength = 20
//Content will only be compressed if content length is either unknown or greater than gzipMinLength.
gzipMinLength = defaultGzipMinLength
//The compression level used for deflate compression. (0-9).
gzipCompressLevel int
//List of HTTP methods to compress. If not set, only GET requests are compressed.
includedMethods map[string]bool
getMethodOnly bool
)
func InitGzip(minLength, compressLevel int, methods []string) {
if minLength >= 0 {
gzipMinLength = minLength
}
gzipCompressLevel = compressLevel
if gzipCompressLevel < flate.NoCompression || gzipCompressLevel > flate.BestCompression {
gzipCompressLevel = flate.BestSpeed
}
getMethodOnly = (len(methods) == 0) || (len(methods) == 1 && strings.ToUpper(methods[0]) == "GET")
includedMethods = make(map[string]bool, len(methods))
for _, v := range methods {
includedMethods[strings.ToUpper(v)] = true
}
}
type resetWriter interface { type resetWriter interface {
io.Writer io.Writer
Reset(w io.Writer) Reset(w io.Writer)
@ -41,20 +68,20 @@ func (n nopResetWriter) Reset(w io.Writer) {
} }
type acceptEncoder struct { type acceptEncoder struct {
name string name string
levelEncode func(int) resetWriter levelEncode func(int) resetWriter
bestSpeedPool *sync.Pool customCompressLevelPool *sync.Pool
bestCompressionPool *sync.Pool bestCompressionPool *sync.Pool
} }
func (ac acceptEncoder) encode(wr io.Writer, level int) resetWriter { func (ac acceptEncoder) encode(wr io.Writer, level int) resetWriter {
if ac.bestSpeedPool == nil || ac.bestCompressionPool == nil { if ac.customCompressLevelPool == nil || ac.bestCompressionPool == nil {
return nopResetWriter{wr} return nopResetWriter{wr}
} }
var rwr resetWriter var rwr resetWriter
switch level { switch level {
case flate.BestSpeed: case flate.BestSpeed:
rwr = ac.bestSpeedPool.Get().(resetWriter) rwr = ac.customCompressLevelPool.Get().(resetWriter)
case flate.BestCompression: case flate.BestCompression:
rwr = ac.bestCompressionPool.Get().(resetWriter) rwr = ac.bestCompressionPool.Get().(resetWriter)
default: default:
@ -65,13 +92,18 @@ func (ac acceptEncoder) encode(wr io.Writer, level int) resetWriter {
} }
func (ac acceptEncoder) put(wr resetWriter, level int) { func (ac acceptEncoder) put(wr resetWriter, level int) {
if ac.bestSpeedPool == nil || ac.bestCompressionPool == nil { if ac.customCompressLevelPool == nil || ac.bestCompressionPool == nil {
return return
} }
wr.Reset(nil) wr.Reset(nil)
//notice
//compressionLevel==BestCompression DOES NOT MATTER
//sync.Pool will not memory leak
switch level { switch level {
case flate.BestSpeed: case gzipCompressLevel:
ac.bestSpeedPool.Put(wr) ac.customCompressLevelPool.Put(wr)
case flate.BestCompression: case flate.BestCompression:
ac.bestCompressionPool.Put(wr) ac.bestCompressionPool.Put(wr)
} }
@ -79,28 +111,22 @@ func (ac acceptEncoder) put(wr resetWriter, level int) {
var ( var (
noneCompressEncoder = acceptEncoder{"", nil, nil, nil} noneCompressEncoder = acceptEncoder{"", nil, nil, nil}
gzipCompressEncoder = acceptEncoder{"gzip", gzipCompressEncoder = acceptEncoder{
func(level int) resetWriter { wr, _ := gzip.NewWriterLevel(nil, level); return wr }, name: "gzip",
&sync.Pool{ levelEncode: func(level int) resetWriter { wr, _ := gzip.NewWriterLevel(nil, level); return wr },
New: func() interface{} { wr, _ := gzip.NewWriterLevel(nil, flate.BestSpeed); return wr }, customCompressLevelPool: &sync.Pool{New: func() interface{} { wr, _ := gzip.NewWriterLevel(nil, gzipCompressLevel); return wr }},
}, bestCompressionPool: &sync.Pool{New: func() interface{} { wr, _ := gzip.NewWriterLevel(nil, flate.BestCompression); return wr }},
&sync.Pool{
New: func() interface{} { wr, _ := gzip.NewWriterLevel(nil, flate.BestCompression); return wr },
},
} }
//according to the sec :http://tools.ietf.org/html/rfc2616#section-3.5 ,the deflate compress in http is zlib indeed //according to the sec :http://tools.ietf.org/html/rfc2616#section-3.5 ,the deflate compress in http is zlib indeed
//deflate //deflate
//The "zlib" format defined in RFC 1950 [31] in combination with //The "zlib" format defined in RFC 1950 [31] in combination with
//the "deflate" compression mechanism described in RFC 1951 [29]. //the "deflate" compression mechanism described in RFC 1951 [29].
deflateCompressEncoder = acceptEncoder{"deflate", deflateCompressEncoder = acceptEncoder{
func(level int) resetWriter { wr, _ := zlib.NewWriterLevel(nil, level); return wr }, name: "deflate",
&sync.Pool{ levelEncode: func(level int) resetWriter { wr, _ := zlib.NewWriterLevel(nil, level); return wr },
New: func() interface{} { wr, _ := zlib.NewWriterLevel(nil, flate.BestSpeed); return wr }, customCompressLevelPool: &sync.Pool{New: func() interface{} { wr, _ := zlib.NewWriterLevel(nil, gzipCompressLevel); return wr }},
}, bestCompressionPool: &sync.Pool{New: func() interface{} { wr, _ := zlib.NewWriterLevel(nil, flate.BestCompression); return wr }},
&sync.Pool{
New: func() interface{} { wr, _ := zlib.NewWriterLevel(nil, flate.BestCompression); return wr },
},
} }
) )
@ -120,7 +146,11 @@ func WriteFile(encoding string, writer io.Writer, file *os.File) (bool, string,
// WriteBody reads writes content to writer by the specific encoding(gzip/deflate) // WriteBody reads writes content to writer by the specific encoding(gzip/deflate)
func WriteBody(encoding string, writer io.Writer, content []byte) (bool, string, error) { func WriteBody(encoding string, writer io.Writer, content []byte) (bool, string, error) {
return writeLevel(encoding, writer, bytes.NewReader(content), flate.BestSpeed) if encoding == "" || len(content) < gzipMinLength {
_, err := writer.Write(content)
return false, "", err
}
return writeLevel(encoding, writer, bytes.NewReader(content), gzipCompressLevel)
} }
// writeLevel reads from reader,writes to writer by specific encoding and compress level // writeLevel reads from reader,writes to writer by specific encoding and compress level
@ -156,7 +186,10 @@ func ParseEncoding(r *http.Request) string {
if r == nil { if r == nil {
return "" return ""
} }
return parseEncoding(r) if (getMethodOnly && r.Method == "GET") || includedMethods[r.Method] {
return parseEncoding(r)
}
return ""
} }
type q struct { type q struct {

View File

@ -6,6 +6,7 @@ import (
"net/http" "net/http"
"path/filepath" "path/filepath"
"github.com/astaxie/beego/context"
"github.com/astaxie/beego/session" "github.com/astaxie/beego/session"
) )
@ -91,3 +92,14 @@ func registerAdmin() error {
} }
return nil return nil
} }
func registerGzip() error {
if BConfig.EnableGzip {
context.InitGzip(
AppConfig.DefaultInt("gzipMinLength", -1),
AppConfig.DefaultInt("gzipCompressLevel", -1),
AppConfig.DefaultStrings("includedMethods", []string{"GET"}),
)
}
return nil
}