docs(handler/tunnel): add architecture documentation and fix observeStats event loss

- Add package-level architecture doc in handler.go covering:
  - NAT traversal reverse proxy architecture
  - CmdBind/CmdConnect roles and data flow
  - Connector lifecycle and waitClose semantics
  - Entrypoint protocol dispatch (first-byte sniffing)
  - SD fallback behavior
- Add data-flow-oriented doc comments to all business files:
  - entrypoint.go: protocol dispatch, dial flow
  - connector.go: Connector/ConnectorPool semantics
  - dialer.go: two-phase dial strategy
  - tunnel.go: MaxWeight semantics, selection algorithm
  - bind.go: 6-step CmdBind flow
  - connect.go: CmdConnect flow with relay framing
  - ephttp.go, eptls.go, eprelay.go: per-protocol entrypoint flow
- Fix observeStats: after successful error retry, also flush new events
  instead of skipping the current tick (handler.go:261-271)
- Add test for observeStats retry-then-flush (handler_test.go)
This commit is contained in:
ginuerzh
2026-06-02 18:36:52 +08:00
parent e791ba47e2
commit 31878a72ec
12 changed files with 264 additions and 3 deletions
+20
View File
@@ -10,10 +10,30 @@ import (
"github.com/go-gost/x/selector"
)
// MaxWeight is the exclusive takeover weight (255). A connector at MaxWeight
// triggers rw.Reset() in GetConnector, clearing all previously added connectors
// so that only this one is selected. If that MaxWeight connector later dies
// (IsClosed), GetConnector returns nil until Tunnel.clean() removes it.
const (
MaxWeight uint8 = 0xff
)
// Tunnel groups Connectors that share the same tunnel ID.
//
// Connector selection (GetConnector):
// - Single connector: fast path, no weighted random.
// - Multiple connectors: weighted random selection by network type (tcp/udp).
// - MaxWeight (255): exclusive — triggers Reset() and only the MaxWeight
// connector is added. If the MaxWeight connector closes, selection returns
// nil until clean() removes it.
//
// Tunnel lifecycle:
// - NewTunnel starts a clean() goroutine that runs every TTL (default 15s).
// Each tick removes closed connectors and renews SD registrations.
// A tunnel with 0 connectors gets cleaned up.
// - CloseOnIdle closes the tunnel if it has 0 connectors (called by
// ConnectorPool.closeIdles on a 15-minute ticker).
// - Close() closes all connectors and signals shutdown.
type Tunnel struct {
node string
id relay.TunnelID