Add /:stats support

This commit is contained in:
Igor Chubin 2022-11-27 22:16:32 +01:00
parent 8fd712f790
commit ec264850a4
2 changed files with 116 additions and 40 deletions

View file

@ -28,6 +28,8 @@ type RequestProcessor struct {
peakRequest30 sync.Map
peakRequest60 sync.Map
lruCache *lru.Cache
stats *Stats
router Router
}
// NewRequestProcessor returns new RequestProcessor.
@ -37,9 +39,15 @@ func NewRequestProcessor() (*RequestProcessor, error) {
return nil, err
}
return &RequestProcessor{
rp := &RequestProcessor{
lruCache: lruCache,
}, nil
stats: NewStats(),
}
// Initialize routes.
rp.router.AddPath("/:stats", rp.stats)
return rp, nil
}
// Start starts async request processor jobs, such as peak handling.
@ -53,11 +61,23 @@ func (rp *RequestProcessor) ProcessRequest(r *http.Request) (*ResponseWithHeader
err error
)
rp.stats.Inc("total")
// Main routing logic.
if rh := rp.router.Route(r); rh != nil {
result := rh.Response(r)
if result != nil {
return result, nil
}
}
if resp, ok := redirectInsecure(r); ok {
rp.stats.Inc("redirects")
return resp, nil
}
if dontCache(r) {
rp.stats.Inc("uncached")
return get(r)
}
@ -69,6 +89,7 @@ func (rp *RequestProcessor) ProcessRequest(r *http.Request) (*ResponseWithHeader
cacheBody, ok := rp.lruCache.Get(cacheDigest)
if ok {
rp.stats.Inc("cache1")
cacheEntry := cacheBody.(ResponseWithHeader)
// if after all attempts we still have no answer,
@ -93,7 +114,17 @@ func (rp *RequestProcessor) ProcessRequest(r *http.Request) (*ResponseWithHeader
}
if !foundInCache {
// Handling query.
format := r.URL.Query().Get("format")
if len(format) != 0 {
rp.stats.Inc("format")
if format == "j1" {
rp.stats.Inc("format=j1")
}
}
rp.lruCache.Add(cacheDigest, ResponseWithHeader{InProgress: true})
response, err = get(r)
if err != nil {
return nil, err

View file

@ -1,40 +1,85 @@
package main
// import (
// "log"
// "sync"
// "time"
// )
//
// type safeCounter struct {
// v map[int]int
// mux sync.Mutex
// }
//
// func (c *safeCounter) inc(key int) {
// c.mux.Lock()
// c.v[key]++
// c.mux.Unlock()
// }
//
// // func (c *safeCounter) val(key int) int {
// // c.mux.Lock()
// // defer c.mux.Unlock()
// // return c.v[key]
// // }
// //
// // func (c *safeCounter) reset(key int) int {
// // c.mux.Lock()
// // defer c.mux.Unlock()
// // result := c.v[key]
// // c.v[key] = 0
// // return result
// // }
//
// var queriesPerMinute safeCounter
//
// func printStat() {
// _, min, _ := time.Now().Clock()
// queriesPerMinute.inc(min)
// log.Printf("Processed %d requests\n", min)
// }
import (
"bytes"
"fmt"
"net/http"
"sync"
"time"
)
// Stats holds processed requests statistics.
type Stats struct {
m sync.Mutex
v map[string]int
startTime time.Time
}
// NewStats returns new Stats.
func NewStats() *Stats {
return &Stats{
v: map[string]int{},
startTime: time.Now(),
}
}
// Inc key by one.
func (c *Stats) Inc(key string) {
c.m.Lock()
c.v[key]++
c.m.Unlock()
}
// Get current key counter value.
func (c *Stats) Get(key string) int {
c.m.Lock()
defer c.m.Unlock()
return c.v[key]
}
// Reset key counter.
func (c *Stats) Reset(key string) int {
c.m.Lock()
defer c.m.Unlock()
result := c.v[key]
c.v[key] = 0
return result
}
// Show returns current statistics formatted as []byte.
func (c *Stats) Show() []byte {
var (
b bytes.Buffer
)
c.m.Lock()
defer c.m.Unlock()
uptime := time.Since(c.startTime) / time.Second
fmt.Fprintf(&b, "%-20s: %v\n", "Running since", c.startTime.Format(time.RFC3339))
fmt.Fprintf(&b, "%-20s: %d\n", "Uptime (min)", uptime/60)
fmt.Fprintf(&b, "%-20s: %d\n", "Total queries", c.v["total"])
if uptime != 0 {
fmt.Fprintf(&b, "%-20s: %d\n", "Throughput (QpM)", c.v["total"]*60/int(uptime))
}
fmt.Fprintf(&b, "%-20s: %d\n", "Cache L1 queries", c.v["cache1"])
if c.v["total"] != 0 {
fmt.Fprintf(&b, "%-20s: %d\n", "Cache L1 queries (%)", (100*c.v["cache1"])/c.v["total"])
}
fmt.Fprintf(&b, "%-20s: %d\n", "Upstream queries", c.v["total"]-c.v["cache1"])
fmt.Fprintf(&b, "%-20s: %d\n", "Queries with format", c.v["format"])
fmt.Fprintf(&b, "%-20s: %d\n", "Queries with format=j1", c.v["format=j1"])
return b.Bytes()
}
func (c *Stats) Response(*http.Request) *ResponseWithHeader {
return &ResponseWithHeader{
Body: c.Show(),
StatusCode: 200,
}
}