12306搶票帶來的啓示:看我如何用Go實現百萬QPS的秒殺系統

本文爲開源實驗性工程:「github.com/GuoZhaoran/spikeSystem」的配套文章,原做者:「繪你一世傾城」,現爲:獵豹移動php開發工程師,感謝原做者的技術分享。php

一、引言

Go語言的出現,讓開發高性能、高穩定性服務端系統變的容易,與高貴冷豔的Erlang語言不一樣的是,Go語言簡單易學,在高性能服務端架構中的應用愈來愈普遍。html

對於即時通信(IM系統、消息推送系統等高性能實時通訊等)等場景下,Go語言已經被愈來愈多的用於核心通訊模塊中。本文內容雖是從秒殺系統談起,並未直接涉及即時通信相關知識,但有關Go的高併發實踐,仍然值得廣大即時通信技術愛好者們研究和學習,必竟業務能夠不一樣,但技術都是相通的,或許能爲你即時通信系統的高併發架構帶來新的思路和靈感。技多不壓身,殺望對你有所幫助!nginx

本文所述並不是純理論水文,均以已付實踐,配套源碼請見:git

1)主要地址: https://github.com/GuoZhaoran/spikeSystem
2)備用地址: https://github.com/52im/spikeSystem

友情提示:本文適合有必定高併發服務端設計經驗的開發者閱讀,如您對這方面知識瞭解太少,請務必先按序閱讀如下文章:github

新手入門:零基礎理解大型分佈式架構的演進歷史、技術原理、最佳實踐
一篇讀懂分佈式架構下的負載均衡技術:分類、原理、算法、常見方案等
重新手到架構師,一篇就夠:從100到1000萬高併發的架構演進之路
騰訊資深架構師乾貨總結:一文讀懂大型分佈式系統設計的方方面面
快速理解高性能HTTP服務端的負載均衡技術原理

(本文同步發佈於:http://www.52im.net/thread-2771-1-1.htmlredis

二、12306搶票,極限併發帶來的思考

雖然如今大多數狀況下都能訂到票,可是放票瞬間即無票的場景,相信你們都深有體會。算法

尤爲是春節期間,你們不只使用 12306,還會考慮「智行」和其餘的搶票軟件,全國上下幾億人在這段時間都在搶票。數據庫

「12306 服務」承受着這個世界上任何秒殺系統都沒法超越的 QPS,上百萬的併發再正常不過了!apache

筆者專門研究了一下「12306」的服務端架構,學習到了其系統設計上不少亮點,在這裏和你們分享一下並模擬一個例子:如何在 100 萬人同時搶 1 萬張火車票時,系統提供正常、穩定的服務。後端

三、大型高併發系統架構

高併發的系統架構都會採用分佈式集羣部署,服務上層有着層層負載均衡,並提供各類容災手段(雙火機房、節點容錯、服務器災備等)保證系統的高可用,流量也會根據不一樣的負載能力和配置策略均衡到不一樣的服務器上。

下邊是一個簡單的示意圖:

3.1 負載均衡簡介

上圖中描述了用戶請求到服務器經歷了三層的負載均衡,下邊分別簡單介紹一下這三種負載均衡。

① OSPF(開放式最短鏈路優先)是一個內部網關協議(Interior Gateway Protocol,簡稱 IGP)

OSPF 經過路由器之間通告網絡接口的狀態來創建鏈路狀態數據庫,生成最短路徑樹,OSPF 會自動計算路由接口上的 Cost 值,但也能夠經過手工指定該接口的 Cost 值,手工指定的優先於自動計算的值。

OSPF 計算的 Cost,一樣是和接口帶寬成反比,帶寬越高,Cost 值越小。到達目標相同 Cost 值的路徑,能夠執行負載均衡,最多 6 條鏈路同時執行負載均衡。

② LVS (Linux Virtual Server)

它是一種集羣(Cluster)技術,採用 IP 負載均衡技術和基於內容請求分發技術。

調度器具備很好的吞吐率,將請求均衡地轉移到不一樣的服務器上執行,且調度器自動屏蔽掉服務器的故障,從而將一組服務器構成一個高性能的、高可用的虛擬服務器。

③ Nginx

想必你們都很熟悉了,是一款很是高性能的 HTTP 代理/反向代理服務器,服務開發中也常用它來作負載均衡。

Nginx 實現負載均衡的方式主要有三種:

1)輪詢;
2)加權輪詢;
3)IP Hash 輪詢。

下面咱們就針對 Nginx 的加權輪詢作專門的配置和測試。

3.2 Nginx 加權輪詢的演示

Nginx 實現負載均衡經過 Upstream 模塊實現,其中加權輪詢的配置是能夠給相關的服務加上一個權重值,配置的時候可能根據服務器的性能、負載能力設置相應的負載。

下面是一個加權輪詢負載的配置,我將在本地的監聽 3001-3004 端口,分別配置 1,2,3,4 的權重:

配置負載均衡

    upstream load_rule {
       server 127.0.0.1:3001 weight=1;
       server 127.0.0.1:3002 weight=2;
       server 127.0.0.1:3003 weight=3;
       server 127.0.0.1:3004 weight=4;
    }
    ...
    server {
    listen       80;
    server_name  load_balance.com [url=http://www.load_balance.com]www.load_balance.com[/url];
    location / {
       proxy_pass http://load_rule;
    }
}

我在本地 /etc/hosts 目錄下配置了 www.load_balance.com 的虛擬域名地址。

接下來使用 Go 語言開啓四個 HTTP 端口監聽服務,下面是監聽在 3001 端口的 Go 程序,其餘幾個只須要修改端口便可:

package main
import(
    "net/http"
    "os"
    "strings"
)

func main() {
    http.HandleFunc("/buy/ticket", handleReq)
    http.ListenAndServe(":3001", nil)
}

//處理請求函數,根據請求將響應結果信息寫入日誌
func handleReq(w http.ResponseWriter, r *http.Request) {
    failedMsg :=  "handle in port:"
    writeLog(failedMsg, "./stat.log")
}

//寫入日誌
func writeLog(msg string, logPath string) {
    fd, _ := os.OpenFile(logPath, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0644)
    defer fd.Close()
    content := strings.Join([]string{msg, "\r\n"}, "3001")
    buf := []byte(content)
    fd.Write(buf)
}

我將請求的端口日誌信息寫到了 ./stat.log 文件當中,而後使用 AB 壓測工具作壓測:

ab -n 1000 -c 100 http://www.load_balance.com/buy/ticket

統計日誌中的結果,3001-3004 端口分別獲得了 100、200、300、400 的請求量。

這和我在 Nginx 中配置的權重佔比很好的吻合在了一塊兒,而且負載後的流量很是的均勻、隨機。

具體的實現你們能夠參考 Nginx 的 Upsteam 模塊實現源碼,這裏推薦一篇文章《Nginx 中 Upstream 機制的負載均衡》。

四、秒殺搶購系統選型

回到咱們最初提到的問題中來:火車票秒殺系統如何在高併發狀況下提供正常、穩定的服務呢?

從上面的介紹咱們知道用戶秒殺流量經過層層的負載均衡,均勻到了不一樣的服務器上,即便如此,集羣中的單機所承受的 QPS 也是很是高的。如何將單機性能優化到極致呢?

要解決這個問題,咱們就要想明白一件事:通常訂票系統要處理生成訂單、減扣庫存、用戶支付這三個基本的階段。

咱們系統要作的事情是要保證火車票訂單不超賣、很多賣,每張售賣的車票都必須支付纔有效,還要保證系統承受極高的併發。

這三個階段的前後順序該怎麼分配才更加合理呢?咱們來分析一下。。。

4.1 下單減庫存

當用戶併發請求到達服務端時,首先建立訂單,而後扣除庫存,等待用戶支付。

這種順序是咱們通常人首先會想到的解決方案,這種狀況下也能保證訂單不會超賣,由於建立訂單以後就會減庫存,這是一個原子操做。

可是這樣也會產生一些問題:

1)在極限併發狀況下,任何一個內存操做的細節都相當影響性能,尤爲像建立訂單這種邏輯,通常都須要存儲到磁盤數據庫的,對數據庫的壓力是可想而知的;

2)若是用戶存在惡意下單的狀況,只下單不支付這樣庫存就會變少,會少賣不少訂單,雖然服務端能夠限制 IP 和用戶的購買訂單數量,這也不算是一個好方法。

4.2 支付減庫存

若是等待用戶支付了訂單在減庫存,第一感受就是不會少賣。可是這是併發架構的大忌,由於在極限併發狀況下,用戶可能會建立不少訂單。

當庫存減爲零的時候不少用戶發現搶到的訂單支付不了了,這也就是所謂的「超賣」。也不能避免併發操做數據庫磁盤 IO。

4.3 預扣庫存

從上邊兩種方案的考慮,咱們能夠得出結論:只要建立訂單,就要頻繁操做數據庫 IO。

那麼有沒有一種不須要直接操做數據庫 IO 的方案呢,這就是預扣庫存。先扣除了庫存,保證不超賣,而後異步生成用戶訂單,這樣響應給用戶的速度就會快不少;那麼怎麼保證很多賣呢?用戶拿到了訂單,不支付怎麼辦?

咱們都知道如今訂單都有有效期,好比說用戶五分鐘內不支付,訂單就失效了,訂單一旦失效,就會加入新的庫存,這也是如今不少網上零售企業保證商品很多賣採用的方案。

訂單的生成是異步的,通常都會放到 MQ、Kafka 這樣的即時消費隊列中處理,訂單量比較少的狀況下,生成訂單很是快,用戶幾乎不用排隊。

五、扣庫存的藝術

從上面的分析可知,顯然預扣庫存的方案最合理。咱們進一步分析扣庫存的細節,這裏還有很大的優化空間,庫存存在哪裏?怎樣保證高併發下,正確的扣庫存,還能快速的響應用戶請求?

在單機低併發狀況下,咱們實現扣庫存一般是這樣的:

爲了保證扣庫存和生成訂單的原子性,須要採用事務處理,而後取庫存判斷、減庫存,最後提交事務,整個流程有不少 IO,對數據庫的操做又是阻塞的。

這種方式根本不適合高併發的秒殺系統。接下來咱們對單機扣庫存的方案作優化:本地扣庫存

咱們把必定的庫存量分配到本地機器,直接在內存中減庫存,而後按照以前的邏輯異步建立訂單。

改進過以後的單機系統是這樣的:

這樣就避免了對數據庫頻繁的 IO 操做,只在內存中作運算,極大的提升了單機抗併發的能力。

可是百萬的用戶請求量單機是不管如何也抗不住的,雖然 Nginx 處理網絡請求使用 Epoll 模型,c10k 的問題在業界早已獲得瞭解決。

可是 Linux 系統下,一切資源皆文件,網絡請求也是這樣,大量的文件描述符會使操做系統瞬間失去響應。

上面咱們提到了 Nginx 的加權均衡策略,咱們不妨假設將 100W 的用戶請求量平均均衡到 100 臺服務器上,這樣單機所承受的併發量就小了不少。

而後咱們每臺機器本地庫存 100 張火車票,100 臺服務器上的總庫存仍是 1 萬,這樣保證了庫存訂單不超賣。

下面是咱們描述的集羣架構:

問題接踵而至,在高併發狀況下,如今咱們還沒法保證系統的高可用,假如這 100 臺服務器上有兩三臺機器由於扛不住併發的流量或者其餘的緣由宕機了。那麼這些服務器上的訂單就賣不出去了,這就形成了訂單的少賣。

要解決這個問題,咱們須要對總訂單量作統一的管理,這就是接下來的容錯方案。服務器不只要在本地減庫存,另外要遠程統一減庫存。

有了遠程統一減庫存的操做,咱們就能夠根據機器負載狀況,爲每臺機器分配一些多餘的「Buffer 庫存」用來防止機器中有機器宕機的狀況。

咱們結合下面架構圖具體分析一下:

咱們採用 Redis 存儲統一庫存,由於 Redis 的性能很是高,號稱單機 QPS 能抗 10W 的併發。

在本地減庫存之後,若是本地有訂單,咱們再去請求 Redis 遠程減庫存,本地減庫存和遠程減庫存都成功了,才返回給用戶搶票成功的提示,這樣也能有效的保證訂單不會超賣。

當機器中有機器宕機時,由於每一個機器上有預留的 Buffer 餘票,因此宕機機器上的餘票依然可以在其餘機器上獲得彌補,保證了很多賣。

Buffer 餘票設置多少合適呢,理論上 Buffer 設置的越多,系統容忍宕機的機器數量就越多,可是 Buffer 設置的太大也會對 Redis 形成必定的影響。

雖然 Redis 內存數據庫抗併發能力很是高,請求依然會走一次網絡 IO,其實搶票過程當中對 Redis 的請求次數是本地庫存和 Buffer 庫存的總量。

由於當本地庫存不足時,系統直接返回用戶「已售罄」的信息提示,就不會再走統一扣庫存的邏輯。

這在必定程度上也避免了巨大的網絡請求量把 Redis 壓跨,因此 Buffer 值設置多少,須要架構師對系統的負載能力作認真的考量。

六、動手寫代碼

Go 語言原生爲併發設計,我採用 Go 語言給你們演示一下單機搶票的具體流程。

6.1 初始化工做

Go 包中的 Init 函數先於 Main 函數執行,在這個階段主要作一些準備性工做。

咱們系統須要作的準備工做有:初始化本地庫存、初始化遠程 Redis 存儲統一庫存的 Hash 鍵值、初始化 Redis 鏈接池。

另外還須要初始化一個大小爲 1 的 Int 類型 Chan,目的是實現分佈式鎖的功能。

也能夠直接使用讀寫鎖或者使用 Redis 等其餘的方式避免資源競爭,但使用 Channel 更加高效,這就是 Go 語言的哲學:不要經過共享內存來通訊,而要經過通訊來共享內存。

Redis 庫使用的是 Redigo,下面是代碼實現:

...
//localSpike包結構體定義
packagelocalSpike

typeLocalSpike struct{
    LocalInStock     int64
    LocalSalesVolume int64
}
...
//remoteSpike對hash結構的定義和redis鏈接池
packageremoteSpike
//遠程訂單存儲健值
typeRemoteSpikeKeys struct{
    SpikeOrderHashKey string//redis中秒殺訂單hash結構key
    TotalInventoryKey string//hash結構中總訂單庫存key
    QuantityOfOrderKey string//hash結構中已有訂單數量key
}

//初始化redis鏈接池
funcNewPool() *redis.Pool {
    return&redis.Pool{
        MaxIdle:   10000,
        MaxActive: 12000, // max number of connections
        Dial: func() (redis.Conn, error) {
            c, err := redis.Dial("tcp", ":6379")
            iferr != nil{
                panic(err.Error())
            }
            returnc, err
        },
    }
}
...
funcinit() {
    localSpike = localSpike2.LocalSpike{
        LocalInStock:     150,
        LocalSalesVolume: 0,
    }
    remoteSpike = remoteSpike2.RemoteSpikeKeys{
        SpikeOrderHashKey:  "ticket_hash_key",
        TotalInventoryKey:  "ticket_total_nums",
        QuantityOfOrderKey: "ticket_sold_nums",
    }
    redisPool = remoteSpike2.NewPool()
    done = make(chanint, 1)
    done <- 1
}

6.2 本地扣庫存和統一扣庫存

本地扣庫存邏輯很是簡單,用戶請求過來,添加銷量,而後對比銷量是否大於本地庫存,返回 Bool 值:

package localSpike
//本地扣庫存,返回bool值
func(spike *LocalSpike) LocalDeductionStock() bool{
    spike.LocalSalesVolume = spike.LocalSalesVolume + 1
    returnspike.LocalSalesVolume < spike.LocalInStock
}

注意這裏對共享數據 LocalSalesVolume 的操做是要使用鎖來實現的,可是由於本地扣庫存和統一扣庫存是一個原子性操做,因此在最上層使用 Channel 來實現,這塊後邊會講。

統一扣庫存操做 Redis,由於 Redis 是單線程的,而咱們要實現從中取數據,寫數據並計算一些列步驟,咱們要配合 Lua 腳本打包命令,保證操做的原子性:

package remoteSpike
......
constLuaScript = `
        local ticket_key = KEYS[1]
        local ticket_total_key = ARGV[1]
        local ticket_sold_key = ARGV[2]
        local ticket_total_nums = tonumber(redis.call('HGET', ticket_key, ticket_total_key))
        local ticket_sold_nums = tonumber(redis.call('HGET', ticket_key, ticket_sold_key))
        -- 查看是否還有餘票,增長訂單數量,返回結果值
       if(ticket_total_nums >= ticket_sold_nums) then
            return redis.call('HINCRBY', ticket_key, ticket_sold_key, 1)
        end
        return 0
`
//遠端統一扣庫存
func(RemoteSpikeKeys *RemoteSpikeKeys) RemoteDeductionStock(conn redis.Conn) bool{
    lua := redis.NewScript(1, LuaScript)
    result, err := redis.Int(lua.Do(conn, RemoteSpikeKeys.SpikeOrderHashKey, RemoteSpikeKeys.TotalInventoryKey, RemoteSpikeKeys.QuantityOfOrderKey))
    iferr != nil{
        returnfalse
    }
    returnresult != 0
}

咱們使用 Hash 結構存儲總庫存和總銷量的信息,用戶請求過來時,判斷總銷量是否大於庫存,而後返回相關的 Bool 值。

在啓動服務以前,咱們須要初始化 Redis 的初始庫存信息:

1hmset ticket_hash_key "ticket_total_nums"10000 "ticket_sold_nums"0

6.3 響應用戶信息

咱們開啓一個 HTTP 服務,監聽在一個端口上:

package main
...
funcmain() {
    http.HandleFunc("/buy/ticket", handleReq)
    http.ListenAndServe(":3005", nil)
}

上面咱們作完了全部的初始化工做,接下來 handleReq 的邏輯很是清晰,判斷是否搶票成功,返回給用戶信息就能夠了。

package main
//處理請求函數,根據請求將響應結果信息寫入日誌
funchandleReq(w http.ResponseWriter, r *http.Request) {
    redisConn := redisPool.Get()
    LogMsg := ""
    <-done
    //全局讀寫鎖
    iflocalSpike.LocalDeductionStock() && remoteSpike.RemoteDeductionStock(redisConn) {
        util.RespJson(w, 1,  "搶票成功", nil)
        LogMsg = LogMsg + "result:1,localSales:"+ strconv.FormatInt(localSpike.LocalSalesVolume, 10)
    } else{
        util.RespJson(w, -1, "已售罄", nil)
        LogMsg = LogMsg + "result:0,localSales:"+ strconv.FormatInt(localSpike.LocalSalesVolume, 10)
    }
    done <- 1

    //將搶票狀態寫入到log中
    writeLog(LogMsg, "./stat.log")
}

funcwriteLog(msg string, logPath string) {
    fd, _ := os.OpenFile(logPath, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0644)
    deferfd.Close()
    content := strings.Join([]string{msg, "\r\n"}, "")
    buf := []byte(content)
    fd.Write(buf)
}

前邊提到咱們扣庫存時要考慮競態條件,咱們這裏是使用 Channel 避免併發的讀寫,保證了請求的高效順序執行。咱們將接口的返回信息寫入到了 ./stat.log 文件方便作壓測統計。

6.4 單機服務壓測

開啓服務,咱們使用 AB 壓測工具進行測試:

ab -n 10000 -c 100 http://127.0.0.1:3005/buy/ticket

下面是我本地低配 Mac 的壓測信息:

This is ApacheBench, Version 2.3 <$Revision: 1826891 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, [url= http://www.zeustech.net/] http://www.zeustech.net/[/url]
Licensed to The Apache Software Foundation, [url= http://www.apache.org/] http://www.apache.org/[/url]

Benchmarking 127.0.0.1 (be patient)
Completed 1000 requests
Completed 2000 requests
Completed 3000 requests
Completed 4000 requests
Completed 5000 requests
Completed 6000 requests
Completed 7000 requests
Completed 8000 requests
Completed 9000 requests
Completed 10000 requests
Finished 10000 requests

Server Software:
Server Hostname:        127.0.0.1
Server Port:            3005

Document Path:          /buy/ticket
Document Length:        29 bytes

Concurrency Level:      100
Time taken fortests:   2.339 seconds
Complete requests:      10000
Failed requests:        0
Total transferred:      1370000 bytes
HTML transferred:       290000 bytes
Requests per second:    4275.96 [#/sec] (mean)
Time per request:       23.387 [ms] (mean)
Time per request:       0.234 [ms] (mean, across all concurrent requests)
Transfer rate:          572.08 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        0    8  14.7      6     223
Processing:     2   15  17.6     11     232
Waiting:        1   11  13.5      8     225
Total:          7   23  22.8     18     239

Percentage of the requests served within a certain time(ms)
  50%     18
  66%     24
  75%     26
  80%     28
  90%     33
  95%     39
  98%     45
  99%     54
 100%    239 (longest request)

根據指標顯示,我單機每秒就能處理 4000+ 的請求,正常服務器都是多核配置,處理 1W+ 的請求根本沒有問題。

並且查看日誌發現整個服務過程當中,請求都很正常,流量均勻,Redis 也很正常:

//stat.log
...
result:1,localSales:145
result:1,localSales:146
result:1,localSales:147
result:1,localSales:148
result:1,localSales:149
result:1,localSales:150
result:0,localSales:151
result:0,localSales:152
result:0,localSales:153
result:0,localSales:154
result:0,localSales:156
...

七、本文小結

整體來講,秒殺系統是很是複雜的。咱們這裏只是簡單介紹模擬了一下單機如何優化到高性能,集羣如何避免單點故障,保證訂單不超賣、很多賣的一些策略,完整的訂單系統還有訂單進度的查看,每臺服務器上都有一個任務,定時的從總庫存同步餘票和庫存信息展現給用戶,還有用戶在訂單有效期內不支付,釋放訂單,補充到庫存等等。

咱們實現了高併發搶票的核心邏輯,能夠說系統設計的很是的巧妙,巧妙的避開了對 DB 數據庫 IO 的操做。

對 Redis 網絡 IO 的高併發請求,幾乎全部的計算都是在內存中完成的,並且有效的保證了不超賣、很多賣,還可以容忍部分機器的宕機。

我以爲其中有兩點特別值得學習總結的。

① 負載均衡,分而治之:

經過負載均衡,將不一樣的流量劃分到不一樣的機器上,每臺機器處理好本身的請求,將本身的性能發揮到極致。

這樣系統的總體也就能承受極高的併發了,就像工做的一個團隊,每一個人都將本身的價值發揮到了極致,團隊成長天然是很大的。

② 合理的使用併發和異步:

自 Epoll 網絡架構模型解決了 c10k 問題以來,異步愈來愈被服務端開發人員所接受,可以用異步來作的工做,就用異步來作,在功能拆解上能達到意想不到的效果。

這點在 Nginx、Node.JS、Redis 上都能體現,他們處理網絡請求使用的 Epoll 模型,用實踐告訴了咱們單線程依然能夠發揮強大的威力。

服務器已經進入了多核時代,Go 語言這種天生爲併發而生的語言,完美的發揮了服務器多核優點,不少能夠併發處理的任務均可以使用併發來解決,好比 Go 處理 HTTP 請求時每一個請求都會在一個 Goroutine 中執行。

總之,怎樣合理的壓榨 CPU,讓其發揮出應有的價值,是咱們一直須要探索學習的方向。

附錄:更多高併發架構設計方面的文章

[1] 通用架構設計相關文章:
騰訊資深架構師乾貨總結:一文讀懂大型分佈式系統設計的方方面面
快速理解高性能HTTP服務端的負載均衡技術原理
子彈短信光鮮的背後:網易雲信首席架構師分享億級IM平臺的技術實踐
知乎技術分享:從單機到2000萬QPS併發的Redis高性能緩存實踐之路
新手入門:零基礎理解大型分佈式架構的演進歷史、技術原理、最佳實踐
阿里技術分享:深度揭祕阿里數據庫技術方案的10年變遷史
阿里技術分享:阿里自研金融級數據庫OceanBase的艱辛成長之路
達達O2O後臺架構演進實踐:從0到4000高併發請求背後的努力
優秀後端架構師必會知識:史上最全MySQL大表優化方案總結
小米技術分享:解密小米搶購系統千萬高併發架構的演進和實踐
一篇讀懂分佈式架構下的負載均衡技術:分類、原理、算法、常見方案等
通俗易懂:如何設計能支撐百萬併發的數據庫架構?
多維度對比5款主流分佈式MQ消息隊列,媽媽不再擔憂個人技術選型了
重新手到架構師,一篇就夠:從100到1000萬高併發的架構演進之路
美團技術分享:深度解密美團的分佈式ID生成算法
12306搶票帶來的啓示:看我如何用Go實現百萬QPS的秒殺系統(含源碼)
>>  更多同類文章 ……
[2] 有關IM架構設計的文章:
淺談IM系統的架構設計
簡述移動端IM開發的那些坑:架構設計、通訊協議和客戶端
一套海量在線用戶的移動端IM架構設計實踐分享(含詳細圖文)
一套原創分佈式即時通信(IM)系統理論架構方案
從零到卓越:京東客服即時通信系統的技術架構演進歷程
蘑菇街即時通信/IM服務器開發之架構選擇
騰訊QQ1.4億在線用戶的技術挑戰和架構演進之路PPT
微信後臺基於時間序的海量數據冷熱分級架構設計實踐
微信技術總監談架構:微信之道——大道至簡(演講全文)
如何解讀《微信技術總監談架構:微信之道——大道至簡》
快速裂變:見證微信強大後臺架構從0到1的演進歷程(一)
17年的實踐:騰訊海量產品的技術方法論
移動端IM中大規模羣消息的推送如何保證效率、實時性?
現代IM系統中聊天消息的同步和存儲方案探討
IM開發基礎知識補課(二):如何設計大量圖片文件的服務端存儲架構?
IM開發基礎知識補課(三):快速理解服務端數據庫讀寫分離原理及實踐建議
IM開發基礎知識補課(四):正確理解HTTP短鏈接中的Cookie、Session和Token
WhatsApp技術實踐分享:32人工程團隊創造的技術神話
微信朋友圈千億訪問量背後的技術挑戰和實踐總結
王者榮耀2億用戶量的背後:產品定位、技術架構、網絡方案等
IM系統的MQ消息中間件選型:Kafka仍是RabbitMQ?
騰訊資深架構師乾貨總結:一文讀懂大型分佈式系統設計的方方面面
以微博類應用場景爲例,總結海量社交系統的架構設計步驟
快速理解高性能HTTP服務端的負載均衡技術原理
子彈短信光鮮的背後:網易雲信首席架構師分享億級IM平臺的技術實踐
知乎技術分享:從單機到2000萬QPS併發的Redis高性能緩存實踐之路
IM開發基礎知識補課(五):通俗易懂,正確理解並用好MQ消息隊列
微信技術分享:微信的海量IM聊天消息序列號生成實踐(算法原理篇)
微信技術分享:微信的海量IM聊天消息序列號生成實踐(容災方案篇)
新手入門:零基礎理解大型分佈式架構的演進歷史、技術原理、最佳實踐
一套高可用、易伸縮、高併發的IM羣聊、單聊架構方案設計實踐
阿里技術分享:深度揭祕阿里數據庫技術方案的10年變遷史
阿里技術分享:阿里自研金融級數據庫OceanBase的艱辛成長之路
社交軟件紅包技術解密(一):全面解密QQ紅包技術方案——架構、技術實現等
社交軟件紅包技術解密(二):解密微信搖一搖紅包從0到1的技術演進
社交軟件紅包技術解密(三):微信搖一搖紅包雨背後的技術細節
社交軟件紅包技術解密(四):微信紅包系統是如何應對高併發的
社交軟件紅包技術解密(五):微信紅包系統是如何實現高可用性的
社交軟件紅包技術解密(六):微信紅包系統的存儲層架構演進實踐
社交軟件紅包技術解密(七):支付寶紅包的海量高併發技術實踐
社交軟件紅包技術解密(八):全面解密微博紅包技術方案
社交軟件紅包技術解密(九):談談手Q紅包的功能邏輯、容災、運維、架構等
即時通信新手入門:一文讀懂什麼是Nginx?它可否實現IM的負載均衡?
即時通信新手入門:快速理解RPC技術——基本概念、原理和用途
多維度對比5款主流分佈式MQ消息隊列,媽媽不再擔憂個人技術選型了
從游擊隊到正規軍:馬蜂窩旅遊網的IM系統架構演進之路
IM開發基礎知識補課(六):數據庫用NoSQL仍是SQL?讀這篇就夠了!
>>  更多同類文章 ……

(本文同步發佈於:http://www.52im.net/thread-2771-1-1.html

相關文章
相關標籤/搜索