echo
memcached中间件
import (
"bytes"
"net/http"
"sync"
"github.com/bradfitz/gomemcache/memcache"
"github.com/labstack/echo/v4"
)
var memcacheClientMux sync.Mutex
var memcacheClient *memcache.Client
// TODO,要不要带上http头部,例如:数据类型html/json/js,Date,Cache-control等等
type middleResp struct {
http.ResponseWriter
isOK bool
key string
expiration int32
bytes.Buffer
}
var StoreErrHandler func(error)
func (mr *middleResp) writeStore() {
if mr.Buffer.Len() > 0 {
err := memcacheClient.Set(&memcache.Item{
Key: mr.key,
Flags: 0,
Expiration: mr.expiration,
Value: mr.Bytes(),
})
if StoreErrHandler != nil {
StoreErrHandler(err)
}
}
}
func (mr *middleResp) Write(body []byte) (int, error) {
if mr.isOK && memcacheClient != nil {
_, err := mr.Buffer.Write(body)
if StoreErrHandler != nil {
StoreErrHandler(err)
}
}
return mr.ResponseWriter.Write(body)
}
func (mr *middleResp) WriteHeader(statusCode int) {
mr.isOK = statusCode == http.StatusOK
mr.ResponseWriter.WriteHeader(statusCode)
}
func MemcacheStore(server string, expireSecond int32) func(echo.HandlerFunc) echo.HandlerFunc {
memcacheClientMux.Lock()
defer memcacheClientMux.Unlock()
if memcacheClient == nil {
memcacheClient = memcache.New(server)
}
return func(next echo.HandlerFunc) echo.HandlerFunc {
return func(c echo.Context) error {
resp := c.Response()
mr := &middleResp{
ResponseWriter: resp.Writer,
key: c.Request().RequestURI,
expiration: expireSecond,
}
resp.Writer = mr
err := next(c)
mr.writeStore()
return err
}
}
}
func MemcacheWrap(server string, expireSecond int32, handler echo.HandlerFunc) echo.HandlerFunc {
return MemcacheStore(server, expireSecond)(handler)
}
main使用
package main
import (
"fmt"
"math/rand"
"net/http"
"github.com/labstack/echo/v4"
"github.com/labstack/echo/v4/middleware"
)
func main() {
e := echo.New()
// Middleware
e.Use(middleware.Logger())
e.Use(middleware.Recover())
StoreErrHandler = func(err error) {
e.Logger.Error(err)
}
// Routes
e.GET("/echo/string", stringHandler, MemcacheStore("127.0.0.1:11211", 60))
e.GET("/echo/html", MemcacheWrap("127.0.0.1:11211", 60, htmlHandler))
// Start server
e.Logger.Fatal(e.Start(":1323"))
}
// Handler
func stringHandler(c echo.Context) error {
err := c.String(http.StatusOK, fmt.Sprintf("<H1>path:%s</H1>", c.Path()))
if err != nil {
return err
}
return c.String(http.StatusOK, fmt.Sprintf(
"<H2>query:%s,rand:%d</H2>",
c.QueryString(),
rand.Int(),
))
}
// Handler
func htmlHandler(c echo.Context) error {
err := c.HTML(http.StatusOK, fmt.Sprintf("<H1>uri:%s</H1>", c.Request().RequestURI))
if err != nil {
return err
}
return c.HTML(http.StatusOK, fmt.Sprintf(
"<H2>query:%s,rand:%d</H2>",
c.QueryString(),
rand.Int(),
))
}
nginx转发
...
location /echo {
set $memcached_key "$request_uri";
memcached_pass 127.0.0.1:11211;
#指示返回为html,方便浏览器直接显示
default_type text/html;
error_page 404 502 504 = @echoServer;
}
location @echoServer {
proxy_pass http://127.0.0.1:1323;
}
...