PortRelay:把内网服务临时、可控地开放给同事
边缘网关 + Agent 的端口中继:Agent 主动连出到网关,把内网目标挂到网关端口上; 通过邀请链接按设备授权,可选审批,首次连接起计时,到期、闲置自动收回。 - portrelay-server:边缘网关(端口池、白名单与授权租约、邀请与授权网页) - portrelay-agent:Agent(yamux 隧道、本地 API 与网页面板、流量统计) - mac-native:SwiftUI 菜单栏 App
This commit is contained in:
commit
86e159b3f0
67 files changed
+13888
No files matched your search
@@ -0,0 +1,127 @@
|
||||
package client
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
rateStep = 5 * time.Second
|
||||
ratePoints = 120 // 10 分钟
|
||||
totalSeries = "__total__"
|
||||
)
|
||||
|
||||
// RateSeries is a fixed-length rate history (oldest first). The last point is the
|
||||
// current, still-accumulating bucket so the chart moves every second.
|
||||
type RateSeries struct {
|
||||
End int64 `json:"end"` // 最后一个点所在 5 秒桶的起始时间(Unix 秒)
|
||||
Step int `json:"step"` // 每个点的秒数
|
||||
Up []int64 `json:"up"` // 上行 bytes/s(客户端 → 目标)
|
||||
Down []int64 `json:"down"` // 下行 bytes/s(目标 → 客户端)
|
||||
}
|
||||
|
||||
type rateBucket struct {
|
||||
start time.Time
|
||||
up, down int64
|
||||
}
|
||||
|
||||
type rateHistory struct {
|
||||
done []rateBucket // completed buckets, oldest first, at most ratePoints
|
||||
curStart time.Time
|
||||
curUp, curDown int64
|
||||
curSamples int64
|
||||
lastSampleBucket time.Time
|
||||
}
|
||||
|
||||
func (h *rateHistory) add(now time.Time, up, down int64) {
|
||||
bs := now.Truncate(rateStep)
|
||||
if h.curStart.IsZero() {
|
||||
h.curStart = bs
|
||||
}
|
||||
if bs.After(h.curStart) {
|
||||
if h.curSamples > 0 {
|
||||
h.done = append(h.done, rateBucket{h.curStart, h.curUp / h.curSamples, h.curDown / h.curSamples})
|
||||
}
|
||||
if len(h.done) > ratePoints {
|
||||
h.done = h.done[len(h.done)-ratePoints:]
|
||||
}
|
||||
h.curStart, h.curUp, h.curDown, h.curSamples = bs, 0, 0, 0
|
||||
}
|
||||
h.curUp += up
|
||||
h.curDown += down
|
||||
h.curSamples++
|
||||
}
|
||||
|
||||
// series renders the last ratePoints buckets ending at now's bucket; missing buckets are zero
|
||||
func (h *rateHistory) series(now time.Time) RateSeries {
|
||||
end := now.Truncate(rateStep)
|
||||
s := RateSeries{End: end.Unix(), Step: int(rateStep / time.Second), Up: make([]int64, ratePoints), Down: make([]int64, ratePoints)}
|
||||
first := end.Add(-time.Duration(ratePoints-1) * rateStep)
|
||||
put := func(start time.Time, up, down int64) {
|
||||
if start.Before(first) || start.After(end) {
|
||||
return
|
||||
}
|
||||
i := int(start.Sub(first) / rateStep)
|
||||
s.Up[i], s.Down[i] = up, down
|
||||
}
|
||||
for _, b := range h.done {
|
||||
put(b.start, b.up, b.down)
|
||||
}
|
||||
if h.curSamples > 0 {
|
||||
put(h.curStart, h.curUp/h.curSamples, h.curDown/h.curSamples)
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
// rateRecorder keeps per-tunnel and total rate histories in memory (近 10 分钟,不落盘)
|
||||
type rateRecorder struct {
|
||||
mu sync.Mutex
|
||||
hist map[string]*rateHistory
|
||||
}
|
||||
|
||||
func newRateRecorder() *rateRecorder {
|
||||
return &rateRecorder{hist: make(map[string]*rateHistory)}
|
||||
}
|
||||
|
||||
// record adds one sample per tunnel (rates in bytes/s) plus the total
|
||||
func (r *rateRecorder) record(now time.Time, rates map[string][2]int64) {
|
||||
r.mu.Lock()
|
||||
defer r.mu.Unlock()
|
||||
var tu, td int64
|
||||
for id, v := range rates {
|
||||
h := r.hist[id]
|
||||
if h == nil {
|
||||
h = &rateHistory{}
|
||||
r.hist[id] = h
|
||||
}
|
||||
h.add(now, v[0], v[1])
|
||||
tu += v[0]
|
||||
td += v[1]
|
||||
}
|
||||
t := r.hist[totalSeries]
|
||||
if t == nil {
|
||||
t = &rateHistory{}
|
||||
r.hist[totalSeries] = t
|
||||
}
|
||||
t.add(now, tu, td)
|
||||
}
|
||||
|
||||
func (r *rateRecorder) series(id string, now time.Time) RateSeries {
|
||||
r.mu.Lock()
|
||||
defer r.mu.Unlock()
|
||||
if h := r.hist[id]; h != nil {
|
||||
return h.series(now)
|
||||
}
|
||||
return (&rateHistory{}).series(now)
|
||||
}
|
||||
|
||||
// forget drops the history of tunnels that are no longer enabled
|
||||
func (r *rateRecorder) retain(enabled map[string]bool) {
|
||||
r.mu.Lock()
|
||||
defer r.mu.Unlock()
|
||||
for id := range r.hist {
|
||||
if id != totalSeries && !enabled[id] {
|
||||
delete(r.hist, id)
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user