写点什么

ARTS 打卡 第二周,业精于勤

作者:海风极客
  • 2023-09-02
    北京
  • 本文字数:4792 字

    阅读完需:约 16 分钟

ARTS 打卡 第二周,业精于勤

第二周来咯~

Algorithm

选自力扣网站上的题目:移动零


给定一个数组 nums,编写一个函数将所有 0 移动到数组的末尾,同时保持非零元素的相对顺序。(必须在不复制数组的情况下原地对数组进行操作。)
示例 1:输入: nums = [0,1,0,3,12]输出: [1,3,12,0,0]
示例 2:输入: nums = [0]输出: [0]
复制代码


code:


func moveZeroes(nums []int) {    l, r, n := 0, 0, len(nums)    for r < n {        if nums[r] != 0 {            nums[l], nums[r] = nums[r], nums[l]            l++        }        r++    }}
复制代码


over~

Review

Rob Pike 的 5 条编程规则


原文地址http://users.ece.utexas.edu/~adnan/pike.html


中文翻译


  • 规则 1:你无法判断一个程序将把时间花在哪里。瓶颈出现在令人惊讶的地方,所以在你证明瓶颈所在之前,不要试图事后猜测并进行速度破解。

  • 规则 2:测量在测量完之前不要调整速度,即使这样也不要调整,除非代码的一部分压倒了其他部分。

  • 规则 3:当 n 很小时,花式算法会很慢,而 n 通常很小。新奇的算法有很大的常数。 在你知道 n 经常会很大之前,不要幻想。(即使 n 变大,也要先使用规则 2。)

  • 规则 4:花哨的算法比简单的算法更容易出错,而且更难实现。使用简单的算法以及简单的数据结构。

  • 规则 5:数据占主导地位。如果你选择了正确的数据结构并很好地组织了事情,那么算法几乎总是不言自明的。编程的核心是数据结构,而不是算法。

Technique/Tips

本次分享的小技巧:基于 Gin 框架的 HTTP 接口限速实践


在当今的微服务架构和 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、提高用户体验等。

Share

分布式共识的算法与思想随着分布式系统的普及,分布式共识算法成为了分布式系统中必不可少的一部分。而经典的分布式共识算法也不断衍生出了几种不同的类型。


(1)Paxos 算法 Paxos 算法是一种基于消息传递且具有高度容错性的分布式共识算法。它能够确保在存在网络分区或节点失效情况下,仍能够达成正确的共识结果。Paxos 算法的核心思想是通过多轮的投票来达成一致意见。每一轮投票中,节点会对一个提案进行投票,当达到法定人数时,便可以认为该提案获得了通过。


Multi-Paxos 是 Basic Paxos 的改进版,是将 Basic Paxos 实例执行多次,对一系列值达成共识,同时它也是一部分分布式共识算法的统称,可以说是一种思想。


(2)ZAB 算法 ZAB 算法是 Zookeeper Atomic Broadcast 的缩写,是 Zookeeper 保证数据一致性的核心算法。ZAB 算法是为分布式协调服务 Zookeeper 专门设计的一种支持崩溃恢复的原子广播协议 。


ZAB 算法的主要作用是保证分布式系统中各个节点之间的数据一致性。在 ZAB 算法中,有两种角色:Leader 和 Follower。Leader 负责处理客户端请求,并将数据同步到所有 Follower 节点上;Follower 节点则负责接收 Leader 节点的数据,并将其写入本地磁盘。当 Leader 节点发生故障时,Follower 节点会根据 ZAB 算法选举出新的 Leader 节点,并重新同步数据 。


(3)Raft 算法 Raft 算法是 Paxos 算法的一种改进版,具体的说是基于 Multi-Paxos,其主要思想是将多个领导者(Leader)缩减为一个主节点(Leader),同时将投票阶段拆分为多个步骤,使得系统更容易理解和维护。Raft 算法通过选举产生主节点,主节点负责管理日志的复制和一致性验证等工作,而其他节点只负责接收主节点发送的消息并进行响应。


(4)Gossip 协议 Gossip 协议是一种基于信息传播的分布式共识算法。它通过节点之间的互相交流来达成共识,并将共识结果广播给其他节点。与 Paxos 和 Raft 等算法不同,Gossip 协议不需要固定的领导节点,也没有明确的提案过程,而是通过对节点之间消息的传播来实现共识。



小结

本次的分享就到这里喽,总体来说每天花一些时间来分别研究这几件事情,然后一周的周末来一个总结,这样的方式也是相当不错的。

发布于: 刚刚阅读数: 5
用户头像

海风极客

关注

做兴趣使然的Hero 2021-01-14 加入

Just do it.

评论

发布
暂无评论
ARTS 打卡 第二周,业精于勤_ARTS 打卡计划_海风极客_InfoQ写作社区