mirror of
https://github.com/smallnest/rpcx.git
synced 2025-09-26 20:21:14 +08:00
add statsview
This commit is contained in:
@@ -6,6 +6,7 @@
|
||||
- support io_uring
|
||||
- add CacheDiscovery
|
||||
- add Oneshot method for XClient
|
||||
- add statsview: http://xxx.xxx.xxx.xxx:xxxx/debug/statsview
|
||||
|
||||
|
||||
## 1.8.0
|
||||
|
2
go.mod
2
go.mod
@@ -9,6 +9,7 @@ require (
|
||||
github.com/apache/thrift v0.18.1
|
||||
github.com/edwingeng/doublejump v1.0.1
|
||||
github.com/fatih/color v1.14.1
|
||||
github.com/go-echarts/go-echarts/v2 v2.3.2
|
||||
github.com/go-ping/ping v1.1.0
|
||||
github.com/go-redis/redis/v8 v8.11.5
|
||||
github.com/go-redis/redis_rate/v9 v9.1.2
|
||||
@@ -30,6 +31,7 @@ require (
|
||||
github.com/rs/cors v1.8.3
|
||||
github.com/rubyist/circuitbreaker v2.2.1+incompatible
|
||||
github.com/smallnest/quick v0.1.0
|
||||
github.com/smallnest/statsview v0.0.0-20231119053547-3d59443f9ae3
|
||||
github.com/soheilhy/cmux v0.1.5
|
||||
github.com/stretchr/testify v1.7.2
|
||||
github.com/tinylib/msgp v1.1.8
|
||||
|
4
go.sum
4
go.sum
@@ -58,6 +58,8 @@ github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMo
|
||||
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
|
||||
github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY=
|
||||
github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw=
|
||||
github.com/go-echarts/go-echarts/v2 v2.3.2 h1:imRxqF5sLtEPBsv5HGwz9KklNuwCo0fTITZ31mrgfzo=
|
||||
github.com/go-echarts/go-echarts/v2 v2.3.2/go.mod h1:56YlvzhW/a+du15f3S2qUGNDfKnFOeJSThBIrVFHDtI=
|
||||
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
||||
github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
||||
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
|
||||
@@ -263,6 +265,8 @@ github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPx
|
||||
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
|
||||
github.com/smallnest/quick v0.1.0 h1:7/a3mvWjBNSKpcwmuiizTi5Alcn7xRkHNNuKgRda1V8=
|
||||
github.com/smallnest/quick v0.1.0/go.mod h1:mjmFVnOUd/Ruq8Gb7wxFjweGsrVILFsKrcwLz4QyX3g=
|
||||
github.com/smallnest/statsview v0.0.0-20231119053547-3d59443f9ae3 h1:N7tssEnlhZlAEGQH+79XGYwrI1vuk5iVEdPiROKsQS4=
|
||||
github.com/smallnest/statsview v0.0.0-20231119053547-3d59443f9ae3/go.mod h1:BMgxQ/U/wQ/mJY8WyPxIG+e3cx7HKbKSZL+DniC2tvc=
|
||||
github.com/soheilhy/cmux v0.1.5 h1:jjzc5WVemNEDTLwv9tlmemhC73tI08BNOIGwBOo10Js=
|
||||
github.com/soheilhy/cmux v0.1.5/go.mod h1:T7TcVDs9LWfQgPlPsdngu6I6QIoyIFZDDC6sNE1GqG0=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
|
@@ -1,6 +1,7 @@
|
||||
package server
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"context"
|
||||
"errors"
|
||||
"io"
|
||||
@@ -38,8 +39,19 @@ func (s *Server) startGateway(network string, ln net.Listener) net.Listener {
|
||||
go s.startJSONRPC2(jsonrpc2Ln)
|
||||
}
|
||||
|
||||
if s.EnableProfile {
|
||||
// debugLn := m.Match(http1Path("/debug/"))
|
||||
debugLn := m.Match(cmux.HTTP1Fast())
|
||||
vm := NewViewManager(debugLn)
|
||||
go func() {
|
||||
if err := vm.Start(); err != nil {
|
||||
log.Errorf("start view manager failed: %v", err)
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
if !s.DisableHTTPGateway {
|
||||
httpLn := m.Match(cmux.HTTP1Fast())
|
||||
httpLn := m.Match(cmux.HTTP1Fast()) // X-RPCX-MessageID
|
||||
go s.startHTTP1APIGateway(httpLn)
|
||||
}
|
||||
|
||||
@@ -48,6 +60,67 @@ func (s *Server) startGateway(network string, ln net.Listener) net.Listener {
|
||||
return rpcxLn
|
||||
}
|
||||
|
||||
func http1Path(prefix string) cmux.Matcher {
|
||||
return func(r io.Reader) bool {
|
||||
return matchHTTP1Field(r, prefix, func(gotValue string) bool {
|
||||
br := bufio.NewReader(&io.LimitedReader{R: r, N: 1024})
|
||||
l, part, err := br.ReadLine()
|
||||
if err != nil || part {
|
||||
return false
|
||||
}
|
||||
|
||||
_, uri, _, ok := parseRequestLine(string(l))
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
|
||||
if strings.HasPrefix(uri, prefix) {
|
||||
return true
|
||||
}
|
||||
|
||||
u, err := url.Parse(uri)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
return strings.HasPrefix(u.Path, prefix)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// grabbed from net/http.
|
||||
func parseRequestLine(line string) (method, uri, proto string, ok bool) {
|
||||
s1 := strings.Index(line, " ")
|
||||
s2 := strings.Index(line[s1+1:], " ")
|
||||
if s1 < 0 || s2 < 0 {
|
||||
return
|
||||
}
|
||||
s2 += s1 + 1
|
||||
return line[:s1], line[s1+1 : s2], line[s2+1:], true
|
||||
}
|
||||
|
||||
func http1HeaderExist(name string) cmux.Matcher {
|
||||
return func(r io.Reader) bool {
|
||||
return matchHTTP1Field(r, name, func(gotValue string) bool {
|
||||
req, err := http.ReadRequest(bufio.NewReader(r))
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
return req.Header.Get(name) != ""
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func matchHTTP1Field(r io.Reader, name string, matches func(string) bool) (matched bool) {
|
||||
req, err := http.ReadRequest(bufio.NewReader(r))
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
return matches(req.Header.Get(name))
|
||||
}
|
||||
|
||||
func rpcxPrefixByteMatcher() cmux.Matcher {
|
||||
magic := protocol.MagicNumber()
|
||||
return func(r io.Reader) bool {
|
||||
|
@@ -84,6 +84,7 @@ type Server struct {
|
||||
jsonrpcHTTPServer *http.Server
|
||||
DisableHTTPGateway bool // disable http invoke or not.
|
||||
DisableJSONRPC bool // disable json rpc or not.
|
||||
EnableProfile bool // enable profile and statsview or not
|
||||
AsyncWrite bool // set true if your server only serves few clients
|
||||
pool WorkerPool
|
||||
|
||||
|
134
server/statsview.go
Normal file
134
server/statsview.go
Normal file
@@ -0,0 +1,134 @@
|
||||
package server
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/http/pprof"
|
||||
"time"
|
||||
|
||||
"github.com/go-echarts/go-echarts/v2/components"
|
||||
"github.com/go-echarts/go-echarts/v2/templates"
|
||||
"github.com/rs/cors"
|
||||
"github.com/smallnest/statsview/statics"
|
||||
"github.com/smallnest/statsview/viewer"
|
||||
)
|
||||
|
||||
// ViewManager
|
||||
type ViewManager struct {
|
||||
ln net.Listener
|
||||
srv *http.Server
|
||||
|
||||
Smgr *viewer.StatsMgr
|
||||
Ctx context.Context
|
||||
Cancel context.CancelFunc
|
||||
Views []viewer.Viewer
|
||||
}
|
||||
|
||||
// Register registers views to the ViewManager
|
||||
func (vm *ViewManager) Register(views ...viewer.Viewer) {
|
||||
vm.Views = append(vm.Views, views...)
|
||||
|
||||
}
|
||||
|
||||
// Start runs a http server and begin to collect metrics
|
||||
func (vm *ViewManager) Start() error {
|
||||
return vm.srv.Serve(vm.ln)
|
||||
}
|
||||
|
||||
// Stop shutdown the http server gracefully
|
||||
func (vm *ViewManager) Stop() {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
|
||||
defer cancel()
|
||||
vm.srv.Shutdown(ctx)
|
||||
vm.Cancel()
|
||||
}
|
||||
|
||||
func init() {
|
||||
templates.PageTpl = `
|
||||
{{- define "page" }}
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
{{- template "header" . }}
|
||||
<body>
|
||||
<p> rpcx profiler</em></p>
|
||||
<style> .box { justify-content:center; display:flex; flex-wrap:wrap } </style>
|
||||
<div class="box"> {{- range .Charts }} {{ template "base" . }} {{- end }} </div>
|
||||
</body>
|
||||
</html>
|
||||
{{ end }}
|
||||
`
|
||||
}
|
||||
|
||||
// NewViewManager creates a new ViewManager instance
|
||||
func NewViewManager(ln net.Listener) *ViewManager {
|
||||
viewer.SetConfiguration(viewer.WithAddr(ln.Addr().String()), viewer.WithLinkAddr(ln.Addr().String()))
|
||||
|
||||
page := components.NewPage()
|
||||
page.PageTitle = "Statsview"
|
||||
page.AssetsHost = fmt.Sprintf("http://%s/debug/statsview/statics/", viewer.LinkAddr())
|
||||
page.Assets.JSAssets.Add("jquery.min.js")
|
||||
|
||||
srv := &http.Server{
|
||||
ReadTimeout: time.Minute,
|
||||
WriteTimeout: time.Minute,
|
||||
MaxHeaderBytes: 1 << 20,
|
||||
}
|
||||
|
||||
mgr := &ViewManager{
|
||||
ln: ln,
|
||||
srv: srv,
|
||||
}
|
||||
|
||||
mgr.Ctx, mgr.Cancel = context.WithCancel(context.Background())
|
||||
mgr.Register(
|
||||
viewer.NewGoroutinesViewer(),
|
||||
viewer.NewHeapViewer(),
|
||||
viewer.NewStackViewer(),
|
||||
viewer.NewGCNumViewer(),
|
||||
viewer.NewGCSizeViewer(),
|
||||
viewer.NewGCCPUFractionViewer(),
|
||||
)
|
||||
smgr := viewer.NewStatsMgr(mgr.Ctx)
|
||||
for _, v := range mgr.Views {
|
||||
v.SetStatsMgr(smgr)
|
||||
}
|
||||
|
||||
mux := http.NewServeMux()
|
||||
mux.HandleFunc("/debug/pprof/", pprof.Index)
|
||||
mux.HandleFunc("/debug/pprof/cmdline", pprof.Cmdline)
|
||||
mux.HandleFunc("/debug/pprof/profile", pprof.Profile)
|
||||
mux.HandleFunc("/debug/pprof/symbol", pprof.Symbol)
|
||||
mux.HandleFunc("/debug/pprof/trace", pprof.Trace)
|
||||
|
||||
for _, v := range mgr.Views {
|
||||
page.AddCharts(v.View())
|
||||
mux.HandleFunc("/debug/statsview/view/"+v.Name(), v.Serve)
|
||||
}
|
||||
|
||||
mux.HandleFunc("/debug/statsview", func(w http.ResponseWriter, _ *http.Request) {
|
||||
page.Render(w)
|
||||
})
|
||||
|
||||
staticsPrev := "/debug/statsview/statics/"
|
||||
mux.HandleFunc(staticsPrev+"echarts.min.js", func(w http.ResponseWriter, _ *http.Request) {
|
||||
w.Write([]byte(statics.EchartJS))
|
||||
})
|
||||
|
||||
mux.HandleFunc(staticsPrev+"jquery.min.js", func(w http.ResponseWriter, _ *http.Request) {
|
||||
w.Write([]byte(statics.JqueryJS))
|
||||
})
|
||||
|
||||
mux.HandleFunc(staticsPrev+"themes/westeros.js", func(w http.ResponseWriter, _ *http.Request) {
|
||||
w.Write([]byte(statics.WesterosJS))
|
||||
})
|
||||
|
||||
mux.HandleFunc(staticsPrev+"themes/macarons.js", func(w http.ResponseWriter, _ *http.Request) {
|
||||
w.Write([]byte(statics.MacaronsJS))
|
||||
})
|
||||
|
||||
mgr.srv.Handler = cors.AllowAll().Handler(mux)
|
||||
|
||||
return mgr
|
||||
}
|
@@ -50,10 +50,6 @@ func (v *visitor) Visit(n ast.Node) (w ast.Visitor) {
|
||||
}
|
||||
return v
|
||||
case *ast.StructType:
|
||||
// if isExported(v.name) {
|
||||
//fmt.Printf("@@@@%s: %s\n", v.name, pretty.Sprint(n.Fields))
|
||||
//v.StructNames = append(v.StructNames, v.name)
|
||||
// }
|
||||
return nil
|
||||
case *ast.FuncDecl:
|
||||
if isExported(v.name) {
|
||||
|
Reference in New Issue
Block a user