This is a continuation of the common design patterns I found in my old code series, which I started in a previous post.
The proxy pattern is a design pattern in which a class (proxy) acts as an interface to something else. The proxy could be an interface to anything: a network connection, another class, a file, etc.
A proxy can be useful in a variety of situations:
- A frontend for load balancing
- Hide private infrastructure
- Caching layer
- etc
A good example of a proxy that can be used as a load balancer (and other purposes) is nginx or net/http/httputil (the ReverseProxy
).
To illustrate how to use this pattern in Go, let’s implement a simple caching layer for files to speed up multiple reads for the same files:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
|
package fileproxy
import (
"io"
"io/ioutil"
"sync"
)
var mux sync.RWMutex
var cache map[string][]byte
func init() {
cache = map[string][]byte{}
}
func NewFileProxy(name string) FileProxy {
return &fileProxy{Name: name}
}
type FileProxy interface {
io.Reader
}
type fileProxy struct {
Name string
}
func (fp *fileProxy) Read(b []byte) (n int, err error) {
mux.RLock()
if data, ok := cache[fp.Name]; ok {
copy(b, data)
mux.RUnlock()
n = len(b)
return
}
mux.RUnlock()
var data []byte
data, err = ioutil.ReadFile(fp.Name)
if err == nil {
copy(b, data)
mux.Lock()
n = len(b)
cache[fp.Name] = data
mux.Unlock()
}
return
}
var _ FileProxy = &fileProxy{}
|
And if you’d run a simple benchmark:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
func BenchmarkReadFile(b *testing.B) {
for n := 0; n < b.N; n++ {
_, err := ioutil.ReadFile("somefile.txt")
if err != nil {
b.Fatal(err)
}
}
}
func BenchmarkFileProxy(b *testing.B) {
data := make([]byte, 24)
fp := NewFileProxy("somefile.txt")
for n := 0; n < b.N; n++ {
_, err := fp.Read(data)
if err != nil {
b.Fatal(err)
}
}
}
|
1
2
|
BenchmarkReadFile-8 87010 13601 ns/op
BenchmarkFileProxy-8 52101362 21.9 ns/op
|
You can see how the proxy could improve your app performance in a similar situation.
And that’s it. I hope this example has shed some light on how the proxy pattern works and can be used to improve your code performance.
For more design pattern examples, please checkout rolandjitsu/go-design-patterns.