add stats support for tunnel handler
This commit is contained in:
@@ -8,6 +8,7 @@ import (
|
|||||||
|
|
||||||
"github.com/go-gost/core/ingress"
|
"github.com/go-gost/core/ingress"
|
||||||
"github.com/go-gost/core/logger"
|
"github.com/go-gost/core/logger"
|
||||||
|
"github.com/go-gost/core/observer/stats"
|
||||||
"github.com/go-gost/core/sd"
|
"github.com/go-gost/core/sd"
|
||||||
"github.com/go-gost/relay"
|
"github.com/go-gost/relay"
|
||||||
"github.com/go-gost/x/internal/util/mux"
|
"github.com/go-gost/x/internal/util/mux"
|
||||||
@@ -60,7 +61,11 @@ func (h *tunnelHandler) handleBind(ctx context.Context, conn net.Conn, network,
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
h.pool.Add(tunnelID, NewConnector(connectorID, tunnelID, h.id, session, h.md.sd), h.md.tunnelTTL)
|
var stats *stats.Stats
|
||||||
|
if h.stats != nil {
|
||||||
|
stats = h.stats.Stats(tunnelID.String())
|
||||||
|
}
|
||||||
|
h.pool.Add(tunnelID, NewConnector(connectorID, tunnelID, h.id, session, h.md.sd, stats), h.md.tunnelTTL)
|
||||||
if h.md.ingress != nil {
|
if h.md.ingress != nil {
|
||||||
h.md.ingress.SetRule(ctx, &ingress.Rule{
|
h.md.ingress.SetRule(ctx, &ingress.Rule{
|
||||||
Hostname: endpoint,
|
Hostname: endpoint,
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ func (d *Dialer) Dial(ctx context.Context, network string, tid string) (conn net
|
|||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
conn, err = c.Session().GetConn()
|
conn, err = c.GetConn()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
d.log.Error(err)
|
d.log.Error(err)
|
||||||
continue
|
continue
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ import (
|
|||||||
"github.com/go-gost/relay"
|
"github.com/go-gost/relay"
|
||||||
ctxvalue "github.com/go-gost/x/ctx"
|
ctxvalue "github.com/go-gost/x/ctx"
|
||||||
xnet "github.com/go-gost/x/internal/net"
|
xnet "github.com/go-gost/x/internal/net"
|
||||||
|
stats_util "github.com/go-gost/x/internal/util/stats"
|
||||||
xrecorder "github.com/go-gost/x/recorder"
|
xrecorder "github.com/go-gost/x/recorder"
|
||||||
"github.com/go-gost/x/registry"
|
"github.com/go-gost/x/registry"
|
||||||
xservice "github.com/go-gost/x/service"
|
xservice "github.com/go-gost/x/service"
|
||||||
@@ -45,6 +46,8 @@ type tunnelHandler struct {
|
|||||||
ep *entrypoint
|
ep *entrypoint
|
||||||
md metadata
|
md metadata
|
||||||
log logger.Logger
|
log logger.Logger
|
||||||
|
stats *stats_util.HandlerStats
|
||||||
|
cancel context.CancelFunc
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewHandler(opts ...handler.Option) handler.Handler {
|
func NewHandler(opts ...handler.Option) handler.Handler {
|
||||||
@@ -55,6 +58,7 @@ func NewHandler(opts ...handler.Option) handler.Handler {
|
|||||||
|
|
||||||
return &tunnelHandler{
|
return &tunnelHandler{
|
||||||
options: options,
|
options: options,
|
||||||
|
stats: stats_util.NewHandlerStats(options.Service),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -97,6 +101,13 @@ func (h *tunnelHandler) Init(md md.Metadata) (err error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
|
h.cancel = cancel
|
||||||
|
|
||||||
|
if h.options.Observer != nil {
|
||||||
|
go h.observeStats(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -268,6 +279,11 @@ func (h *tunnelHandler) Close() error {
|
|||||||
h.epSvc.Close()
|
h.epSvc.Close()
|
||||||
}
|
}
|
||||||
h.pool.Close()
|
h.pool.Close()
|
||||||
|
|
||||||
|
if h.cancel != nil {
|
||||||
|
h.cancel()
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -282,3 +298,25 @@ func (h *tunnelHandler) checkRateLimit(addr net.Addr) bool {
|
|||||||
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (h *tunnelHandler) observeStats(ctx context.Context) {
|
||||||
|
if h.options.Observer == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
d := h.md.observePeriod
|
||||||
|
if d < time.Millisecond {
|
||||||
|
d = 5 * time.Second
|
||||||
|
}
|
||||||
|
ticker := time.NewTicker(d)
|
||||||
|
defer ticker.Stop()
|
||||||
|
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-ticker.C:
|
||||||
|
h.options.Observer.Observe(ctx, h.stats.Events())
|
||||||
|
case <-ctx.Done():
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ type metadata struct {
|
|||||||
ingress ingress.Ingress
|
ingress ingress.Ingress
|
||||||
sd sd.SD
|
sd sd.SD
|
||||||
muxCfg *mux.Config
|
muxCfg *mux.Config
|
||||||
|
observePeriod time.Duration
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *tunnelHandler) parseMetadata(md mdata.Metadata) (err error) {
|
func (h *tunnelHandler) parseMetadata(md mdata.Metadata) (err error) {
|
||||||
@@ -81,5 +82,7 @@ func (h *tunnelHandler) parseMetadata(md mdata.Metadata) (err error) {
|
|||||||
h.md.muxCfg.Version = 2
|
h.md.muxCfg.Version = 2
|
||||||
}
|
}
|
||||||
|
|
||||||
|
h.md.observePeriod = mdutil.GetDuration(md, "observePeriod")
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
+43
-22
@@ -2,6 +2,7 @@ package tunnel
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"net"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@@ -9,6 +10,9 @@ import (
|
|||||||
"github.com/go-gost/core/sd"
|
"github.com/go-gost/core/sd"
|
||||||
"github.com/go-gost/relay"
|
"github.com/go-gost/relay"
|
||||||
"github.com/go-gost/x/internal/util/mux"
|
"github.com/go-gost/x/internal/util/mux"
|
||||||
|
|
||||||
|
"github.com/go-gost/core/observer/stats"
|
||||||
|
stats_wrapper "github.com/go-gost/x/observer/stats/wrapper"
|
||||||
"github.com/go-gost/x/selector"
|
"github.com/go-gost/x/selector"
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
)
|
)
|
||||||
@@ -18,22 +22,24 @@ const (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type Connector struct {
|
type Connector struct {
|
||||||
id relay.ConnectorID
|
id relay.ConnectorID
|
||||||
tid relay.TunnelID
|
tid relay.TunnelID
|
||||||
node string
|
node string
|
||||||
sd sd.SD
|
sd sd.SD
|
||||||
t time.Time
|
t time.Time
|
||||||
s *mux.Session
|
s *mux.Session
|
||||||
|
stats *stats.Stats
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewConnector(id relay.ConnectorID, tid relay.TunnelID, node string, s *mux.Session, sd sd.SD) *Connector {
|
func NewConnector(id relay.ConnectorID, tid relay.TunnelID, node string, s *mux.Session, sd sd.SD, stats *stats.Stats) *Connector {
|
||||||
c := &Connector{
|
c := &Connector{
|
||||||
id: id,
|
id: id,
|
||||||
tid: tid,
|
tid: tid,
|
||||||
node: node,
|
node: node,
|
||||||
sd: sd,
|
sd: sd,
|
||||||
t: time.Now(),
|
stats: stats,
|
||||||
s: s,
|
t: time.Now(),
|
||||||
|
s: s,
|
||||||
}
|
}
|
||||||
go c.accept()
|
go c.accept()
|
||||||
return c
|
return c
|
||||||
@@ -62,8 +68,20 @@ func (c *Connector) ID() relay.ConnectorID {
|
|||||||
return c.id
|
return c.id
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Connector) Session() *mux.Session {
|
func (c *Connector) GetConn() (net.Conn, error) {
|
||||||
return c.s
|
if c == nil || c.s == nil {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
conn, err := c.s.GetConn()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if c.stats != nil {
|
||||||
|
conn = stats_wrapper.WrapConn(conn, c.stats)
|
||||||
|
}
|
||||||
|
return conn, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Connector) Close() error {
|
func (c *Connector) Close() error {
|
||||||
@@ -74,6 +92,14 @@ func (c *Connector) Close() error {
|
|||||||
return c.s.Close()
|
return c.s.Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *Connector) IsClosed() bool {
|
||||||
|
if c == nil || c.s == nil {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.s.IsClosed()
|
||||||
|
}
|
||||||
|
|
||||||
type Tunnel struct {
|
type Tunnel struct {
|
||||||
node string
|
node string
|
||||||
id relay.TunnelID
|
id relay.TunnelID
|
||||||
@@ -83,7 +109,6 @@ type Tunnel struct {
|
|||||||
mu sync.RWMutex
|
mu sync.RWMutex
|
||||||
sd sd.SD
|
sd sd.SD
|
||||||
ttl time.Duration
|
ttl time.Duration
|
||||||
// rw *selector.RandomWeighted[*Connector]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewTunnel(node string, tid relay.TunnelID, ttl time.Duration) *Tunnel {
|
func NewTunnel(node string, tid relay.TunnelID, ttl time.Duration) *Tunnel {
|
||||||
@@ -93,7 +118,6 @@ func NewTunnel(node string, tid relay.TunnelID, ttl time.Duration) *Tunnel {
|
|||||||
t: time.Now(),
|
t: time.Now(),
|
||||||
close: make(chan struct{}),
|
close: make(chan struct{}),
|
||||||
ttl: ttl,
|
ttl: ttl,
|
||||||
// rw: selector.NewRandomWeighted[*Connector](),
|
|
||||||
}
|
}
|
||||||
if t.ttl <= 0 {
|
if t.ttl <= 0 {
|
||||||
t.ttl = defaultTTL
|
t.ttl = defaultTTL
|
||||||
@@ -125,9 +149,6 @@ func (t *Tunnel) GetConnector(network string) *Connector {
|
|||||||
t.mu.RLock()
|
t.mu.RLock()
|
||||||
defer t.mu.RUnlock()
|
defer t.mu.RUnlock()
|
||||||
|
|
||||||
// rw := t.rw
|
|
||||||
// rw.Reset()
|
|
||||||
|
|
||||||
if len(t.connectors) == 1 {
|
if len(t.connectors) == 1 {
|
||||||
return t.connectors[0]
|
return t.connectors[0]
|
||||||
}
|
}
|
||||||
@@ -136,7 +157,7 @@ func (t *Tunnel) GetConnector(network string) *Connector {
|
|||||||
|
|
||||||
found := false
|
found := false
|
||||||
for _, c := range t.connectors {
|
for _, c := range t.connectors {
|
||||||
if c.Session().IsClosed() {
|
if c.IsClosed() {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -206,7 +227,7 @@ func (t *Tunnel) clean() {
|
|||||||
}
|
}
|
||||||
var connectors []*Connector
|
var connectors []*Connector
|
||||||
for _, c := range t.connectors {
|
for _, c := range t.connectors {
|
||||||
if c.Session().IsClosed() {
|
if c.IsClosed() {
|
||||||
logger.Default().Debugf("remove tunnel: %s, connector: %s", t.id, c.id)
|
logger.Default().Debugf("remove tunnel: %s, connector: %s", t.id, c.id)
|
||||||
if t.sd != nil {
|
if t.sd != nil {
|
||||||
t.sd.Deregister(context.Background(), &sd.Service{
|
t.sd.Deregister(context.Background(), &sd.Service{
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"io"
|
"io"
|
||||||
"net"
|
"net"
|
||||||
|
"sync"
|
||||||
"syscall"
|
"syscall"
|
||||||
|
|
||||||
"github.com/go-gost/core/metadata"
|
"github.com/go-gost/core/metadata"
|
||||||
@@ -18,17 +19,23 @@ var (
|
|||||||
|
|
||||||
type conn struct {
|
type conn struct {
|
||||||
net.Conn
|
net.Conn
|
||||||
stats *stats.Stats
|
stats *stats.Stats
|
||||||
|
closed chan struct{}
|
||||||
|
mu sync.Mutex
|
||||||
}
|
}
|
||||||
|
|
||||||
func WrapConn(c net.Conn, stats *stats.Stats) net.Conn {
|
func WrapConn(c net.Conn, pStats *stats.Stats) net.Conn {
|
||||||
if stats == nil {
|
if pStats == nil {
|
||||||
return c
|
return c
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pStats.Add(stats.KindTotalConns, 1)
|
||||||
|
pStats.Add(stats.KindCurrentConns, 1)
|
||||||
|
|
||||||
return &conn{
|
return &conn{
|
||||||
Conn: c,
|
Conn: c,
|
||||||
stats: stats,
|
stats: pStats,
|
||||||
|
closed: make(chan struct{}),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -44,6 +51,21 @@ func (c *conn) Write(b []byte) (n int, err error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *conn) Close() error {
|
||||||
|
c.mu.Lock()
|
||||||
|
defer c.mu.Unlock()
|
||||||
|
|
||||||
|
select {
|
||||||
|
case <-c.closed:
|
||||||
|
return nil
|
||||||
|
default:
|
||||||
|
close(c.closed)
|
||||||
|
}
|
||||||
|
|
||||||
|
c.stats.Add(stats.KindCurrentConns, -1)
|
||||||
|
return c.Conn.Close()
|
||||||
|
}
|
||||||
|
|
||||||
func (c *conn) SyscallConn() (rc syscall.RawConn, err error) {
|
func (c *conn) SyscallConn() (rc syscall.RawConn, err error) {
|
||||||
if sc, ok := c.Conn.(syscall.Conn); ok {
|
if sc, ok := c.Conn.(syscall.Conn); ok {
|
||||||
rc, err = sc.SyscallConn()
|
rc, err = sc.SyscallConn()
|
||||||
|
|||||||
+2
-7
@@ -187,8 +187,6 @@ func (s *defaultService) Serve() error {
|
|||||||
s.setState(StateReady)
|
s.setState(StateReady)
|
||||||
}
|
}
|
||||||
|
|
||||||
s.status.stats.Add(stats.KindTotalConns, 1)
|
|
||||||
|
|
||||||
clientAddr := conn.RemoteAddr().String()
|
clientAddr := conn.RemoteAddr().String()
|
||||||
clientIP := clientAddr
|
clientIP := clientAddr
|
||||||
if h, _, _ := net.SplitHostPort(clientAddr); h != "" {
|
if h, _, _ := net.SplitHostPort(clientAddr); h != "" {
|
||||||
@@ -208,16 +206,13 @@ func (s *defaultService) Serve() error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if s.options.admission != nil &&
|
if s.options.admission != nil &&
|
||||||
!s.options.admission.Admit(ctx, conn.RemoteAddr().String()) {
|
!s.options.admission.Admit(ctx, clientAddr) {
|
||||||
conn.Close()
|
conn.Close()
|
||||||
s.options.logger.Debugf("admission: %s is denied", conn.RemoteAddr())
|
s.options.logger.Debugf("admission: %s is denied", clientAddr)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
s.status.stats.Add(stats.KindCurrentConns, 1)
|
|
||||||
defer s.status.stats.Add(stats.KindCurrentConns, -1)
|
|
||||||
|
|
||||||
if v := xmetrics.GetCounter(xmetrics.MetricServiceRequestsCounter,
|
if v := xmetrics.GetCounter(xmetrics.MetricServiceRequestsCounter,
|
||||||
metrics.Labels{"service": s.name, "client": clientIP}); v != nil {
|
metrics.Labels{"service": s.name, "client": clientIP}); v != nil {
|
||||||
v.Inc()
|
v.Inc()
|
||||||
|
|||||||
Reference in New Issue
Block a user