开发一个带缓存的代理服务器
开发一个带缓存的代理服务器
背景
前端页面中经常会有很多的请求,而开发环境的服务器资源比较差,就会经常出现页面加载两三面的情况,并且开发时也经常需要刷新页面,这就是一个降低了工作效率的点。
为了解决该问题,我就打算开发一个可以缓存请求的代理服务器。
原来的工作流程是,前端启动项目时,后端服务器地址可以是线上的测试环境,或者本地的Go服务器。
使用缓存服务后,就将服务器地址改为本地的缓存服务地址,比如localhost:7000。
缓存服务有web管理页面,可以配置源地址,可以设置忽略缓存的条件、清理和查看缓存。
实现
1. 服务器
先是一个普通的服务器,监听 7001 端口
// Handle the requests
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
proxyHandler(w, r, proxy)
})
log.Fatal(http.ListenAndServe("localhost:7000", nil))
3. 处理缓存
缓存保存在内存中,是一个全局唯一的结构体
type MemoryCache struct {
cache map[string]string
queue chan []string
}
缓存的主体是一个原生的map。key是由url和请求体计算而来的,value是返回体。
queue是为了解决map并发不安全的报错。
func NewMemoryCache() ICache {
ins := &MemoryCache{
cache: make(map[string]string),
queue: make(chan []string),
}
// Concurrency safe set
go func() {
for i := range ins.queue {
ins.cache[i[0]] = i[1]
}
}()
return ins
}
func (c *MemoryCache) Set(key string, value string) {
// Concurrency safe set
c.queue <- []string{key, value}
}
处理请求
如果没有命中缓存,就要发起一个请求到源。要分别处理请header、body,要根据配置设置对应的host地址。
利用标准库的 httputil.NewSingleHostReverseProxy 方法发起请求,
返回的结果既要返回前端,又要进行缓存。
使用效果
首先缓存没有做持久化,感觉没必要,因为第一次请求也就两三秒,持久化的意义不大。
所以第一次进入系统或页面,还是要进行正常的请求。后面在刷新页面的时间都能控制在100-200ms的范围。
使用方法
下载代码直接运行,或者 go build 出可执行文件,或者下载打包好的文件即可使用。
运行服务后会在本地7000端口开启一个缓存代理服务器和一个管理服务器,通过 localhost:8080 可以进入管理页面,设置源服务器地址,忽略缓存的条件、查看缓存的内容等。
遇到的问题
- map并发问题
- NewSingleHostReverseProxy需要再Director回调中设置请求的header、url等信息
- 返回的数据是在ResponseWriter中,好像不能被读取两次,不能同时返回给前端和保存到缓存,要进行一些特殊处理。