Cache
cache is a middleware for caching HTTP Responses, which helps to improve the concurrent access capacity of the Server. Hertz also provides the adaptation of cache, supporting multi-backend, referring to gin-cache.
Install
go get github.com/hertz-contrib/cache
Import
import "github.com/hertz-contrib/cache"
Example
- memory
func main() {
h := server.New()
// sets the global TTL value for items in the cache, which can be overridden at the item level
memoryStore := persist.NewMemoryStore(1 * time.Minute)
// sets the TTL value for URI-based items in the cache
h.Use(cache.NewCacheByRequestURI(memoryStore, 2*time.Second))
h.GET("/hello", func(ctx context.Context, c *app.RequestContext) {
c.String(http.StatusOK, "hello world")
})
h.Spin()
}
- redis
func main() {
h := server.New()
redisStore := persist.NewRedisStore(redis.NewClient(&redis.Options{
Network: "tcp",
Addr: "127.0.0.1:6379",
}))
h.Use(cache.NewCacheByRequestURI(redisStore, 2*time.Second))
h.GET("/hello", func(ctx context.Context, c *app.RequestContext) {
c.String(http.StatusOK, "hello world")
})
h.Spin()
}
Initialization
cache provides three ways of initialization.
NewCacheByRequestURI
NewCacheByRequestURI is used to create middleware that caches response results with URI as the key.
Function Signature:
func NewCacheByRequestURI(defaultCacheStore persist.CacheStore, defaultExpire time.Duration, opts ...Option) app.HandlerFunc
Sample Code:
func main() {
h := server.New()
memoryStore := persist.NewMemoryStore(1 * time.Minute)
h.Use(cache.NewCacheByRequestURI(memoryStore, 2*time.Second))
h.GET("/hello", func(ctx context.Context, c *app.RequestContext) {
c.String(http.StatusOK, "hello world")
})
h.Spin()
}
NewCacheByRequestPath
NewCacheByRequestPath is used to create middleware that caches response results with URL as the key, discarding the query parameter.
Function Signature:
func NewCacheByRequestPath(defaultCacheStore persist.CacheStore, defaultExpire time.Duration, opts ...Option) app.HandlerFunc
Sample Code:
func main() {
h := server.New()
memoryStore := persist.NewMemoryStore(1 * time.Minute)
h.Use(cache.NewCacheByRequestPath(memoryStore, 2*time.Second))
h.GET("/hello", func(ctx context.Context, c *app.RequestContext) {
c.String(http.StatusOK, "hello world")
})
h.Spin()
}
NewCache
NewCache is used to create middleware for custom cache logic, and the cache key must be declared manually (required in conjunction with WithCacheStrategyByRequest).
Function Signature:
func NewCache(
defaultCacheStore persist.CacheStore,
defaultExpire time.Duration,
opts ...Option,
) app.HandlerFunc
Sample Code:
func main() {
h := server.New()
memoryStore := persist.NewMemoryStore(1 * time.Minute)
h.Use(cache.NewCache(
memoryStore,
2*time.Second,
cache.WithCacheStrategyByRequest(func(ctx context.Context, c *app.RequestContext) (bool, cache.Strategy) {
return true, cache.Strategy{
CacheKey: c.Request.URI().String(),
}
}),
))
h.GET("/hello", func(ctx context.Context, c *app.RequestContext) {
c.String(http.StatusOK, "hello world")
})
h.Spin()
}
NewCacheByRequestURIWithIgnoreQueryOrder
Create caching middleware that uses URIs as keys and ignores the order of query parameters
Function Signature:
func NewCacheByRequestURIWithIgnoreQueryOrder(defaultCacheStore persist.CacheStore, defaultExpire time.Duration, opts ...Option) app.HandlerFunc
Sample Code:
func main() {
h := server.New()
memoryStore := persist.NewMemoryStore(1 * time.Minute)
h.Use(cache.NewCacheByRequestURIWithIgnoreQueryOrder(
memoryStore,
2*time.Second,
cache.WithCacheStrategyByRequest(func(ctx context.Context, c *app.RequestContext) (bool, cache.Strategy) {
return true, cache.Strategy{
CacheKey: c.Request.URI().String(),
}
}),
))
h.GET("/hello", func(ctx context.Context, c *app.RequestContext) {
c.String(http.StatusOK, "hello world")
})
h.Spin()
}
Configuration
Generic Configuration
| Configuration | Default | Description |
|---|---|---|
| WithOnHitCache | nil | Used to set the callback function after a cache hits |
| WithOnMissCache | nil | Used to set the callback function for cache misses |
| WithBeforeReplyWithCache | nil | Used to set the callback function before returning the cached response |
| WithOnShareSingleFlight | nil | Used to set the callback function when the result of a SingleFlight is shared by the request |
| WithSingleFlightForgetTimeout | 0 | Used to set the timeout for SingleFlight |
| WithPrefixKey | "" | Used to set the prefix of the cache response key |
| WithoutHeader | false | Used to set whether response headers need to be cached |
| WithCacheStrategyByRequest | nil | Used to set custom caching policies |
WithCacheStrategyByRequest
Customize the cache policy by using WithCacheStrategyByRequest, including the cache key, storage medium, and expiration time.
This configuration assumes that the cache middleware is initialized via the cache.NewCache method.
Function Signature:
func WithCacheStrategyByRequest(getGetCacheStrategyByRequest GetCacheStrategyByRequest) Option
Sample Code:
func main() {
h := server.New()
memoryStore := persist.NewMemoryStore(1 * time.Minute)
h.Use(cache.NewCache(
memoryStore,
2*time.Second,
cache.WithCacheStrategyByRequest(func(ctx context.Context, c *app.RequestContext) (bool, cache.Strategy) {
return true, cache.Strategy{
CacheKey: c.Request.URI().String(),
}
}),
))
h.GET("/hello", func(ctx context.Context, c *app.RequestContext) {
c.String(http.StatusOK, "hello world")
})
h.Spin()
}
WithOnHitCache & WithOnMissCache
Set the callback function for cache hits by using WithOnHitCache.
Set the callback function for cache misses by using WithOnMissCache.
Function Signature:
func WithOnHitCache(cb OnHitCacheCallback) Option
func WithOnMissCache(cb OnMissCacheCallback) Option
Sample Code:
func main() {
h := server.New()
memoryStore := persist.NewMemoryStore(1 * time.Minute)
var cacheHitCount, cacheMissCount int32
h.Use(cache.NewCacheByRequestURI(
memoryStore,
2*time.Second,
cache.WithOnHitCache(func(ctx context.Context, c *app.RequestContext) {
atomic.AddInt32(&cacheHitCount, 1)
}),
cache.WithOnMissCache(func(ctx context.Context, c *app.RequestContext) {
atomic.AddInt32(&cacheMissCount, 1)
}),
))
h.GET("/hello", func(ctx context.Context, c *app.RequestContext) {
c.String(http.StatusOK, "hello world")
})
h.GET("/get_hit_count", func(ctx context.Context, c *app.RequestContext) {
c.String(200, fmt.Sprintf("total hit count: %d", cacheHitCount))
})
h.GET("/get_miss_count", func(ctx context.Context, c *app.RequestContext) {
c.String(200, fmt.Sprintf("total miss count: %d", cacheMissCount))
})
h.Spin()
}
WithBeforeReplyWithCache
Set the callback function before returning the cached response by using WithBeforeReplyWithCache.
Function Signature:
func WithBeforeReplyWithCache(cb BeforeReplyWithCacheCallback) Option
Sample Code:
func main() {
h := server.New()
memoryStore := persist.NewMemoryStore(1 * time.Minute)
h.Use(cache.NewCacheByRequestURI(
memoryStore,
2*time.Second,
cache.WithBeforeReplyWithCache(func(c *app.RequestContext, cache *cache.ResponseCache) {
cache.Data = append([]byte{'p', 'r', 'e', 'f', 'i', 'x', '-'}, cache.Data...)
}),
))
h.GET("/hello", func(ctx context.Context, c *app.RequestContext) {
c.String(http.StatusOK, "hello world")
})
h.Spin()
}
WithOnShareSingleFlight & WithSingleFlightForgetTimeout
Set the callback function when a SingleFlight result is requested to be shared by using WithOnShareSingleFlight.
Set the timeout for SingleFlight by using WithSingleFlightForgetTimeout.
Function Signature:
func WithOnShareSingleFlight(cb OnShareSingleFlightCallback) Option
func WithSingleFlightForgetTimeout(forgetTimeout time.Duration) Option
Sample Code:
func main() {
h := server.New()
memoryStore := persist.NewMemoryStore(1 * time.Minute)
h.Use(cache.NewCacheByRequestPath(
memoryStore,
10*time.Second,
cache.WithOnShareSingleFlight(func(ctx context.Context, c *app.RequestContext) {
hlog.Info("share the singleFlight result " + string(c.Response.Body()))
}),
cache.WithSingleFlightForgetTimeout(1*time.Second),
))
h.GET("/hello", func(ctx context.Context, c *app.RequestContext) {
time.Sleep(3 * time.Second)
c.String(http.StatusOK, "hello world")
})
h.Spin()
}
WithPrefixKey
Set the prefix of the response key by using WithPrefixKey.
Function Signature:
func WithPrefixKey(prefix string) Option
Sample Code:
func main() {
h := server.New()
memoryStore := persist.NewMemoryStore(1 * time.Minute)
h.Use(cache.NewCache(
memoryStore,
60*time.Second,
cache.WithPrefixKey("prefix-"),
cache.WithOnHitCache(func(ctx context.Context, c *app.RequestContext) {
resp := &cache.ResponseCache{}
memoryStore.Get(ctx, "prefix-test", &resp)
hlog.Info("data = " + string(resp.Data))
}),
cache.WithCacheStrategyByRequest(func(ctx context.Context, c *app.RequestContext) (bool, cache.Strategy) {
return true, cache.Strategy{
CacheKey: "test",
}
}),
))
h.GET("/hello", func(ctx context.Context, c *app.RequestContext) {
c.String(http.StatusOK, "hello world")
})
h.Spin()
}
WithoutHeader
Set whether the response header should be cached by using WithoutHeader, or cache the response header if it is false.
Function Signature:
func WithoutHeader(b bool) Option
Sample Code:
func main() {
h := server.New()
memoryStore := persist.NewMemoryStore(1 * time.Minute)
h.Use(cache.NewCache(
memoryStore,
60*time.Second,
cache.WithoutHeader(true),
cache.WithCacheStrategyByRequest(func(ctx context.Context, c *app.RequestContext) (bool, cache.Strategy) {
return true, cache.Strategy{
CacheKey: "test-key",
}
}),
cache.WithOnHitCache(func(ctx context.Context, c *app.RequestContext) {
resp := &cache.ResponseCache{}
memoryStore.Get(ctx, "test-key", &resp)
hlog.Info("header = " + string(resp.Header.Get("head")))
hlog.Info("data = " + string(resp.Data))
}),
))
h.GET("/hello", func(ctx context.Context, c *app.RequestContext) {
c.String(http.StatusOK, "hello world")
})
h.Spin()
}
Full Example
Refer to the cache/example for full usage examples.