如何在一个 HTTP 服务器中使用缓存来提升性能,同时使用 sync.Once 和 Ticker 来实现了缓存的初始化和定时更新。
sync.Once 是 Go 标准库提供的使函数只执行一次的实现,常应用于单例模式,例如初始化配置、保持数据库连接等。
go-cache 是一个轻量级的基于内存的 K-V 储存组件,内部实现了一个线程安全的 map[string]interface{},适用于单机应用。
go-cache 具备如下功能:
- 线程安全,多 goroutine 并发安全访问;
- 每个 item 可以设置过期时间(或无过期时间)
- 自动定期清理过期的 item;
- 可以自定义清理回调函数;
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 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110
| package main
import ( "context" "log" "net/http" "sync" "time"
"github.com/gin-gonic/gin" "github.com/patrickmn/go-cache" "github.com/robfig/cron/v3" )
const ( defaultExpireTime = 10 * time.Second )
var ( startUpdateCacheOnce sync.Once )
type CacheClient struct { cache *cache.Cache once sync.Once }
func NewCacheClient() *CacheClient { return &CacheClient{} }
func (c *CacheClient) GetCache() *cache.Cache { c.once.Do(func() { c.cache = cache.New(5*time.Minute, 10*time.Minute) }) return c.cache }
type CronClient struct { cron *cron.Cron once sync.Once }
func NewCronClient() *CronClient { return &CronClient{} }
func (c *CronClient) GetCron() *cron.Cron { c.once.Do(func() { sh, _ := time.LoadLocation("Asia/Shanghai") c.cron = cron.New(cron.WithLocation(sh)) c.cron.Start() }) return c.cron }
func setDefaultCache(cacheClient *cache.Cache) { time.Sleep(1 * time.Second) cacheClient.Set("foo", "balalala~"+time.Now().Format(time.RFC3339Nano), defaultExpireTime) }
func main() { r := gin.Default()
cacheClient := NewCacheClient().GetCache() ctx, cancel := context.WithCancel(context.Background()) defer cancel()
crontab := NewCronClient().GetCron() defer crontab.Stop() if _, err := crontab.AddFunc("@every 1s", func() { log.Printf("-----debug crontab-----> %s \n", time.Now().Format(time.RFC3339)) }); err != nil { return }
apiv1 := r.Group("/api/v1") { apiv1.GET("/go-cache", func(c *gin.Context) { startUpdateCacheOnce.Do(func() { setDefaultCache(cacheClient) go func() { log.Printf("----debug----> %s \n", time.Now().Format(time.RFC3339)) ticker := time.NewTicker(defaultExpireTime - 1*time.Microsecond) defer ticker.Stop() for { select { case <-ticker.C: setDefaultCache(cacheClient) case <-ctx.Done(): return } } }() }) foo, found := cacheClient.Get("foo") if !found { c.JSON(http.StatusOK, gin.H{ "message": "cache not found", }) return } c.JSON(http.StatusOK, gin.H{ "message": foo, }) }) } }
|