google.golang.org/grpc@v1.72.2/benchmark/latency/latency.go (about) 1 /* 2 * 3 * Copyright 2017 gRPC authors. 4 * 5 * Licensed under the Apache License, Version 2.0 (the "License"); 6 * you may not use this file except in compliance with the License. 7 * You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 * 17 */ 18 19 // Package latency provides wrappers for net.Conn, net.Listener, and 20 // net.Dialers, designed to interoperate to inject real-world latency into 21 // network connections. 22 package latency 23 24 import ( 25 "bytes" 26 "context" 27 "encoding/binary" 28 "fmt" 29 "io" 30 "net" 31 "time" 32 ) 33 34 // Dialer is a function matching the signature of net.Dial. 35 type Dialer func(network, address string) (net.Conn, error) 36 37 // TimeoutDialer is a function matching the signature of net.DialTimeout. 38 type TimeoutDialer func(network, address string, timeout time.Duration) (net.Conn, error) 39 40 // ContextDialer is a function matching the signature of 41 // net.Dialer.DialContext. 42 type ContextDialer func(ctx context.Context, network, address string) (net.Conn, error) 43 44 // Network represents a network with the given bandwidth, latency, and MTU 45 // (Maximum Transmission Unit) configuration, and can produce wrappers of 46 // net.Listeners, net.Conn, and various forms of dialing functions. The 47 // Listeners and Dialers/Conns on both sides of connections must come from this 48 // package, but need not be created from the same Network. Latency is computed 49 // when sending (in Write), and is injected when receiving (in Read). This 50 // allows senders' Write calls to be non-blocking, as in real-world 51 // applications. 52 // 53 // Note: Latency is injected by the sender specifying the absolute time data 54 // should be available, and the reader delaying until that time arrives to 55 // provide the data. This package attempts to counter-act the effects of clock 56 // drift and existing network latency by measuring the delay between the 57 // sender's transmission time and the receiver's reception time during startup. 58 // No attempt is made to measure the existing bandwidth of the connection. 59 type Network struct { 60 Kbps int // Kilobits per second; if non-positive, infinite 61 Latency time.Duration // One-way latency (sending); if non-positive, no delay 62 MTU int // Bytes per packet; if non-positive, infinite 63 } 64 65 var ( 66 // Local simulates local network. 67 Local = Network{0, 0, 0} 68 // LAN simulates local area network. 69 LAN = Network{100 * 1024, 2 * time.Millisecond, 1500} 70 // WAN simulates wide area network. 71 WAN = Network{20 * 1024, 30 * time.Millisecond, 1500} 72 // Longhaul simulates bad network. 73 Longhaul = Network{1000 * 1024, 200 * time.Millisecond, 9000} 74 ) 75 76 func (n *Network) isLocal() bool { 77 return *n == Local 78 } 79 80 // Conn returns a net.Conn that wraps c and injects n's latency into that 81 // connection. This function also imposes latency for connection creation. 82 // If n's Latency is lower than the measured latency in c, an error is 83 // returned. 84 func (n *Network) Conn(c net.Conn) (net.Conn, error) { 85 if n.isLocal() { 86 return c, nil 87 } 88 start := now() 89 nc := &conn{Conn: c, network: n, readBuf: new(bytes.Buffer)} 90 if err := nc.sync(); err != nil { 91 return nil, err 92 } 93 sleep(start.Add(nc.delay).Sub(now())) 94 return nc, nil 95 } 96 97 type conn struct { 98 net.Conn 99 network *Network 100 101 readBuf *bytes.Buffer // one packet worth of data received 102 lastSendEnd time.Time // time the previous Write should be fully on the wire 103 delay time.Duration // desired latency - measured latency 104 } 105 106 // header is sent before all data transmitted by the application. 107 type header struct { 108 ReadTime int64 // Time the reader is allowed to read this packet (UnixNano) 109 Sz int32 // Size of the data in the packet 110 } 111 112 func (c *conn) Write(p []byte) (n int, err error) { 113 tNow := now() 114 if c.lastSendEnd.Before(tNow) { 115 c.lastSendEnd = tNow 116 } 117 for len(p) > 0 { 118 pkt := p 119 if c.network.MTU > 0 && len(pkt) > c.network.MTU { 120 pkt = pkt[:c.network.MTU] 121 p = p[c.network.MTU:] 122 } else { 123 p = nil 124 } 125 if c.network.Kbps > 0 { 126 if congestion := c.lastSendEnd.Sub(tNow) - c.delay; congestion > 0 { 127 // The network is full; sleep until this packet can be sent. 128 sleep(congestion) 129 tNow = tNow.Add(congestion) 130 } 131 } 132 c.lastSendEnd = c.lastSendEnd.Add(c.network.pktTime(len(pkt))) 133 hdr := header{ReadTime: c.lastSendEnd.Add(c.delay).UnixNano(), Sz: int32(len(pkt))} 134 if err := binary.Write(c.Conn, binary.BigEndian, hdr); err != nil { 135 return n, err 136 } 137 x, err := c.Conn.Write(pkt) 138 n += x 139 if err != nil { 140 return n, err 141 } 142 } 143 return n, nil 144 } 145 146 func (c *conn) Read(p []byte) (n int, err error) { 147 if c.readBuf.Len() == 0 { 148 var hdr header 149 if err := binary.Read(c.Conn, binary.BigEndian, &hdr); err != nil { 150 return 0, err 151 } 152 defer func() { sleep(time.Unix(0, hdr.ReadTime).Sub(now())) }() 153 154 if _, err := io.CopyN(c.readBuf, c.Conn, int64(hdr.Sz)); err != nil { 155 return 0, err 156 } 157 } 158 // Read from readBuf. 159 return c.readBuf.Read(p) 160 } 161 162 // sync does a handshake and then measures the latency on the network in 163 // coordination with the other side. 164 func (c *conn) sync() error { 165 const ( 166 pingMsg = "syncPing" 167 warmup = 10 // minimum number of iterations to measure latency 168 giveUp = 50 // maximum number of iterations to measure latency 169 accuracy = time.Millisecond // req'd accuracy to stop early 170 goodRun = 3 // stop early if latency within accuracy this many times 171 ) 172 173 type syncMsg struct { 174 SendT int64 // Time sent. If zero, stop. 175 RecvT int64 // Time received. If zero, fill in and respond. 176 } 177 178 // A trivial handshake 179 if err := binary.Write(c.Conn, binary.BigEndian, []byte(pingMsg)); err != nil { 180 return err 181 } 182 var ping [8]byte 183 if err := binary.Read(c.Conn, binary.BigEndian, &ping); err != nil { 184 return err 185 } else if string(ping[:]) != pingMsg { 186 return fmt.Errorf("malformed handshake message: %v (want %q)", ping, pingMsg) 187 } 188 189 // Both sides are alive and syncing. Calculate network delay / clock skew. 190 att := 0 191 good := 0 192 var latency time.Duration 193 localDone, remoteDone := false, false 194 send := true 195 for !localDone || !remoteDone { 196 if send { 197 if err := binary.Write(c.Conn, binary.BigEndian, syncMsg{SendT: now().UnixNano()}); err != nil { 198 return err 199 } 200 att++ 201 send = false 202 } 203 204 // Block until we get a syncMsg 205 m := syncMsg{} 206 if err := binary.Read(c.Conn, binary.BigEndian, &m); err != nil { 207 return err 208 } 209 210 if m.RecvT == 0 { 211 // Message initiated from other side. 212 if m.SendT == 0 { 213 remoteDone = true 214 continue 215 } 216 // Send response. 217 m.RecvT = now().UnixNano() 218 if err := binary.Write(c.Conn, binary.BigEndian, m); err != nil { 219 return err 220 } 221 continue 222 } 223 224 lag := time.Duration(m.RecvT - m.SendT) 225 latency += lag 226 avgLatency := latency / time.Duration(att) 227 if e := lag - avgLatency; e > -accuracy && e < accuracy { 228 good++ 229 } else { 230 good = 0 231 } 232 if att < giveUp && (att < warmup || good < goodRun) { 233 send = true 234 continue 235 } 236 localDone = true 237 latency = avgLatency 238 // Tell the other side we're done. 239 if err := binary.Write(c.Conn, binary.BigEndian, syncMsg{}); err != nil { 240 return err 241 } 242 } 243 if c.network.Latency <= 0 { 244 return nil 245 } 246 c.delay = c.network.Latency - latency 247 if c.delay < 0 { 248 return fmt.Errorf("measured network latency (%v) higher than desired latency (%v)", latency, c.network.Latency) 249 } 250 return nil 251 } 252 253 // Listener returns a net.Listener that wraps l and injects n's latency in its 254 // connections. 255 func (n *Network) Listener(l net.Listener) net.Listener { 256 if n.isLocal() { 257 return l 258 } 259 return &listener{Listener: l, network: n} 260 } 261 262 type listener struct { 263 net.Listener 264 network *Network 265 } 266 267 func (l *listener) Accept() (net.Conn, error) { 268 c, err := l.Listener.Accept() 269 if err != nil { 270 return nil, err 271 } 272 return l.network.Conn(c) 273 } 274 275 // Dialer returns a Dialer that wraps d and injects n's latency in its 276 // connections. n's Latency is also injected to the connection's creation. 277 func (n *Network) Dialer(d Dialer) Dialer { 278 if n.isLocal() { 279 return d 280 } 281 return func(network, address string) (net.Conn, error) { 282 conn, err := d(network, address) 283 if err != nil { 284 return nil, err 285 } 286 return n.Conn(conn) 287 } 288 } 289 290 // TimeoutDialer returns a TimeoutDialer that wraps d and injects n's latency 291 // in its connections. n's Latency is also injected to the connection's 292 // creation. 293 func (n *Network) TimeoutDialer(d TimeoutDialer) TimeoutDialer { 294 if n.isLocal() { 295 return d 296 } 297 return func(network, address string, timeout time.Duration) (net.Conn, error) { 298 conn, err := d(network, address, timeout) 299 if err != nil { 300 return nil, err 301 } 302 return n.Conn(conn) 303 } 304 } 305 306 // ContextDialer returns a ContextDialer that wraps d and injects n's latency 307 // in its connections. n's Latency is also injected to the connection's 308 // creation. 309 func (n *Network) ContextDialer(d ContextDialer) ContextDialer { 310 if n.isLocal() { 311 return d 312 } 313 return func(ctx context.Context, network, address string) (net.Conn, error) { 314 conn, err := d(ctx, network, address) 315 if err != nil { 316 return nil, err 317 } 318 return n.Conn(conn) 319 } 320 } 321 322 // pktTime returns the time it takes to transmit one packet of data of size b 323 // in bytes. 324 func (n *Network) pktTime(b int) time.Duration { 325 if n.Kbps <= 0 { 326 return time.Duration(0) 327 } 328 return time.Duration(b) * time.Second / time.Duration(n.Kbps*(1024/8)) 329 } 330 331 // Wrappers for testing 332 333 var now = time.Now 334 var sleep = time.Sleep