成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

從 Discord 的做法中學習 — 使用 Golang 進行請求合并

開發 后端
在這篇文章中,我們將討論Discord對數據服務的方法,并探討如何利用Golang的并發特性來減少特定情況下的數據庫負載。

正如你可能之前看到的,Discord去年發布了一篇有價值的文章,討論了他們成功存儲了數萬億條消息。雖然有很多關于這篇文章的YouTube視頻和文章,但我認為這篇文章中一個名為“數據服務為數據服務”的部分沒有得到足夠的關注。在這篇文章中,我們將討論Discord對數據服務的方法,并探討如何利用Golang的并發特性來減少特定情況下的數據庫負載。

數據服務拯救熱分區

如你所知,消息和頻道是Discord中最常用的組件。讓我們想象一個場景:一個擁有50萬成員的頻道的管理員提到@everyone。會發生什么?成千上萬個同時的請求直接指向那個數據庫分區,所有請求的目標都是檢索相同的消息。這種模式重復發生,直到該分區無法回應其他請求。

Discord引入了一個位于Python API和數據庫集群之間的中間服務 — 他們稱之為數據服務。這個服務大致包含每個查詢一個gRPC端點,沒有任何業務邏輯。對Discord來說,這個服務的重要特性就是請求合并。

請求合并

正如我們之前討論過的,每當在一個龐大的頻道中有提及時,就會有大量類似的請求直接指向數據庫分區。通過合并這些請求,如果多個用戶請求相同的數據庫行,我們可以將這些請求合并成一個選擇查詢,并執行該查詢。

通過使用數據服務而不是直接連接到數據庫,我們可以實現許多令人興奮的功能,比如批量查詢,這些功能可以顯著減少數據庫開銷,并改善查詢的平均值,特別是第99百分位數。

使用Golang實現簡單的請求合并

與許多其他公司一樣,Discord使用Python作為其主要的后端語言。無論是微服務還是單體架構,后端服務通常直接連接到數據源進行查詢。雖然Python確實是一種多功能語言,但在并發性方面存在一些不足。使用Python實現并發和高吞吐量的服務可能有些挑戰,而性能與用C++、Rust和Golang等編譯語言編寫的類似服務相比,往往會較低。

在進行任何操作之前,讓我們模擬一下提到的情況。假設服務總共收到了5,000個請求,其中并發數為1,000。

  • 總請求數: 5,000
  • 并發數: 1,000
  • 需要檢索的唯一消息數: 100
type Message struct {
   gorm.Model

   Text string
   User string // some random properties that a message row may have
}


func generateRandomData(db *gorm.DB) {
 for i := 0; i < 100; i++ {
  msg := &messages.Message{Text: fmt.Sprintf("Message #%d", i)}
  db.Save(msg)
 }
}

我使用Gorm構建了一個簡單的數據庫模型來表示Message(消息)表,然后向表中填充了100條虛擬消息。

e := echo.New()
e.GET("/randomMessage", func(c echo.Context) error {
   randomMessageID := rand.Intn(100)
   var msg messages.Message
   if err := db.Where("id=?", randomMessageID).First(&msg).Error; err != nil {
      return err
   }
   return c.JSON(200, msg)
})
e.Logger.Fatal(e.Start(":1323"))

我創建了一個簡單的端點來模擬對0到100之間的隨機ID進行SELECT查詢。現在我們可以對這個端點進行基準測試,模擬在這種情況下會發生什么。

img

  • 平均每秒請求數 (RPS): 300
  • 平均響應時間: 3.2秒
  • 50% 響應時間: 546毫秒
  • 99% 響應時間: 14.7秒

如果我們有10秒的超時策略,大約有2%的請求將收不到響應。現在讓我們改變代碼。Golang有一個名為“single flight”的內置包。這個包提供了重復函數調用抑制機制。一般來說,你給它一個鍵和一個函數,而不是多次運行該函數,SingleFlight會暫時保持其他調用,直到第一次調用完成其請求并以相同的結果作出響應。

var g = singleflight.Group{}
e.GET("/randomMessage", func(c echo.Context) error {
   randomMessageID := rand.Intn(100)
   msg, err, _ := g.Do(fmt.Sprint(randomMessageID), func() (interface{}, error) {
      var msg messages.Message
      if err := db.Where("id=?", randomMessageID).First(&msg).Error; err != nil {
         return nil, err
      }
      return &msg, nil
   })
   if err != nil {
      return err
   }
   return c.JSON(200, msg)
})

func (g *Group) Do(key string, fn func() (interface{}, error)) (v interface{}, err error, shared bool)

Do 執行并返回給定函數的結果,確保同一時間針對給定鍵只有一個執行過程。如果出現重復,重復的調用者會等待原始調用完成并接收相同的結果。返回值 shared 表示是否將 v 給了多個調用者。

現在讓我們重新運行模擬并比較結果。

  • 平均每秒請求數 (RPS): 2309
  • 平均響應時間: 433毫秒
  • 50% 響應時間: 389毫秒
  • 99% 響應時間: 777毫秒

正如你所看到的,僅使用了一個簡單的技術就將第99百分位數減少了14秒,新方法支持的每秒請求次數提高了7.6倍。

結論

從那時起我們就注意到,通過優化數據庫查詢,可以大大提高應用程序的整體性能。雖然我們討論的方法是情景性的,但Discord已經使用了一年多,對他們有很大幫助。

你應該知道,如果你使用數據服務,你將面臨其他的復雜情況。例如,你可能會有多個數據服務實例,而你的Python API必須有一種機制將類似的請求發送到同一個實例。

責任編輯:趙寧寧 來源: 技術的游戲
相關推薦

2009-03-26 09:16:34

微軟裁員職位

2024-07-31 15:36:00

2013-08-19 12:46:27

2009-12-31 10:49:36

VPN配置實例

2021-03-24 10:31:10

JS函數式編程前端

2024-01-07 13:25:32

Go編程代碼

2024-01-15 06:45:29

Go編程代碼

2024-09-18 09:50:00

大模型AI

2021-03-31 08:35:40

人工智能AIFacebook

2019-01-10 13:58:02

2023-11-21 22:48:50

2021-03-09 09:55:02

Vuejs前端代碼

2021-08-14 08:17:49

Android設計模式OKHttp

2023-03-13 07:40:44

高并發golang

2020-03-16 08:35:11

vue開發組件

2021-10-11 09:55:58

Facebook業務中斷網絡安全

2018-08-14 05:34:19

2020-05-19 13:46:33

勒索軟件信息安全攻擊

2017-01-03 15:35:16

CTFUSB流量

2017-12-01 08:54:18

SpringCloudHystrix
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 国产激情视频网 | 色综合天天天天做夜夜夜夜做 | 欧美视频成人 | 日韩色图在线观看 | 91精品国产91久久久久久三级 | 国产精品亚洲综合 | 成人在线一区二区 | 日韩在线第一 | 在线免费国产视频 | 观看av| 精品无码久久久久久国产 | 欧美精品片 | 日韩av黄色| 天天看天天爽 | 夜夜骑首页 | 亚洲永久在线 | 亚洲婷婷六月天 | 99精品久久久久久久 | 国久久 | 久草网站| 免费看91| 一区二区三区中文字幕 | 91在线观看视频 | 奇米av| 日韩在线资源 | a在线观看 | 久久久久久国产一区二区三区 | 五月婷婷中文 | 一区二区在线观看免费视频 | 韩日精品视频 | 另类二区 | 毛片视频免费观看 | 精品免费国产一区二区三区四区介绍 | 久久免费精品 | 亚洲一区二区在线免费观看 | 一级黄色片免费在线观看 | 中国一级特黄毛片大片 | 亚洲成av人片在线观看 | 国精产品一区二区三区 | 日韩成人一区 | 91高清免费观看 |