在Golang𢢸中缓存
#go #caching #gofiber #postman

简介ð

缓存是网络开发中用于提高应用程序性能的重要技术。它涉及将经常访问的数据存储在临时存储区域(例如内存)中,以减少从其源获取数据所需的时间。

有不同类型的缓存,包括内存中心,数据库缓存和基于文件的缓存。每种类型都有其优点和缺点,并且选择正确的一种取决于特定用例和应用程序的要求。

内存中的缓存是可用的最简单,最快的缓存方法之一,使其成为许多应用程序的流行选择。它涉及将数据存储在服务器的内存中,使应用程序可以快速访问应用程序,而无需任何外部依赖关系。

在这篇博客文章中,我们将重点介绍Golang的内存中缓存,并以简单的API为例演示其实现。到本博客结束时,您将对内存中的缓存作品有深入的了解。

先决条件

要继续进行教程,首先您需要安装Golang和光纤。

安装:

  • Golang
  • Fiber:我们将在教程中看到这一点。

入门ð

让我们通过使用以下命令创建主要项目目录Go-Cache-API开始。

(ð¥请小心,有时我通过在代码中发表评论来完成解释)

mkdir Go-Cache-API //Creates a 'Go-Cache-API' directory
cd Go-Cache-API //Change directory to 'Go-Cache-API'

现在初始化一个mod文件。 (如果您发布模块,则必须是通过Go Tools下载模块的路径。这将是您的代码存储库。)

go mod init github.com/<username>/Go-Cache-API //<username> is your github username

安装光纤框架运行以下命令:

go get -u github.com/gofiber/fiber/v2

用于实现内存中的缓存我们将要使用go-cache,以安装它运行以下命令:

go get github.com/patrickmn/go-cache

go-cache是一个内存密钥:值存储/高速缓存类似于适合在单台计算机上运行的应用程序的memcached。

现在,让我们制作要定义路线的main.go

package main

import (
    "time"
    "github.com/gofiber/fiber/v2"
    "github.com/Siddheshk02/Go-Cache-API/middleware"
    "github.com/Siddheshk02/Go-Cache-API/routes"
    "github.com/patrickmn/go-cache"
)

func main() {
    app := fiber.New() // Creating a new instance of Fiber.

    //cache := cache.New(10*time.Minute, 20*time.Minute) // setting default expiration time and clearance time.

    app.Get("/", func(c *fiber.Ctx) error {
        return c.SendString("Hello, World 👋!")
    })
    //app.Get("/posts/:id", middleware.CacheMiddleware(cache),   routes.GetPosts) //commenting this route just to test the "/" endpoint.
    app.Listen(":8080")
}

main.go文件中,第一步是使用fiber.New()方法初始化新的光纤应用。这将创建一个将处理HTTP请求和响应的光纤框架的新实例。

我们将使用https://jsonplaceholder.typicode.com/来获取示例数据。我们将从 /帖子端点获取数据。 Jsonplaceholder为您可以尝试任何其他API端点提供虚假数据。

cache.New()函数采用两个参数:

  • 第一个参数是缓存条目在自动驱逐之前应延续的时间。在这种情况下,设置为10分钟。
  • 第二个参数是缓存条目可以在自动驱逐之前保持空闲(无访问)的时间。在这种情况下,设置为20分钟。

运行go run main.go命令后,终端将看起来像这样,

Output

您现在可以输入所有代码行。

您可以看到中间软件功能已添加到/posts路由中。当向该端点提出请求时,服务器将首先使用缓存参数执行CacheMiddleware函数。此中间软件功能负责检查请求的数据是否已被缓存并从缓存中返回,而不是进行新的API调用。如果缓存中不存在数据,则中间件功能将将请求传递给中间件链中的下一个功能,即routes.GetPosts

routes.GetPosts是一个函数,当请求达到这一点时将执行。此功能将通过向https://jsonplaceholder.typicode.com/posts/:id上的外部API提出获取帖子数据来处理请求。 URL的:id部分是占位符,将被请求的帖子的实际ID替换。

让我们为此定义CacheMiddleware()函数,在主目录中制作一个文件夹中间件。在此制作文件cache.go

package middleware

import (
    "encoding/json"
    "time"

    "github.com/gofiber/fiber/v2"
    "github.com/patrickmn/go-cache"
)

type Post struct {
    UserID int    `json:"userId"`
    ID     int    `json:"id"`
    Title  string `json:"title"`
    Body   string `json:"body"`
}

func CacheMiddleware(cache *cache.Cache) fiber.Handler {
    return func(c *fiber.Ctx) error {
        if c.Method() != "GET" {
            // Only cache GET requests
            return c.Next()
        }

        cacheKey := c.Path() + "?" + c.Params("id") // Generate a cache key from the request path and query parameters

        // Check if the response is already in the cache
        if cached, found := cache.Get(cacheKey); found {
            return c.JSON(cached)
        }
        err := c.Next()
        if err != nil {
            return err
        }

        var data Post
        cacheKey := c.Path() + "?" + c.Params("id")

        body := c.Response().Body()
        err = json.Unmarshal(body, &data)
        if err != nil {
            return c.JSON(fiber.Map{"error": err.Error()})
        }

        // Cache the response for 10 minutes
        cache.Set(cacheKey, data, 10*time.Minute)

        return nil
    }
}

中间件函数CacheMiddleware将指针作为参数作为参数并返回fiber.Handler函数。

中间件功能首先检查请求的HTTP方法是否为GET。如果不是GET请求,它只需将请求传递给下一个中间件函数。

如果它是GET请求,它通过连接请求路径和查询参数来生成缓存键。然后,它通过使用cache.Cache对象的GET方法来检查该缓存键的响应是否已经存在。如果缓存中存在响应,则使用fiber.Ctx对象的JSON方法返回缓存的响应。

如果缓存中不存在响应,则使用fiber.Ctx对象的下一个方法调用下一个中间件函数。然后,它通过连接请求路径和查询参数来创建一个新的缓存键,并使用fiber.Ctx对象的Response().Body()方法读取响应主体。然后,它使用json.Unmarshal方法将响应主体列入Post结构。

如果在删除响应主体时存在错误,则使用fiber.Ctx对象的JSON方法返回错误响应。

如果没有错误,它使用cache.Cache对象的集合方法将响应缓存10分钟,并返回零以指示中间件函数已完成处理请求。

现在,让我们为此定义GetPosts()函数,在主目录中制作一个文件夹routes。在此制作一个文件routes.go

package routes

import (
    "encoding/json"
    "io/ioutil"
    "log"
    "net/http"

    "github.com/gofiber/fiber/v2"
)

type Post struct {
    UserID int    `json:"userId"`
    ID     int    `json:"id"`
    Title  string `json:"title"`
    Body   string `json:"body"`
}

func GetPosts(c *fiber.Ctx) error {
    id := c.Params("id") // Get the post ID from the request URL parameters
    if id == "" {
        log.Fatal("Invalid ID")
    }

    // Fetch the post data from the API
    resp, err := http.Get("https://jsonplaceholder.typicode.com/posts/" + id)
    if err != nil {
        return c.JSON(fiber.Map{"error": err.Error()})
    }
    defer resp.Body.Close()

    body, err := ioutil.ReadAll(resp.Body)
    if err != nil {
        return c.JSON(fiber.Map{"error": err.Error()})
    }

    var data Post
    err = json.Unmarshal(body, &data)
    if err != nil {
        return c.JSON(fiber.Map{"error": err.Error()})
    }

    return c.JSON(data)

}

此功能首先使用c.Params("id")从URL提取ID参数。如果未提供ID或无效,则会记录致命错误。

然后,它以提供ID作为端点的http GET请求向https://jsonplaceholder.typicode.com/posts/ API。它在请求期间检查是否有任何错误,并在发生错误时返回错误响应。

然后,使用ioutil.ReadAll()函数读取响应主体,并使用json.Unmarshal()将其删除到Post结构中。如果在解散期间发生错误,则返回错误响应。

最后,该函数使用Post数据返回JSON响应。然后,该数据由CacheMiddleware()函数存储在缓存中。

现在,如果我们要确定响应是来自缓存还是API服务器的响应,我们将在响应中添加一个标题,以指示是否从缓存提供了响应。我们将添加一个名为Cache-Status的自定义标头,并根据是否从缓存提供了响应,将其值设置为HITMISS。因此,CacheMiddleware()函数看起来像:

package middleware

import (
    "encoding/json"
    "time"

    "github.com/gofiber/fiber/v2"
    "github.com/patrickmn/go-cache"
)

type Post struct {
    UserID int    `json:"userId"`
    ID     int    `json:"id"`
    Title  string `json:"title"`
    Body   string `json:"body"`
}

func CacheMiddleware(cache *cache.Cache) fiber.Handler {
    return func(c *fiber.Ctx) error {
        if c.Method() != "GET" {
            // Only cache GET requests
            return c.Next()
        }

        cacheKey := c.Path() + "?" + c.Params("id") // Generate a cache key from the request path and query parameters

        // Check if the response is already in the cache
        if cached, found := cache.Get(cacheKey); found {
            c.Response().Header.Set("Cache-Status", "HIT")
            return c.JSON(cached)
        }

        c.Set("Cache-Status", "MISS")
        err := c.Next()
        if err != nil {
            return err
        }

        var data Post
        cacheKey := c.Path() + "?" + c.Params("id")

        body := c.Response().Body()
        err = json.Unmarshal(body, &data)
        if err != nil {
            return c.JSON(fiber.Map{"error": err.Error()})
        }

        // Cache the response for 10 minutes
        cache.Set(cacheKey, data, 10*time.Minute)

        return nil
    }
}

让我们测试API,在终端中运行go run main.go,在获得与早期相同的输出后,打开任何API测试工具,例如Postman

输入url:http://127.0.0.1:8080/posts/1,然后按Enter。

输出:

{
    "userId": 1,
    "id": 1,
    "title": "sunt aut facere repellat provident occaecati excepturi optio reprehenderit",
    "body": "quia et suscipit\nsuscipit recusandae consequuntur expedita et cum\nreprehenderit molestiae ut ut quas totam\nnostrum rerum est autem sunt rem eveniet architecto"
}

现在单击“标题”选项,您可以看到Cache-StatusMISS。再次按Enter的ID,然后查看Cache-Status。现在已更改为HIT。这表明第二次传递相同的参数时,响应是通过缓存发送的。

这是Golang内存内缓存的基本实现。

结论 -

Conclusion

您可以在此处找到本教程的完整代码存储库。

在Golang中,您可以使用内存内存系统实现缓存,就像我们在此博客文章中讨论的那样。通过使用中间件功能,您可以轻松地将缓存集成到Golang Web应用程序中,并减少用户的响应时间。

但是,如果无法正确管理,缓存也可能导致过时的数据。重要的是考虑缓存到期时间并在基础数据更改时更新缓存。

总的来说,缓存可能是您的网络开发工具包的一个很好的补充,我希望这篇博客文章为Golang的内存中心中的内存提供了有用的介绍。

要获取有关Golang概念,项目等的更多信息。并且要保持在教程上的最新信息,请遵循Siddhesh on TwitterGitHub

在那之前继续学习保持建筑