基于Gin框架的HTTP接口限速实践

在微服务和RESTful API时代,为确保系统稳定性和性能,接口限速很重要。本文介绍了接口限速的使用场景,区分了限速与限流的概念,还基于limiter插件给出Gin框架接口限速的实践,包括基本使用、引入自定义拦截处理器、不同接口区分速率等。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

在当今的微服务架构和RESTful API主导的时代,HTTP接口在各个业务模块之间扮演着重要的角色。随着业务规模的不断扩大,接口的访问频率和负载也随之增加。为了确保系统的稳定性和性能,接口限速成了一个重要的话题。

1 接口限速的使用场景

接口限速的使用场景主要涉及以下几种情况:

  1. 防止API滥用:在某些情况下,如果没有有效的限速机制,恶意用户可能会无限制地调用API,导致系统过载。通过接口限速,我们可以限制每个用户对特定接口的访问频率,从而防止API滥用。
  2. 保护服务稳定性:在某些情况下,某些高频调用可能会给后端服务带来巨大的压力,影响服务的稳定性和性能。通过接口限速,我们可以限制对这些接口的访问频率,从而保护服务的稳定性。
  3. 资源合理分配:在一些情况下,我们需要对系统资源进行合理的分配,确保每个用户都能得到公平的资源使用。通过接口限速,我们可以根据用户的请求频率进行资源分配,从而保证公平性。

2 限速不同与限流

接口限速和限流是两个不同的概念,虽然它们都是用来控制流量和保护系统的手段,但它们的目的和实现方式有所不同。

接口限速主要是限制接口的访问速度,避免过快的请求频率对系统造成压力。它关注的是单个接口的访问速率,比如每秒可以访问多少次,而限流则是关注系统的整体流量,限制单位时间内系统的总访问量。

限速通常是通过在接口上设置速率限制来实现的,例如使用令牌桶算法或漏桶算法等。它的主要目的是防止单个接口的过快访问,以保护系统的稳定性和性能。

而限流则是通过一系列机制来限制单位时间内系统的总访问量,以防止系统过载。常见的限流算法包括令牌桶算法、漏桶算法和热点参数等。它的主要目的是保护整个系统,避免因为访问量过大而出现崩溃或性能下降的情况。

在实现方面,限速通常是在应用程序或API网关层面实现的,而限流则可能需要涉及到整个系统的架构和设计。

虽然接口限速和限流的目的和实现方式有所不同,但它们都是为了控制流量和保护系统的稳定性和性能。在实际应用中,我们可以根据实际情况选择合适的限速和限流策略,以实现最佳的流量控制效果。

3 Gin框架接口限速实践

基于limiter插件的GitHub地址:github.com/ulule/limiter

3.1 基本使用
package main

import (
   "fmt"
   "log"
   "net/http"

   "github.com/gin-gonic/gin"
   "github.com/redis/go-redis/v9"
   "github.com/ulule/limiter/v3"
   mgin "github.com/ulule/limiter/v3/drivers/middleware/gin"
   sredis "github.com/ulule/limiter/v3/drivers/store/redis"
)

func main() {
   // Define a limit rate to 4 requests per hour.
   rate, err := limiter.NewRateFromFormatted("4-M")
   if err != nil {
      log.Fatal(err)
      return
   }

   // Create a redis client.
   option, err := redis.ParseURL("redis://localhost:6379/0")
   if err != nil {
      log.Fatal(err)
      return
   }
   client := redis.NewClient(option)

   // Create a store with the redis client.
   store, err := sredis.NewStoreWithOptions(client, limiter.StoreOptions{
      Prefix:   "limiter_gin_example",
      MaxRetry: 3,
   })
   if err != nil {
      log.Fatal(err)
      return
   }

   // Create a new middleware with the limiter instance.
   middleware := mgin.NewMiddleware(limiter.New(store, rate))

   // Launch a simple server.
   router := gin.Default()
   router.ForwardedByClientIP = true
   router.Use(middleware)
   router.GET("/", index)
   log.Fatal(router.Run(":8081"))
}

func index(c *gin.Context) {
   c.JSON(http.StatusOK, "This is my gin api...")
}
3.2 引入自定义拦截处理器
package main

import (
   "fmt"
   "log"
   "net/http"

   "github.com/gin-gonic/gin"
   "github.com/redis/go-redis/v9"
   "github.com/ulule/limiter/v3"
   mgin "github.com/ulule/limiter/v3/drivers/middleware/gin"
   sredis "github.com/ulule/limiter/v3/drivers/store/redis"
)

func main() {
   rate, err := limiter.NewRateFromFormatted("4-M")
   if err != nil {
      log.Fatal(err)
      return
   }

   option, err := redis.ParseURL("redis://localhost:6379/0")
   if err != nil {
      log.Fatal(err)
      return
   }
   client := redis.NewClient(option)

   store, err := sredis.NewStoreWithOptions(client, limiter.StoreOptions{
      Prefix:   "limiter_gin_example",
      MaxRetry: 3,
   })
   if err != nil {
      log.Fatal(err)
      return
   }

   //自定义拦截处理器
   opt := mgin.WithLimitReachedHandler(ExceededHandler)
   middleware := mgin.NewMiddleware(limiter.New(store, rate), opt)

   router := gin.Default()
   router.ForwardedByClientIP = true
   router.Use(middleware)
   router.GET("/", index)
   log.Fatal(router.Run(":8081"))
}

func ExceededHandler(c *gin.Context) {
   c.JSON(200, "This is mu custom ExceededHandler...")
}

func index(c *gin.Context) {
   c.JSON(http.StatusOK, "This is my gin api...")
}

返回结果:

在这里插入图片描述

3.3 不同接口区分速率

我们假设系统有两个接口:

  • /fast : 每分钟允许10次访问
  • /slow : 每分钟允许1次访问

代码实现:

package main

import (
   "fmt"
   "log"
   "net/http"

   "github.com/gin-gonic/gin"
   "github.com/redis/go-redis/v9"
   "github.com/ulule/limiter/v3"
   mgin "github.com/ulule/limiter/v3/drivers/middleware/gin"
   sredis "github.com/ulule/limiter/v3/drivers/store/redis"
)

var (
   fastTime = 0
   slowTime = 0
)

func FastApi(c *gin.Context) {
   fastTime += 1
   c.JSON(200, fmt.Sprintf("This is fast api... %d", fastTime))
}

func SlowApi(c *gin.Context) {
   slowTime += 1
   c.JSON(200, fmt.Sprintf("This is slow api... %d", slowTime))
}

func main() {
   fastRate, err := limiter.NewRateFromFormatted("10-M")
   if err != nil {
      log.Fatal(err)
      return
   }
   slowRate, err := limiter.NewRateFromFormatted("1-M")
   if err != nil {
      log.Fatal(err)
      return
   }

   option, err := redis.ParseURL("redis://localhost:6379/0")
   if err != nil {
      log.Fatal(err)
      return
   }
   client := redis.NewClient(option)

   storeFast, err := sredis.NewStoreWithOptions(client, limiter.StoreOptions{Prefix: "limiter_gin_example_fast", MaxRetry: 3})
   if err != nil {
      log.Fatal(err)
      return
   }
   storeSlow, err := sredis.NewStoreWithOptions(client, limiter.StoreOptions{Prefix: "limiter_gin_example_slow", MaxRetry: 3})
   if err != nil {
      log.Fatal(err)
      return
   }

   //自定义拦截处理器
   opt := mgin.WithLimitReachedHandler(ExceededHandler)
   middlewareFast := mgin.NewMiddleware(limiter.New(storeFast, fastRate), opt)
   middlewareSlow := mgin.NewMiddleware(limiter.New(storeSlow, slowRate), opt)

   router := gin.Default()
   router.ForwardedByClientIP = true
   router.Use(func(c *gin.Context) {
      if c.Request.RequestURI == "/fast" {
         middlewareFast(c)
         return
      }
      if c.Request.RequestURI == "/slow" {
         middlewareSlow(c)
         return
      }
   })
   router.GET("/fast", FastApi)
   router.GET("/slow", SlowApi)
   log.Fatal(router.Run(":8081"))
}

func ExceededHandler(c *gin.Context) {
   c.JSON(200, "This is mu custom ExceededHandler...")
}

4 小总结

接口限速是保护系统稳定性和API的重要手段。在实际应用中,我们需要根据实际情况选择合适的限速方法,实现对接口的全面限速。通过接口限速,我们可以提高系统的稳定性、保护API、提高用户体验等。

Tollbooth 是一个用 Go 语言编写的用来限制 HTTP 访问速度的中间件,可用来限制每个 HTTP 请求的传输速率。例如你可以不限制 / 的访问速率,但是可以针对 /login 限制每个 IP 每秒最多 POST 多少个请求。Go 程序中使用的方法:package main import (     "github.com/didip/tollbooth"     "net/http"     "time" ) func HelloHandler(w http.ResponseWriter, req *http.Request) {     w.Write([]byte("Hello, World!")) } func main() {     // You can create a generic limiter for all your handlers     // or one for each handler. Your choice.     // This limiter basically says: allow at most 1 request per 1 second.     limiter := tollbooth.NewLimiter(1, time.Second)     // This is an example on how to limit only GET and POST requests.     limiter.Methods = []string{"GET", "POST"}     // You can also limit by specific request headers, containing certain values.     // Typically, you prefetched these values from the database.     limiter.Headers = make(map[string][]string)     limiter.Headers["X-Access-Token"] = []string{"abc123", "xyz098"}     // And finally, you can limit access based on basic auth usernames.     // Typically, you prefetched these values from the database as well.     limiter.BasicAuthUsers = []string{"bob", "joe", "didip"}     // Example on how to wrap your request handler.     http.Handle("/", tollbooth.LimitFuncHandler(limiter, HelloHandler))     http.ListenAndServe(":12345", nil) 标签:Tollbooth
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

闫同学鸭

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值