From e288c7c08cad916c4a1baf2690d1dadfb05a602a Mon Sep 17 00:00:00 2001 From: Christine Dodrill Date: Fri, 28 Apr 2017 23:22:00 -0700 Subject: [PATCH] reverse proxy buffer pool --- server/server.go | 2 + vendor-log | 1 + vendor/github.com/oxtoacart/bpool/bpool.go | 6 ++ .../github.com/oxtoacart/bpool/bufferpool.go | 40 +++++++++++++ vendor/github.com/oxtoacart/bpool/bytepool.go | 45 ++++++++++++++ .../oxtoacart/bpool/sizedbufferpool.go | 60 +++++++++++++++++++ 6 files changed, 154 insertions(+) create mode 100644 vendor/github.com/oxtoacart/bpool/bpool.go create mode 100644 vendor/github.com/oxtoacart/bpool/bufferpool.go create mode 100644 vendor/github.com/oxtoacart/bpool/bytepool.go create mode 100644 vendor/github.com/oxtoacart/bpool/sizedbufferpool.go diff --git a/server/server.go b/server/server.go index b96dfae..551cc0b 100644 --- a/server/server.go +++ b/server/server.go @@ -12,6 +12,7 @@ import ( "git.xeserv.us/xena/route/lib/tun2" proto "git.xeserv.us/xena/route/proto" "github.com/mtneug/pkg/ulid" + "github.com/oxtoacart/bpool" "golang.org/x/crypto/acme/autocert" "google.golang.org/grpc" "google.golang.org/grpc/credentials" @@ -139,6 +140,7 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { Director: s.Director, Transport: s.ts, FlushInterval: 1 * time.Second, + BufferPool: bpool.NewBytePool(256, 4096), } rp.ServeHTTP(w, r) diff --git a/vendor-log b/vendor-log index c942ae0..7593ef1 100644 --- a/vendor-log +++ b/vendor-log @@ -133,3 +133,4 @@ da118f7b8e5954f39d0d2130ab35d4bf0e3cb344 golang.org/x/net/context 0eb507a2ca07f13baf499f89d66cc566bf644643 (dirty) google.golang.org/grpc/credentials 737072b4e32b7a5018b4a7125da8d12de90e8045 github.com/mattn/go-runewidth 44e365d423f4f06769182abfeeae2b91be9d529b github.com/olekukonko/tablewriter +4e1c5567d7c2dd59fa4c7c83d34c2f3528b025d6 github.com/oxtoacart/bpool diff --git a/vendor/github.com/oxtoacart/bpool/bpool.go b/vendor/github.com/oxtoacart/bpool/bpool.go new file mode 100644 index 0000000..6232a38 --- /dev/null +++ b/vendor/github.com/oxtoacart/bpool/bpool.go @@ -0,0 +1,6 @@ +/* +Package bpool implements leaky pools of byte arrays and Buffers as bounded +channels. It is based on the leaky buffer example from the Effective Go +documentation: http://golang.org/doc/effective_go.html#leaky_buffer +*/ +package bpool diff --git a/vendor/github.com/oxtoacart/bpool/bufferpool.go b/vendor/github.com/oxtoacart/bpool/bufferpool.go new file mode 100644 index 0000000..8c8ac64 --- /dev/null +++ b/vendor/github.com/oxtoacart/bpool/bufferpool.go @@ -0,0 +1,40 @@ +package bpool + +import ( + "bytes" +) + +// BufferPool implements a pool of bytes.Buffers in the form of a bounded +// channel. +type BufferPool struct { + c chan *bytes.Buffer +} + +// NewBufferPool creates a new BufferPool bounded to the given size. +func NewBufferPool(size int) (bp *BufferPool) { + return &BufferPool{ + c: make(chan *bytes.Buffer, size), + } +} + +// Get gets a Buffer from the BufferPool, or creates a new one if none are +// available in the pool. +func (bp *BufferPool) Get() (b *bytes.Buffer) { + select { + case b = <-bp.c: + // reuse existing buffer + default: + // create new buffer + b = bytes.NewBuffer([]byte{}) + } + return +} + +// Put returns the given Buffer to the BufferPool. +func (bp *BufferPool) Put(b *bytes.Buffer) { + b.Reset() + select { + case bp.c <- b: + default: // Discard the buffer if the pool is full. + } +} diff --git a/vendor/github.com/oxtoacart/bpool/bytepool.go b/vendor/github.com/oxtoacart/bpool/bytepool.go new file mode 100644 index 0000000..ef3898a --- /dev/null +++ b/vendor/github.com/oxtoacart/bpool/bytepool.go @@ -0,0 +1,45 @@ +package bpool + +// BytePool implements a leaky pool of []byte in the form of a bounded +// channel. +type BytePool struct { + c chan []byte + w int +} + +// NewBytePool creates a new BytePool bounded to the given maxSize, with new +// byte arrays sized based on width. +func NewBytePool(maxSize int, width int) (bp *BytePool) { + return &BytePool{ + c: make(chan []byte, maxSize), + w: width, + } +} + +// Get gets a []byte from the BytePool, or creates a new one if none are +// available in the pool. +func (bp *BytePool) Get() (b []byte) { + select { + case b = <-bp.c: + // reuse existing buffer + default: + // create new buffer + b = make([]byte, bp.w) + } + return +} + +// Put returns the given Buffer to the BytePool. +func (bp *BytePool) Put(b []byte) { + select { + case bp.c <- b: + // buffer went back into pool + default: + // buffer didn't go back into pool, just discard + } +} + +// Width returns the width of the byte arrays in this pool. +func (bp *BytePool) Width() (n int) { + return bp.w +} diff --git a/vendor/github.com/oxtoacart/bpool/sizedbufferpool.go b/vendor/github.com/oxtoacart/bpool/sizedbufferpool.go new file mode 100644 index 0000000..8519aca --- /dev/null +++ b/vendor/github.com/oxtoacart/bpool/sizedbufferpool.go @@ -0,0 +1,60 @@ +package bpool + +import ( + "bytes" +) + +// SizedBufferPool implements a pool of bytes.Buffers in the form of a bounded +// channel. Buffers are pre-allocated to the requested size. +type SizedBufferPool struct { + c chan *bytes.Buffer + a int +} + +// SizedBufferPool creates a new BufferPool bounded to the given size. +// size defines the number of buffers to be retained in the pool and alloc sets +// the initial capacity of new buffers to minimize calls to make(). +// +// The value of alloc should seek to provide a buffer that is representative of +// most data written to the the buffer (i.e. 95th percentile) without being +// overly large (which will increase static memory consumption). You may wish to +// track the capacity of your last N buffers (i.e. using an []int) prior to +// returning them to the pool as input into calculating a suitable alloc value. +func NewSizedBufferPool(size int, alloc int) (bp *SizedBufferPool) { + return &SizedBufferPool{ + c: make(chan *bytes.Buffer, size), + a: alloc, + } +} + +// Get gets a Buffer from the SizedBufferPool, or creates a new one if none are +// available in the pool. Buffers have a pre-allocated capacity. +func (bp *SizedBufferPool) Get() (b *bytes.Buffer) { + select { + case b = <-bp.c: + // reuse existing buffer + default: + // create new buffer + b = bytes.NewBuffer(make([]byte, 0, bp.a)) + } + return +} + +// Put returns the given Buffer to the SizedBufferPool. +func (bp *SizedBufferPool) Put(b *bytes.Buffer) { + b.Reset() + + // Release buffers over our maximum capacity and re-create a pre-sized + // buffer to replace it. + // Note that the cap(b.Bytes()) provides the capacity from the read off-set + // only, but as we've called b.Reset() the full capacity of the underlying + // byte slice is returned. + if cap(b.Bytes()) > bp.a { + b = bytes.NewBuffer(make([]byte, 0, bp.a)) + } + + select { + case bp.c <- b: + default: // Discard the buffer if the pool is full. + } +}