如何通过Redis将Redis用作数据库

2020年12月30日11:14:42 发表评论 68 次浏览

本文概述

雷迪斯是用作数据库, 缓存或消息代理的内存中数据存储。去redis / redis是Go的类型安全的Redis客户端库, 具有对以下功能的支持:发布/订阅, 哨兵和流水线.

注意:我们将客户端库称为" go-redis", 以帮助区别于Redis本身。

在本文中, 我们将探索go-redis并使用其管道功能来构建排行榜API。该API将使用杜松子酒和Redis的排序集在引擎盖下。它将公开以下端点:

  • GET / points /:用户名-获取用户的得分及其在整体排行榜中的排名
  • POST /点—添加或更新用户及其分数。该端点还将返回用户的新等级
  • GET /排行榜—返回当前的排行榜, 用户按其排名的升序排序

先决条件

要跟随这篇文章, 你将需要:

  • 具有模块支持的Go安装
  • Redis安装在本地计算机上(或者, 你可以使用Docker镜像如果你已安装Docker)
  • 体验写Go

入门

首先, 请在你首选的位置为项目创建一个文件夹, 然后初始化Go模块:

$ mkdir rediboard && cd rediboard
$ go mod init gitlab.com/idoko/rediboard

使用以下命令安装应用程序依赖项(gin-gonic / gin和go-redis / redis):

$ go get github.com/gin-gonic/gin github.com/go-redis/redis

接下来, 创建一个main.go文件作为项目的切入点。同时, 我们还将在项目根目录中创建一个db文件夹, 以保存负责与Redis交互的代码:

$ touch main.go
$ mkdir db

熟悉go-redis

有了我们的应用程序支架, 让我们了解一些go-redis的基础知识。与Redis数据库的连接由"客户端"处理-线程安全的可以被多个人共享的价值例行程序通常在应用程序的整个生命周期中都有效。下面的代码创建一个新客户端:

client := redis.NewClient(&redis.Options{
   Addr:     "localhost:6379", // host:port of the redis server
   Password: "", // no password set
   DB:       0, // use default DB
})

Go-redis通过以下方式提供了许多配置选项:redis。选项参数。一些选项包括泳池大小设置最大连接数和TLS配置用于连接到受TLS保护Redis服务器。

然后, 客户端将命令公开为接收者方法。例如, 代码显示了如何设置和从Redis数据库获取值:

ctx := context.TODO()
client.Set(ctx, "language", "Go", 0)
language := client.Get(ctx, "language")
year := client.Get(ctx, "year")

fmt.Println(language.Val()) // "Go"
fmt.Println(year.Val()) // ""

图书馆需要一个语境参数以允许诸如基于上下文的取消正在运行的命令之类的操作。由于我们不需要这里提供的好处, 因此我们创建了一个空的上下文context.TODO()。接下来, 我们将语言设置为" Go", 并且不给它任何截止日期(通过传递0值)。我们继续获取语言和年份的值, 但是因为我们没有为年, 它是零和year.Val()返回一个空字符串。

我们为制作了一个自定义演示.
不完全是。点击这里查看.

如何通过Redis将Redis用作数据库1

使用Go连接到Redis

要为我们的应用程序创建Redis客户端, 请创建一个新的db.go文件在db我们之前创建的文件夹, 并在其中添加以下代码段:

package db

import (
   "context"
   "errors"
   "github.com/go-redis/redis/v8"
)

type Database struct {
   Client *redis.Client
}

var (
   ErrNil = errors.New("no matching record found in redis database")
   Ctx    = context.TODO()
)

func NewDatabase(address string) (*Database, error) {
   client := redis.NewClient(&redis.Options{
      Addr: address, Password: "", DB: 0, })
   if err := client.Ping(Ctx).Err(); err != nil {
      return nil, err
   }
   return &Database{
      Client: client, }, nil
}

上面的代码创建了一个数据库包装redis客户端并将其公开给应用程序的其余部分(路由器等)的结构。它还设置了两个程序包级变量–埃尼尔用来告诉调用代码Redis操作返回了零和Ctx, 供客户端使用的空上下文。我们还创建了一个新数据库函数, 用于设置客户端并使用来检查连接是否处于活动状态平命令。

打开main.go归档并调用NewDatabase()功能如下代码所示:

package main

import (
   "github.com/gin-gonic/gin"
   "gitlab.com/idoko/rediboard/db"
   "log"
   "net/http"
)

var (
   ListenAddr = "localhost:8080"
   RedisAddr = "localhost:6379"
)
func main() {
   database, err := db.NewDatabase(RedisAddr)
   if err != nil {
      log.Fatalf("Failed to connect to redis: %s", err.Error())
   }

   router := initRouter(database)
   router.Run(ListenAddr)
}

上面的代码段尝试连接到数据库并打印在此过程中遇到的任何错误。它也指初始化路由器功能。我们将在下一部分中进行设置。

Gin的API路由

接下来, 创建初始化路由器用于创建和注册应用程序路由的功能。在现有代码下面的main.go中添加以下代码主要功能:

func initRouter(database *db.Database) *gin.Engine {
   r := gin.Default()
   return r
}

目前, 该函数返回的实例杜松子酒。稍后我们将添加特定于路由的处理程序。

go-redis中的交易管道

一种Redis交易将操作排入队列, 并保证所有操作都执行或不执行。 Redis另一个有趣的功能是流水线一种网络优化, 允许Redis客户端将多个请求发送到服务器, 而无需等待答复并立即读取所有请求。

Go-redis将交易和管道都包装在Tx管道方法。以下是在redis-cli上执行的一组示例事务命令:

127.0.0.1:6379> MULTI
OK
127.0.0.1:6379> SET language "golang"
QUEUED
127.0.0.1:6379> SET year 2009
QUEUED
127.0.0.1:6379> EXEC
1) OK
2) OK
127.0.0.1:6379>

上面的命令可以转换为下面的Go代码:

pipe := db.Client.TxPipeline()
pipe.Set(Ctx, "language", "golang")
pipe.Set(Ctx, "year", 2009)
results, err := pipe.Exec()

将用户保存到排序集中

创建一个user.go文件在db文件夹, 并在其中添加以下代码:

package db

import (
   "fmt"
   "github.com/go-redis/redis/v8"
)

type User struct {
   Username string `json:"username" binding:"required"`
   Points   int `json:"points" binding:"required"`
   Rank     int    `json:"rank"`
}

func (db *Database) SaveUser(user *User) error {
   member := &redis.Z{
      Score: float64(user.Points), Member: user.Username, }
   pipe := db.Client.TxPipeline()
   pipe.ZAdd(Ctx, "leaderboard", member)
   rank := pipe.ZRank(Ctx, leaderboardKey, user.Username)
   _, err := pipe.Exec(Ctx)
   if err != nil {
      return err
   }
   fmt.Println(rank.Val(), err)
   user.Rank = int(rank.Val())
   return nil
}

上面的代码创建了一个用户用作排行榜中用户的包装。该结构包括在转换为JSON以及使用Gin的HTTP请求将其转换为字段时, 我们希望字段如何表示捆绑。然后, 它利用管道将新成员添加到排序集中, 并获得该成员的新排名。因为用户参数是一个指针, 秩当我们从SaveUser()功能。

接下来, 改变main.go打电话给保存用户上面获取POST请求时声明的函数/点。打开main.go并将下面的路由处理程序添加到初始化路由器功能(在返回r线):

r.POST("/points", func (c *gin.Context) {
   var userJson db.User
   if err := c.ShouldBindJSON(&userJson); err != nil {
      c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
      return
   }
   err := database.SaveUser(&userJson)
   if err != nil {
      c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
      return
   }
   c.JSON(http.StatusOK, gin.H{"user": userJson})
})

获取用户的分数和排名

同样, 将以下代码添加到user.go获取单个用户的排名和分数:

func (db *Database) GetUser(username string) (*User, error) {
   pipe := db.Client.TxPipeline()
   score := pipe.ZScore(Ctx, leaderboardKey, username)
   rank := pipe.ZRank(Ctx, leaderboardKey, username)
   _, err := pipe.Exec(Ctx)
   if err != nil {
      return nil, err
   }
   if score == nil {
      return nil, ErrNil
   }
   return &User{
      Username: username, Points: int(score.Val()), Rank: int(rank.Val()), }, nil
}

在这里, 我们还利用管道以用户名作为键来获取用户的得分和排名。

如果没有找到匹配的记录, 我们还会向呼叫者发送信号(使用埃尼尔), 以便由调用方单独处理此类情况(例如, 他们可以选择显示404响应)。

接下来, 在其中添加相应的路由处理程序main.go如下:

r.GET("/points/:username", func (c *gin.Context) {
   username := c.Param("username")
   user, err := database.GetUser(username)
   if err != nil {
      if err == db.ErrNil {
         c.JSON(http.StatusNotFound, gin.H{"error": "No record found for " + username})
         return
      }
      c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
      return
   }
   c.JSON(http.StatusOK, gin.H{"user": user})
})

上面的代码段检索了用户名path参数并将其传递给GetUser早先声明的函数。它还会检查返回的错误类型是否正确埃尼尔并在这种情况下显示404响应。

取得完整的页首横幅ZRangeWithScores

为了获得完整的排行榜, Redis提供了ZRange命令, 该命令用于按分数的升序检索排序集中的成员。 ZRange还接受可选扣分告诉它也返回每个成员的得分的参数。另一方面, Go-redis将命令拆分为两个, 分别提供ZRange和ZRangeWithScores。

在中创建一个新文件db文件夹名为Leaderboard.go具有以下内容:

package db

var leaderboardKey = "leaderboard"

type Leaderboard struct {
   Count int `json:"count"`
   Users []*User
}

func (db *Database) GetLeaderboard() (*Leaderboard, error) {
   scores := db.Client.ZRangeWithScores(Ctx, leaderboardKey, 0, -1)
   if scores == nil {
      return nil, ErrNil
   }
   count := len(scores.Val())
   users := make([]*User, count)
   for idx, member := range scores.Val() {
      users[idx] = &User{
         Username: member.Member.(string), Points: int(member.Score), Rank: idx, }
   }
   leaderboard := &Leaderboard{
      Count: count, Users: users, }
   return leaderboard, nil
}

的LeaderboardKey表示用于在我们的Redis数据库中标识集合的密钥。由于我们现在只运行一个命令(ZRangeWithScores), 不再需要使用交易管道来批处理命令, 因此我们将结果直接存储在分数变量。存储在中的值分数包含一片Go地图, 其长度是集合中存储的成员数。

要运行我们的应用程序, 请确保已安装并运行Redis。另外, 你可以拉入Redis Docker映像并使用以下命令运行它:

$ docker run --name=rediboard -p 6379:6379 redis

你现在可以构建并运行(或直接运行)main.go文件, 使用以下命令测试示例项目:

$ go build ./main.go
$ ./main

以下是一些示例cURL命令及其响应。

可以使用cURL, Postman, HTTPie或你最喜欢的API客户端随意尝试该API。

cURL命令:

$ curl -H "Content-type: application/json" -d '{"username": "isa", "points": 25}' localhost:8080/points

响应:

{
  "user": {
    "username": "isa", "points": 25, "rank": 3
  }
}

cURL命令:

$ curl -H "Content-type: application/json" localhost:8080/points/mchl

响应:

{
  "user": {
    "username": "jude", "points": 22, "rank": 0
  }
}

cURL命令:

$ curl -H "Content-type: application/json" localhost:8080/leaderboard

响应:

{
  "leaderboard": {
    "count": 7, "Users": [
      {
        "username": "ene", "points": 22, "rank": 0
      }, {
        "username": "ben", "points": 23, "rank": 2
      }, {
        "username": "isa", "points": 25, "rank": 3
      }, {
        "username": "jola", "points": 39, "rank": 5
      }
    ]
  }
}

这是终端中运行的应用程序的屏幕截图以及cURL响应:

在终端中运行main.go的输出。

总结

如果你想进一步探索, 可以从Redis和Go-redis的文档开始。对于不受支持的命令, go-redis还提供了通用发送()和做()方法。

在本文中, 我们介绍了如何使用go-redis库与Redis数据库进行交互。示例项目的代码位于亚搏体育app.

日志火箭:全面了解你的网络应用

LogRocket仪表板免费试用横幅

日志火箭是一个前端应用程序监视解决方案, 可让你重播问题, 就好像问题发生在你自己的浏览器中一样。 notlogy无需猜测错误发生的原因, 也不要求用户提供屏幕截图和日志转储, 而是让你重播会话以快速了解出了什么问题。无论框架如何, 它都能与任何应用完美配合, 并具有用于记录来自Redux, Vuex和@ ngrx / store的其他上下文的插件。

除了记录Redux动作和状态外, notlogy还会记录控制台日志, JavaScript错误, 堆栈跟踪, 带有标题+正文, 浏览器元数据和自定义日志的网络请求/响应。它还使用DOM来记录页面上的HTML和CSS, 甚至可以为最复杂的单页面应用程序重新创建像素完美的视频。

免费试用

.

一盏木

发表评论

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen: