page.go 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171
  1. package cache
  2. import (
  3. "bytes"
  4. "crypto/sha1"
  5. "io"
  6. "net/http"
  7. "net/url"
  8. "sync"
  9. "go-common/library/log"
  10. bm "go-common/library/net/http/blademaster"
  11. "go-common/library/net/http/blademaster/middleware/cache/store"
  12. proto "github.com/gogo/protobuf/proto"
  13. )
  14. // consts for blademaster cache
  15. const (
  16. _pagePrefix = "bm.page"
  17. )
  18. // Page is used to cache common response
  19. type Page struct {
  20. Expire int32
  21. pool sync.Pool
  22. }
  23. type cachedWriter struct {
  24. ctx *bm.Context
  25. response http.ResponseWriter
  26. store store.Store
  27. status int
  28. expire int32
  29. key string
  30. }
  31. var _ http.ResponseWriter = &cachedWriter{}
  32. // NewPage will create a new page cache struct
  33. func NewPage(expire int32) *Page {
  34. pc := &Page{
  35. Expire: expire,
  36. }
  37. pc.pool.New = func() interface{} {
  38. return &cachedWriter{}
  39. }
  40. return pc
  41. }
  42. // Key is used to identify response cache key in most key-value store
  43. func (p *Page) Key(ctx *bm.Context) string {
  44. url := ctx.Request.URL
  45. key := urlEscape(_pagePrefix, url.RequestURI())
  46. return key
  47. }
  48. // Handler is used to execute cache service
  49. func (p *Page) Handler(store store.Store) bm.HandlerFunc {
  50. return func(ctx *bm.Context) {
  51. var (
  52. resp *ResponseCache
  53. cached []byte
  54. err error
  55. )
  56. key := p.Key(ctx)
  57. cached, err = store.Get(ctx, key)
  58. // if we did got the previous cache,
  59. // try to unmarshal it
  60. if err == nil && len(cached) > 0 {
  61. resp = new(ResponseCache)
  62. err = proto.Unmarshal(cached, resp)
  63. }
  64. // if we failed to fetch the cache or failed to parse cached data,
  65. // then consider try to cache this response
  66. if err != nil || resp == nil {
  67. writer := p.pool.Get().(*cachedWriter)
  68. writer.ctx = ctx
  69. writer.response = ctx.Writer
  70. writer.key = key
  71. writer.expire = p.Expire
  72. writer.store = store
  73. ctx.Writer = writer
  74. ctx.Next()
  75. p.pool.Put(writer)
  76. return
  77. }
  78. // write cached response
  79. headers := ctx.Writer.Header()
  80. for key, value := range resp.Header {
  81. headers[key] = value.Value
  82. }
  83. ctx.Writer.WriteHeader(int(resp.Status))
  84. ctx.Writer.Write(resp.Data)
  85. ctx.Abort()
  86. }
  87. }
  88. func (w *cachedWriter) Header() http.Header {
  89. return w.response.Header()
  90. }
  91. func (w *cachedWriter) WriteHeader(code int) {
  92. w.status = int(code)
  93. w.response.WriteHeader(code)
  94. }
  95. func (w *cachedWriter) Write(data []byte) (size int, err error) {
  96. var (
  97. origin []byte
  98. pdata []byte
  99. )
  100. if size, err = w.response.Write(data); err != nil {
  101. return
  102. }
  103. store := w.store
  104. origin, err = store.Get(w.ctx, w.key)
  105. resp := new(ResponseCache)
  106. if err == nil || len(origin) > 0 {
  107. err1 := proto.Unmarshal(origin, resp)
  108. if err1 == nil {
  109. data = append(resp.Data, data...)
  110. }
  111. }
  112. resp.Status = int32(w.status)
  113. resp.Header = headerValues(w.Header())
  114. resp.Data = data
  115. if pdata, err = proto.Marshal(resp); err != nil {
  116. // cannot happen
  117. log.Error("Failed to marshal response to protobuf: %v", err)
  118. return
  119. }
  120. if err = store.Set(w.ctx, w.key, pdata, w.expire); err != nil {
  121. log.Error("Failed to set response cache: %v", err)
  122. return
  123. }
  124. return
  125. }
  126. func headerValues(headers http.Header) map[string]*HeaderValue {
  127. result := make(map[string]*HeaderValue, len(headers))
  128. for key, values := range headers {
  129. result[key] = &HeaderValue{
  130. Value: values,
  131. }
  132. }
  133. return result
  134. }
  135. func urlEscape(prefix string, u string) string {
  136. key := url.QueryEscape(u)
  137. if len(key) > 200 {
  138. h := sha1.New()
  139. io.WriteString(h, u)
  140. key = string(h.Sum(nil))
  141. }
  142. var buffer bytes.Buffer
  143. buffer.WriteString(prefix)
  144. buffer.WriteString(":")
  145. buffer.WriteString(key)
  146. return buffer.String()
  147. }