Go项目优化——动态缓存Redis的使用

本文涉及的产品
云数据库 Redis 版,社区版 2GB
推荐场景:
搭建游戏排行榜
简介: Go项目优化——动态缓存Redis的使用

1. Redis:

1.1 简介:

garyburd/redigo 包是网上很多博文都在推荐使用的一个高Star的Redis连接包,项目已经迁移到了gomodule/redigo,同时包的获取也理所当然地改成了go get github.com/gomodule/redigo/redis,总之,暂时不管这两个包的详细区别,以下就以新包为准,介绍下redigo包使用。

1.2 连接redis

//第一种连接方法
con,err := redis.Dial("tcp", 
  "链接地址,例如127.0.0.1:6379", 
  redis.DialPassword("密码"),
)
// or
// con,err := redis.Dial("tcp","127.0.0.1:6379")
if err != nil {
   fmt.Println(err)
}
defer con.Close()

1.3 常用api:

// Do 向服务器发送命令并返回收到的应答。
func Do(commandName string, args ...interface{}) (reply interface{}, err error)
// reply 具体类型,具体转换
func Int(reply interface{}, err error) (int, error) 
func String(reply interface{}, err error) (string, error)
func Bool(reply interface{}, err error) (bool, error) 
func Values(reply interface{}, err error) ([]interface{}, error) 
func Strings(reply interface{}, err error) ([]string, error)
func ByteSlices(reply interface{}, err error) ([][]byte, error)
func Ints(reply interface{}, err error) ([]int, error)
func StringMap(result interface{}, err error) (map[string]string, error)
...
// 更多函数自行探索

1.3 连接池

在golang的项目中,若要频繁的用redis(或者其他类似的NoSQL)来存取数据,最好用redigo自带的池来管理连接。

 不然的话,每当要操作redis时,建立连接,用完后再关闭,会导致大量的连接处于TIME_WAIT状态(redis连接本质上就是tcp)。

注:TIME_WAIT,也叫TCP半连接状态,会继续占用本地端口。

import (
  "fmt"
  "github.com/gomodule/redigo/redis"
  "time"
)
var redisPoll *redis.Pool
func initRedis() {
  redisPoll = &redis.Pool{
    // 连接方法
    Dial: func() (redis.Conn, error) {
      c, err := redis.Dial("tcp", "链接地址,例如127.0.0.1:6379",
        redis.DialPassword("密码"),
        redis.DialReadTimeout(1*time.Second),
        redis.DialWriteTimeout(1*time.Second),
        redis.DialConnectTimeout(1*time.Second),
      )
      if err != nil {
        return nil, err
      }
      return c, nil
    },
    // 最大的空闲连接数,
    // 表示即使没有redis连接时依然可以保持N个空闲的连接,而不被清除,随时处于待命状态。
    MaxIdle: 256,
    // 最大的激活连接数,表示同时最多有N个连接
    MaxActive: 256,
    // 最大的空闲连接等待时间,超过此时间后,空闲连接将被关闭
    IdleTimeout: time.Duration(120),
  }
}
// myDo
// @Title myDo
// @Description 封装的 redis Do函数
// Do(commandName string, args ...interface{}) (reply interface{}, err error)
// @Param cmd string 命令
// @Param key interface{} 键
// @Param args ...interface{} 参数
// @Return interface{} redis服务器返回值
// @Return error 错误
func myDo(cmd string, key interface{}, args ...interface{}) (interface{}, error) {
  // 从pool里获取一个redis连接,如果连接池没有,会调用 Dial()
  con := redisPoll.Get()
  if err := con.Err(); err != nil {
    return nil, err
  }
  parmas := make([]interface{}, 0)
  parmas = append(parmas, key)
  // 如果参数不为空,也加入参数列表
  if len(args) > 0 {
    for _, arg := range args {
      parmas = append(parmas, arg)
    }
  }
  return con.Do(cmd, parmas...)
}
func main() {
  initRedis()
  myDo("set", "mykey1", "myvalue1")
  result, err := myDo("get", "mykey1")
  if err != nil {
    fmt.Println(err.Error())
  }
  // String()是将命令应答转换为字符串的帮助器。
  str, _ := redis.String(result, err)
  fmt.Println(str)
}

1.4 项目中使用:

dynamic cache:动态缓存

conf/dynamicache.conf

#*******************
#动态缓存配置
#*******************
# redis地址
dynamicache_addrstr=127.0.0.1:6379
# redis密码
dynamicache_passwd=密码

conf/app.conf

...
# 引入动态缓存配置文件
include "dynamicache.conf"
...

utils/dynamic_cache.go

package dynamicache
import (
  "encoding/json"
  "github.com/astaxie/beego/logs"
  "strconv"
  "time"
  "github.com/astaxie/beego"
  "github.com/gomodule/redigo/redis"
)
var (
  pool *redis.Pool = nil
  // MaxIdle 最大的空闲连接数,
  MaxIdle int = 0
  // MaxOpen 最大的激活连接数,表示同时最多有N个连接
  MaxOpen int = 0
  // ExpireSec 超时时间
  ExpireSec int64 = 0
)
// InitCache 在 sysinit.go 中调用
// 初始化redis配置
func InitCache() {
  addr := beego.AppConfig.String("dynamicache_addrstr")
  if len(addr) == 0 {
    addr = "127.0.0.1:6379"
  }
  if MaxIdle <= 0 {
    MaxIdle = 256
  }
  password := beego.AppConfig.String("dynamicache_passwd")
  if len(password) == 0 {
    pool = &redis.Pool{
      MaxIdle:     MaxIdle,
      MaxActive:   MaxOpen, // 最大
      IdleTimeout: time.Duration(120),
      Dial: func() (redis.Conn, error) {
        return redis.Dial(
          "tcp",
          addr,
          redis.DialReadTimeout(1*time.Second),
          redis.DialWriteTimeout(1*time.Second),
          redis.DialConnectTimeout(1*time.Second),
        )
      },
    }
  } else {
    pool = &redis.Pool{
      MaxIdle:     MaxIdle,
      MaxActive:   MaxOpen,
      IdleTimeout: time.Duration(120),
      Dial: func() (redis.Conn, error) {
        return redis.Dial(
          "tcp",
          addr,
          redis.DialReadTimeout(1*time.Second),
          redis.DialWriteTimeout(1*time.Second),
          redis.DialConnectTimeout(1*time.Second),
          redis.DialPassword(password),
        )
      },
    }
  }
}

sysinit/sysinit.go

package sysinit
import (
    ...
  "mybook/utils/dynamicache"
  ...
)
....
// initDynamicCache
// @Title initDynamicCache
// @Description 初始化dynamic_cache.go里的公有值,并初始化redis配置 
func initDynamicCache() {
  dynamicache.MaxOpen = 128
  dynamicache.MaxIdle = 128
  dynamicache.ExpireSec = 10
  dynamicache.InitCache()
}
....

根据实际业务需要,在utils/dynamic_cache.go里封装会用到的方法

utils/dynamic_cache.go

...
// redisDo
// @Title redisDo
// @Description 封装的 redis Do函数
// Do(commandName string, args ...interface{}) (reply interface{}, err error)
// @Param cmd string 命令
// @Param key interface{} 键
// @Param args ...interface{} 参数
// @Return interface{} redis服务器返回值
// @Return error 错误
func redisDo(cmd string, key interface{}, args ...interface{}) (interface{}, error) {
  con := pool.Get()
  if err := con.Err(); err != nil {
    return nil, err
  }
  params := make([]interface{}, 0)
  params = append(params, key)
  if len(args) > 0 {
    for _, v := range args {
      params = append(params, v)
    }
  }
  return con.Do(cmd, params...)
}
// WriteString
// @Title WriteString
// @Description 写字符串
// @Param key string 键
// @Param value string 值
// @Return error 错误
func WriteString(key string, value string) error {
  _, err := redisDo("SET", key, value)
  logs.Debug("redis set:" + key + "-" + value)
  redisDo("EXPIRE", key, ExpireSec)
  return err
}
// ReadString
// @Title ReadString
// @Description  读字符串
// @Param key string 键
// @Param value string 值
// @Return error 错误
func ReadString(key string) (string, error) {
  result, err := redisDo("GET", key)
  logs.Debug("redis get:" + key)
  if nil == err {
    str, _ := redis.String(result, err)
    return str, nil
  } else {
    logs.Debug("redis get error:" + err.Error())
    return "", err
  }
}
// 以下封装的方法都是在WriteString() ReadString()上封装的,使用的都是redis里的string类型
// WriteStruct
// @Title WriteStruct
// @Description 写结构体(本质是还是写json字符串)
func WriteStruct(key string, obj interface{}) error {
  data, err := json.Marshal(obj)
  if nil == err {
    return WriteString(key, string(data))
  } else {
    return nil
  }
}
// ReadStruct
// @Title ReadStruct
// @Description 读结构体
func ReadStruct(key string, obj interface{}) error {
  if data, err := ReadString(key); nil == err {
    return json.Unmarshal([]byte(data), obj)
  } else {
    return err
  }
}
// WriteList
// @Title WriteList
// @Description 写数组
func WriteList(key string, list interface{}, total int) error {
  realKeyList := key + "_list"
  realKeyCount := key + "_count"
  data, err := json.Marshal(list)
  if nil == err {
    WriteString(realKeyCount, strconv.Itoa(total))
    return WriteString(realKeyList, string(data))
  } else {
    return nil
  }
}
// ReadList
// @Title ReadList
// @Description 读数组
func ReadList(key string, list interface{}) (int, error) {
  realKeyList := key + "_list"
  realKeyCount := key + "_count"
  if data, err := ReadString(realKeyList); nil == err {
    totalStr, _ := ReadString(realKeyCount)
    total := 0
    if len(totalStr) > 0 {
      total, _ = strconv.Atoi(totalStr)
    }
    return total, json.Unmarshal([]byte(data), list)
  } else {
    return 0, err
  }
}
相关实践学习
基于Redis实现在线游戏积分排行榜
本场景将介绍如何基于Redis数据库实现在线游戏中的游戏玩家积分排行榜功能。
云数据库 Redis 版使用教程
云数据库Redis版是兼容Redis协议标准的、提供持久化的内存数据库服务,基于高可靠双机热备架构及可无缝扩展的集群架构,满足高读写性能场景及容量需弹性变配的业务需求。 产品详情:https://www.aliyun.com/product/kvstore &nbsp; &nbsp; ------------------------------------------------------------------------- 阿里云数据库体验:数据库上云实战 开发者云会免费提供一台带自建MySQL的源数据库&nbsp;ECS 实例和一台目标数据库&nbsp;RDS实例。跟着指引,您可以一步步实现将ECS自建数据库迁移到目标数据库RDS。 点击下方链接,领取免费ECS&amp;RDS资源,30分钟完成数据库上云实战!https://developer.aliyun.com/adc/scenario/51eefbd1894e42f6bb9acacadd3f9121?spm=a2c6h.13788135.J_3257954370.9.4ba85f24utseFl
相关文章
|
6天前
|
存储 监控 NoSQL
Redis处理大量数据主要依赖于其内存存储结构、高效的数据结构和算法,以及一系列的优化策略
【5月更文挑战第15天】Redis处理大量数据依赖内存存储、高效数据结构和优化策略。选择合适的数据结构、利用批量操作减少网络开销、控制批量大小、使用Redis Cluster进行分布式存储、优化内存使用及监控调优是关键。通过这些方法,Redis能有效处理大量数据并保持高性能。
27 0
|
6天前
|
数据采集 JavaScript 前端开发
使用Go和JavaScript爬取股吧动态信息的完整指南
本文介绍了如何使用Go和JavaScript构建网络爬虫,从股吧网站抓取实时股市信息。通过设置代理服务器以应对反爬策略,利用`got`库执行JavaScript提取动态数据,如用户讨论和市场分析。示例代码展示了爬虫的实现过程,包括浏览器实例创建、代理配置、JavaScript执行及数据打印。此方法有助于投资者及时获取市场资讯,为决策提供支持。
使用Go和JavaScript爬取股吧动态信息的完整指南
|
6天前
|
存储 缓存 监控
快速掌握Redis优化要点,告别性能瓶颈!
# Redis优化指南 了解如何提升Redis性能,从读写方式(整体与部分)、KV size、Key数量、读写峰值、命中率、过期策略、平均穿透加载时间、可运维性、安全性等方面着手。选择合适的读写策略,如只整体读写或部分读写变更,优化KV size避免过大或差异过大,合理管理Key数量,应对不同读写峰值,监控命中率并持续优化,设置智能过期策略,减少平均穿透加载时间,确保高可运维性并强化安全性。一起探索Redis的性能潜力!
389 4
|
6天前
|
缓存 NoSQL Java
优化Redis缓存:解决性能瓶颈和容量限制
优化Redis缓存:解决性能瓶颈和容量限制
22 0
|
6天前
|
Go 开发者
Golang深入浅出之-Go语言项目构建工具:Makefile与go build
【4月更文挑战第27天】本文探讨了Go语言项目的构建方法,包括`go build`基本命令行工具和更灵活的`Makefile`自动化脚本。`go build`适合简单项目,能直接编译Go源码,但依赖管理可能混乱。通过设置`GOOS`和`GOARCH`可进行跨平台编译。`Makefile`适用于复杂构建流程,能定义多步骤任务,但编写较复杂。在选择构建方式时,应根据项目需求权衡,从`go build`起步,逐渐过渡到Makefile以实现更高效自动化。
29 2
|
6天前
|
NoSQL Shell Go
在go中简单使用go-redis库
在go中简单使用go-redis库
|
6天前
|
存储 缓存 前端开发
【Flutter前端技术开发专栏】Flutter中的图片加载与缓存优化
【4月更文挑战第30天】本文探讨了 Flutter 中如何优化图片加载与缓存,以提升移动应用性能。通过使用图片占位符、压缩裁剪、缓存策略(如`cached_network_image`插件)以及异步加载和预加载图片,可以显著加快加载速度。此外,利用`FadeInImage`、`FutureBuilder`和图片库等工具,能进一步改善用户体验。优化图片处理是提升Flutter应用效率的关键,本文为开发者提供了实用指导。
【Flutter前端技术开发专栏】Flutter中的图片加载与缓存优化
|
6天前
|
监控 安全 Go
【Go语言专栏】Go语言中的并发性能分析与优化
【4月更文挑战第30天】Go语言以其卓越的并发性能和简洁语法著称,通过goroutines和channels实现并发。并发性能分析旨在解决竞态条件、死锁和资源争用等问题,以提升多核环境下的程序效率。使用pprof等工具可检测性能瓶颈,优化策略包括减少锁范围、使用无锁数据结构、控制goroutines数量、应用worker pool和优化channel使用。理解并发模型和合理利用并发原语是编写高效并发代码的关键。
|
6天前
|
存储 缓存 NoSQL
【Go语言专栏】Go语言中的Redis操作与缓存应用
【4月更文挑战第30天】本文探讨了在Go语言中使用Redis进行操作和缓存应用的方法。文章介绍了Redis作为高性能键值存储系统,用于提升应用性能。推荐使用`go-redis/redis`库,示例代码展示了连接、设置、获取和删除键值对的基本操作。文章还详细阐述了缓存应用的步骤及常见缓存策略,包括缓存穿透、缓存击穿和缓存雪崩的解决方案。利用Redis和合适策略可有效优化应用性能。
|
6天前
|
运维 关系型数据库 MySQL
Serverless 应用引擎产品使用之在阿里函数计算中,部署Go项目可以区分环境如何解决
阿里云Serverless 应用引擎(SAE)提供了完整的微服务应用生命周期管理能力,包括应用部署、服务治理、开发运维、资源管理等功能,并通过扩展功能支持多环境管理、API Gateway、事件驱动等高级应用场景,帮助企业快速构建、部署、运维和扩展微服务架构,实现Serverless化的应用部署与运维模式。以下是对SAE产品使用合集的概述,包括应用管理、服务治理、开发运维、资源管理等方面。
19 0