feat: measure each direction of the speed test for 7 s (0.0.7)
This commit is contained in:
1 parent
ab8751d436
commit
5155bc36cd
5 files changed
+32
-21
No files matched your search
+1
-1
@@ -1 +1 @@
|
|||||||
0.0.6
|
0.0.7
|
||||||
+17
-11
@@ -13,24 +13,30 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
// speedWindow is how long each direction is measured for.
|
// speedWindow is how long each direction is measured for, unless the
|
||||||
speedWindow = 3 * time.Second
|
// server asks for another window; speedWindowMax bounds what it may ask.
|
||||||
|
speedWindow = 3 * time.Second
|
||||||
|
speedWindowMax = 30 * time.Second
|
||||||
// speedWait is how long the download may take to start: in a relayed test
|
// speedWait is how long the download may take to start: in a relayed test
|
||||||
// its bytes come from the browser.
|
// its bytes come from the browser.
|
||||||
speedWait = 10 * time.Second
|
speedWait = 10 * time.Second
|
||||||
// speedUpCap bounds the upload, whatever the link does in speedWindow.
|
// speedUpCap bounds the upload, whatever the link does in the window.
|
||||||
speedUpCap = 48 << 20
|
speedUpCap = 48 << 20
|
||||||
)
|
)
|
||||||
|
|
||||||
// doSpeed measures the agent's HTTP link to the server: round-trip time of an
|
// doSpeed measures the agent's HTTP link to the server: round-trip time of an
|
||||||
// empty request, then how much flows each way in speedWindow. In a relayed
|
// empty request, then how much flows each way in the window. In a relayed
|
||||||
// test the server pipes both streams to the browser, so they run end to end.
|
// test the server pipes both streams to the browser, so they run end to end.
|
||||||
func (a *Agent) doSpeed(ctx context.Context, env proto.TaskEnvelope) proto.TaskResult {
|
func (a *Agent) doSpeed(ctx context.Context, env proto.TaskEnvelope) proto.TaskResult {
|
||||||
var args proto.SysSpeedArgs
|
var args proto.SysSpeedArgs
|
||||||
if len(env.Args) > 0 {
|
if len(env.Args) > 0 {
|
||||||
_ = json.Unmarshal(env.Args, &args)
|
_ = json.Unmarshal(env.Args, &args)
|
||||||
}
|
}
|
||||||
res, err := a.measureSpeed(ctx, args.Relay)
|
window := speedWindow
|
||||||
|
if args.WindowMs > 0 {
|
||||||
|
window = min(time.Duration(args.WindowMs)*time.Millisecond, speedWindowMax)
|
||||||
|
}
|
||||||
|
res, err := a.measureSpeed(ctx, args.Relay, window)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return proto.TaskResult{Error: err.Error()}
|
return proto.TaskResult{Error: err.Error()}
|
||||||
}
|
}
|
||||||
@@ -38,7 +44,7 @@ func (a *Agent) doSpeed(ctx context.Context, env proto.TaskEnvelope) proto.TaskR
|
|||||||
return proto.TaskResult{OK: true, Data: data}
|
return proto.TaskResult{OK: true, Data: data}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *Agent) measureSpeed(ctx context.Context, relay bool) (proto.SysSpeedResult, error) {
|
func (a *Agent) measureSpeed(ctx context.Context, relay bool, window time.Duration) (proto.SysSpeedResult, error) {
|
||||||
res := proto.SysSpeedResult{Relay: relay}
|
res := proto.SysSpeedResult{Relay: relay}
|
||||||
streams := "/api/agent/speed"
|
streams := "/api/agent/speed"
|
||||||
if relay {
|
if relay {
|
||||||
@@ -59,14 +65,14 @@ func (a *Agent) measureSpeed(ctx context.Context, relay bool) (proto.SysSpeedRes
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
down, err := a.speedDown(ctx, streams+"/down")
|
down, err := a.speedDown(ctx, streams+"/down", window)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return res, err
|
return res, err
|
||||||
}
|
}
|
||||||
res.DownBps = down
|
res.DownBps = down
|
||||||
|
|
||||||
// Up: send until the window closes or the cap is hit.
|
// Up: send until the window closes or the cap is hit.
|
||||||
body := &speedSource{deadline: time.Now().Add(speedWindow), left: speedUpCap}
|
body := &speedSource{deadline: time.Now().Add(window), left: speedUpCap}
|
||||||
start := time.Now()
|
start := time.Now()
|
||||||
resp, err := a.speedRequest(ctx, http.MethodPost, streams+"/up", body)
|
resp, err := a.speedRequest(ctx, http.MethodPost, streams+"/up", body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -78,9 +84,9 @@ func (a *Agent) measureSpeed(ctx context.Context, relay bool) (proto.SysSpeedRes
|
|||||||
return res, nil
|
return res, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// speedDown reads what the server streams for speedWindow, counted from the
|
// speedDown reads what the server streams for the window, counted from the
|
||||||
// first byte, and returns the rate.
|
// first byte, and returns the rate.
|
||||||
func (a *Agent) speedDown(ctx context.Context, path string) (float64, error) {
|
func (a *Agent) speedDown(ctx context.Context, path string, window time.Duration) (float64, error) {
|
||||||
ctx, cancel := context.WithCancel(ctx)
|
ctx, cancel := context.WithCancel(ctx)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
t := time.AfterFunc(speedWait, cancel)
|
t := time.AfterFunc(speedWait, cancel)
|
||||||
@@ -99,7 +105,7 @@ func (a *Agent) speedDown(ctx context.Context, path string) (float64, error) {
|
|||||||
k, err := resp.Body.Read(buf)
|
k, err := resp.Body.Read(buf)
|
||||||
if k > 0 && start.IsZero() {
|
if k > 0 && start.IsZero() {
|
||||||
start = time.Now()
|
start = time.Now()
|
||||||
t.Reset(speedWindow)
|
t.Reset(window)
|
||||||
}
|
}
|
||||||
n += int64(k)
|
n += int64(k)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -184,6 +184,9 @@ type SysSpeedArgs struct {
|
|||||||
// Relay makes the agent use the relayed streams, which the server pipes
|
// Relay makes the agent use the relayed streams, which the server pipes
|
||||||
// to and from the browser.
|
// to and from the browser.
|
||||||
Relay bool `json:"relay,omitempty"`
|
Relay bool `json:"relay,omitempty"`
|
||||||
|
// WindowMs is how long to measure each direction for; 0 leaves it to the
|
||||||
|
// agent (3 s, all an agent older than 0.0.7 does).
|
||||||
|
WindowMs int `json:"window_ms,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// SysSpeedResult is returned for TaskSysSpeed: the agent's HTTP link to the
|
// SysSpeedResult is returned for TaskSysSpeed: the agent's HTTP link to the
|
||||||
|
|||||||
@@ -20,17 +20,20 @@ const (
|
|||||||
// statCacheFor is how long one sample answers every open status page, so
|
// statCacheFor is how long one sample answers every open status page, so
|
||||||
// several tabs cost the agent a single reading.
|
// several tabs cost the agent a single reading.
|
||||||
statCacheFor = 2 * time.Second
|
statCacheFor = 2 * time.Second
|
||||||
|
// speedWindow is how long the agent is asked to measure each direction
|
||||||
|
// for. An agent older than 0.0.7 keeps to its own 3 s.
|
||||||
|
speedWindow = 7 * time.Second
|
||||||
// speedTaskTimeout is how long a speed test may take. One that is not done
|
// speedTaskTimeout is how long a speed test may take. One that is not done
|
||||||
// by then is answered with what the server has counted itself.
|
// by then is answered with what the server has counted itself.
|
||||||
speedTaskTimeout = 15 * time.Second
|
speedTaskTimeout = 35 * time.Second
|
||||||
// speedDownCap bounds one download stream; speedDownFor ends it if the
|
// speedDownCap bounds one download stream; speedDownFor ends it if the
|
||||||
// agent does not hang up first.
|
// agent does not hang up first.
|
||||||
speedDownCap = 256 << 20
|
speedDownCap = 1 << 30
|
||||||
speedDownFor = 6 * time.Second
|
speedDownFor = 2 * speedWindow
|
||||||
speedUpCap = 64 << 20
|
speedUpCap = 64 << 20
|
||||||
// speedRelayUpFor is how long the agent's upload is passed on to the
|
// speedRelayUpFor is how long the agent's upload is passed on to the
|
||||||
// browser: the agent's own window with slack.
|
// browser: the agent's own window with slack.
|
||||||
speedRelayUpFor = 4 * time.Second
|
speedRelayUpFor = speedWindow + time.Second
|
||||||
)
|
)
|
||||||
|
|
||||||
// statCache holds the last sample per agent and serializes its refresh.
|
// statCache holds the last sample per agent and serializes its refresh.
|
||||||
@@ -71,7 +74,7 @@ func (s *Server) handleStatusPage(w http.ResponseWriter, r *http.Request) {
|
|||||||
http.NotFound(w, r)
|
http.NotFound(w, r)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
s.render(w, "status.html", map[string]any{"Agent": a})
|
s.render(w, "status.html", map[string]any{"Agent": a, "RelayUpMs": speedRelayUpFor.Milliseconds()})
|
||||||
}
|
}
|
||||||
|
|
||||||
// runTask runs a one-shot task on the agent and waits for its result.
|
// runTask runs a one-shot task on the agent and waits for its result.
|
||||||
@@ -144,7 +147,6 @@ func (s *Server) handleAgentSpeed(w http.ResponseWriter, r *http.Request) {
|
|||||||
relay := r.URL.Query().Get("relay") != ""
|
relay := r.URL.Query().Get("relay") != ""
|
||||||
m := s.meters.start(id)
|
m := s.meters.start(id)
|
||||||
defer s.meters.stop(id, m)
|
defer s.meters.stop(id, m)
|
||||||
var args []byte
|
|
||||||
if relay {
|
if relay {
|
||||||
rl := s.relays.get(id)
|
rl := s.relays.get(id)
|
||||||
if rl == nil {
|
if rl == nil {
|
||||||
@@ -152,8 +154,8 @@ func (s *Server) handleAgentSpeed(w http.ResponseWriter, r *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
defer s.relays.drop(id, rl)
|
defer s.relays.drop(id, rl)
|
||||||
args, _ = json.Marshal(proto.SysSpeedArgs{Relay: true})
|
|
||||||
}
|
}
|
||||||
|
args, _ := json.Marshal(proto.SysSpeedArgs{Relay: relay, WindowMs: int(speedWindow.Milliseconds())})
|
||||||
res, err := s.runTask(id, proto.TaskSysSpeed, args, speedTaskTimeout)
|
res, err := s.runTask(id, proto.TaskSysSpeed, args, speedTaskTimeout)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
part, ok := m.result()
|
part, ok := m.result()
|
||||||
|
|||||||
@@ -77,7 +77,7 @@
|
|||||||
<h2>Скорость соединения</h2>
|
<h2>Скорость соединения</h2>
|
||||||
<p class="muted">Клиент замеряет свой канал до хоста панели - тот же, по которому идут терминал, файлы и http-проброс.
|
<p class="muted">Клиент замеряет свой канал до хоста панели - тот же, по которому идут терминал, файлы и http-проброс.
|
||||||
С галочкой «До браузера» данные идут дальше, через панель до этого браузера и обратно: так виден весь путь клиент - панель - браузер.
|
С галочкой «До браузера» данные идут дальше, через панель до этого браузера и обратно: так виден весь путь клиент - панель - браузер.
|
||||||
Замер занимает около 7 секунд, не дольше 20, и на это время нагружает канал.</p>
|
Замер занимает около 15 секунд, не дольше 40, и на это время нагружает канал.</p>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<button id="speed-btn" type="button">Замерить скорость</button>
|
<button id="speed-btn" type="button">Замерить скорость</button>
|
||||||
<label class="check" title="Данные пойдут не только между клиентом и хостом панели, а через панель до этого браузера и обратно">
|
<label class="check" title="Данные пойдут не только между клиентом и хостом панели, а через панель до этого браузера и обратно">
|
||||||
@@ -225,7 +225,7 @@
|
|||||||
return rd.read().then(function (c) {
|
return rd.read().then(function (c) {
|
||||||
if (c.done) return done();
|
if (c.done) return done();
|
||||||
last = performance.now();
|
last = performance.now();
|
||||||
if (!st.start) { st.start = last; setTimeout(st.stop, 4000); onFirst(); }
|
if (!st.start) { st.start = last; setTimeout(st.stop, {{.RelayUpMs}}); onFirst(); }
|
||||||
else st.n += c.value.length;
|
else st.n += c.value.length;
|
||||||
return pump();
|
return pump();
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in new issue
Block a user