Add really simple upload rate limiting and test

This commit is contained in:
Matt Joiner 2016-10-10 14:58:29 +11:00
parent 76c24e70b5
commit 836bb344f3
3 changed files with 51 additions and 8 deletions

View File

@ -22,6 +22,7 @@ import (
"github.com/anacrolix/sync"
"github.com/anacrolix/utp"
"github.com/dustin/go-humanize"
"golang.org/x/time/rate"
"github.com/anacrolix/torrent/bencode"
"github.com/anacrolix/torrent/dht"
@ -73,7 +74,8 @@ type Client struct {
// Our BitTorrent protocol extension bytes, sent in our BT handshakes.
extensionBytes peerExtensionBytes
// The net.Addr.String part that should be common to all active listeners.
listenAddr string
listenAddr string
uploadLimit *rate.Limiter
// Set of addresses that have our client ID. This intentionally will
// include ourselves if we end up trying to connect to our own address
@ -256,6 +258,11 @@ func NewClient(cfg *Config) (cl *Client, err error) {
dopplegangerAddrs: make(map[string]struct{}),
torrents: make(map[metainfo.Hash]*Torrent),
}
if cfg.UploadRateLimiter == nil {
cl.uploadLimit = rate.NewLimiter(rate.Inf, 0)
} else {
cl.uploadLimit = cfg.UploadRateLimiter
}
missinggo.CopyExact(&cl.extensionBytes, defaultExtensionBytes)
cl.event.L = &cl.mu
storageImpl := cfg.DefaultStorage
@ -1086,6 +1093,18 @@ another:
// We want to upload to the peer.
c.Unchoke()
for r := range c.PeerRequests {
res := cl.uploadLimit.ReserveN(time.Now(), int(r.Length))
delay := res.Delay()
if delay > 0 {
res.Cancel()
go func() {
time.Sleep(delay)
cl.mu.Lock()
defer cl.mu.Unlock()
cl.upload(t, c)
}()
return
}
err := cl.sendChunk(t, c, r)
if err != nil {
i := int(r.Index)

View File

@ -24,6 +24,7 @@ import (
"github.com/bradfitz/iter"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"golang.org/x/time/rate"
"github.com/anacrolix/torrent/bencode"
"github.com/anacrolix/torrent/dht"
@ -270,6 +271,17 @@ func TestClientTransferDefault(t *testing.T) {
})
}
func TestClientTransferRateLimited(t *testing.T) {
started := time.Now()
testClientTransfer(t, testClientTransferParams{
// We are uploading 13 bytes (the length of the greeting torrent). The
// chunks are 2 bytes in length. Then the smallest burst we can run
// with is 2. Time taken is (13-burst)/rate.
SeederUploadRateLimiter: rate.NewLimiter(11, 2),
})
require.True(t, time.Since(started) > time.Second)
}
func fileCachePieceResourceStorage(fc *filecache.Cache) storage.ClientImpl {
return storage.NewResourcePieces(fc.AsResourceProvider())
}
@ -332,12 +344,13 @@ func TestClientTransferVarious(t *testing.T) {
}
type testClientTransferParams struct {
Responsive bool
Readahead int64
SetReadahead bool
ExportClientStatus bool
LeecherStorage func(string) storage.ClientImpl
SeederStorage func(string) storage.ClientImpl
Responsive bool
Readahead int64
SetReadahead bool
ExportClientStatus bool
LeecherStorage func(string) storage.ClientImpl
SeederStorage func(string) storage.ClientImpl
SeederUploadRateLimiter *rate.Limiter
}
// Creates a seeder and a leecher, and ensures the data transfers when a read
@ -348,6 +361,7 @@ func testClientTransfer(t *testing.T, ps testClientTransferParams) {
// Create seeder and a Torrent.
cfg := TestingConfig
cfg.Seed = true
cfg.UploadRateLimiter = ps.SeederUploadRateLimiter
// cfg.ListenAddr = "localhost:4000"
if ps.SeederStorage != nil {
cfg.DefaultStorage = ps.SeederStorage(greetingTempDir)
@ -368,7 +382,11 @@ func testClientTransfer(t *testing.T, ps testClientTransferParams) {
leecherDataDir, err := ioutil.TempDir("", "")
require.NoError(t, err)
defer os.RemoveAll(leecherDataDir)
cfg.DefaultStorage = ps.LeecherStorage(leecherDataDir)
if ps.LeecherStorage == nil {
cfg.DataDir = leecherDataDir
} else {
cfg.DefaultStorage = ps.LeecherStorage(leecherDataDir)
}
// cfg.ListenAddr = "localhost:4001"
leecher, err := NewClient(&cfg)
require.NoError(t, err)

View File

@ -1,6 +1,8 @@
package torrent
import (
"golang.org/x/time/rate"
"github.com/anacrolix/torrent/dht"
"github.com/anacrolix/torrent/iplist"
"github.com/anacrolix/torrent/storage"
@ -29,6 +31,10 @@ type Config struct {
// not altruistic, we'll upload slightly more than we download from each
// peer.
Seed bool `long:"seed"`
// If non-zero, is the maximum bytes per second of data pieces we'll
// upload to peers.
UploadRateLimiter *rate.Limiter
// User-provided Client peer ID. If not present, one is generated automatically.
PeerID string
// For the bittorrent protocol.