<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    Jack Jiang

    我的最新工程MobileIMSDK:http://git.oschina.net/jackjiang/MobileIMSDK
    posts - 494, comments - 13, trackbacks - 0, articles - 1

    本文原題“聊聊TCP連接耗時的那些事兒”,本次收錄已征得作者同意,轉載請聯系作者。有少許改動。

    1、系列文章引言

    1.1 文章目的

    作為即時通訊技術的開發者來說,高性能、高并發相關的技術概念早就了然與胸,什么線程池、零拷貝、多路復用、事件驅動、epoll等等名詞信手拈來,又或許你對具有這些技術特征的技術框架比如:Java的Netty、Php的workman、Go的nget等熟練掌握。但真正到了面視或者技術實踐過程中遇到無法釋懷的疑惑時,方知自已所掌握的不過是皮毛。

    返璞歸真、回歸本質,這些技術特征背后的底層原理到底是什么?如何能通俗易懂、毫不費力真正透徹理解這些技術背后的原理,正是《從根上理解高性能、高并發》系列文章所要分享的。

    1.2 文章源起

    我(Jack Jiang)為即時通訊網整理了相當多有關IM、消息推送等即時通訊技術相關的資源和文章,從最開始的開源IM框架MobileIMSDK,到網絡編程經典巨著《TCP/IP詳解》的在線版本,再到IM開發綱領性文章《新手入門一篇就夠:從零開發移動端IM》,以及網絡編程由淺到深的《網絡編程懶人入門》、《腦殘式網絡編程入門》、《高性能網絡編程》、《不為人知的網絡編程》系列文章。

    越往知識的深處走,越覺得對即時通訊技術了解的太少。于是后來,為了讓開發者門更好地從基礎電信技術的角度理解網絡(尤其移動網絡)特性,我跨專業收集整理了《IM開發者的零基礎通信技術入門》系列高階文章。這系列文章已然是普通即時通訊開發者的網絡通信技術知識邊界,加上之前這些網絡編程資料,解決網絡通信方面的知識盲點基本夠用了。

    對于即時通訊IM這種系統的開發來說,網絡通信知識確實非常重要,但回歸到技術本質,實現網絡通信本身的這些技術特征:包括上面提到的線程池、零拷貝、多路復用、事件驅動等等,它們的本質是什么?底層原理又是怎樣?這就是整理本系列文章的目的,希望對你有用。

    1.3 文章目錄

    從根上理解高性能、高并發(一):深入計算機底層,理解線程與線程池》(* 本文

    《從根上理解高性能、高并發(二):深入操作系統,理解I/O與零拷貝技術 (稍后發布..)》

    《從根上理解高性能、高并發(三):深入操作系統,徹底理解I/O多路復用 (稍后發布..)》

    《從根上理解高性能、高并發(四):深入操作系統,徹底理解同步與異步 (稍后發布..)》

    《從根上理解高性能、高并發(五):高并發高性能服務器到底是如何實現的 (稍后發布..)》

    1.4 本篇概述

    本篇是該系列文章的開篇,主要是從CPU這一層來講解多線程以及線程池原理,力求避免復雜的技術概念羅列,盡量做到通俗易懂、老少皆宜。

    2、本文作者

    應作者要求,不提供真名,也不提供個人照片。

    本文作者主要技術方向為互聯網后端、高并發高性能服務器、檢索引擎技術,網名是“碼農的荒島求生”,公眾號“碼農的荒島求生”。感謝作者的無私分享。

    3、一切要從CPU說起

    你可能會有疑問,講多線程為什么要從CPU說起呢?原因很簡單,在這里沒有那些時髦的概念,你可以更加清晰的看清問題的本質。

    實際情況是:CPU并不知道線程、進程之類的概念。

    CPU只知道兩件事:

    • 1)從內存中取出指令;
    • 2)執行指令,然后回到 1)。 

    你看,在這里CPU確實是不知道什么進程、線程之類的概念。

    接下來的問題就是CPU從哪里取出指令呢?答案是來自一個被稱為Program Counter(簡稱PC)的寄存器,也就是我們熟知的程序計數器,在這里大家不要把寄存器想的太神秘,你可以簡單的把寄存器理解為內存,只不過存取速度更快而已。

    PC寄存器中存放的是什么呢?這里存放的是指令在內存中的地址,什么指令呢?是CPU將要執行的下一條指令。

    那么是誰來設置PC寄存器中的指令地址呢?

    原來PC寄存器中的地址默認是自動加1的,這當然是有道理的,因為大部分情況下CPU都是一條接一條順序執行,當遇到if、else時,這種順序執行就被打破了,CPU在執行這類指令時會根據計算結果來動態改變PC寄存器中的值,這樣CPU就可以正確的跳轉到需要執行的指令了。

    聰明的你一定會問,那么PC中的初始值是怎么被設置的呢?

    在回答這個問題之前我們需要知道CPU執行的指令來自哪里?是來自內存,廢話,內存中的指令是從磁盤中保存的可執行程序加載過來的,磁盤中可執行程序是編譯器生成的,編譯器又是從哪里生成的機器指令呢?答案就是我們定義的函數。

    注意是函數,函數被編譯后才會形成CPU執行的指令,那么很自然的,我們該如何讓CPU執行一個函數呢?顯然我們只需要找到函數被編譯后形成的第一條指令就可以了,第一條指令就是函數入口。

    現在你應該知道了吧,我們想要CPU執行一個函數,那么只需要把該函數對應的第一條機器指令的地址寫入PC寄存器就可以了,這樣我們寫的函數就開始被CPU執行起來啦。

    你可能會有疑問,這和線程有什么關系呢?

    4、從CPU到操作系統

    上一小節中我們明白了CPU的工作原理,我們想讓CPU執行某個函數,那么只需要把函數對應的第一條機器執行裝入PC寄存器就可以了,這樣即使沒有操作系統我們也可以讓CPU執行程序,雖然可行但這是一個非常繁瑣的過程。

    我們需要:

    • 1)在內存中找到一塊大小合適的區域裝入程序;
    • 2)找到函數入口,設置好PC寄存器讓CPU開始執行程序。

    這兩個步驟絕不是那么容易的事情,如果每次在執行程序時程序員自己手動實現上述兩個過程會瘋掉的,因此聰明的程序員就會想干脆直接寫個程序來自動完成上面兩個步驟吧。

    機器指令需要加載到內存中執行,因此需要記錄下內存的起始地址和長度;同時要找到函數的入口地址并寫到PC寄存器中,想一想這是不是需要一個數據結構來記錄下這些信息。

    數據結構大致如下:

    struct *** {

       void* start_addr;

       intlen;

       void* start_point;

       ...

    };

    接下來就是起名字時刻。

    這個數據結構總要有個名字吧,這個結構體用來記錄什么信息呢?記錄的是程序在被加載到內存中的運行狀態,程序從磁盤加載到內存跑起來叫什么好呢?干脆就叫進程(Process)好了,我們的指導原則就是一定要聽上去比較神秘,總之大家都不容易弄懂就對了,我將其稱為“弄不懂原則”。

    就這樣進程誕生了。

    CPU執行的第一個函數也起個名字,第一個要被執行的函數聽起來比較重要,干脆就叫main函數吧。

    完成上述兩個步驟的程序也要起個名字,根據“弄不懂原則”這個“簡單”的程序就叫操作系統(Operating System)好啦。

    就這樣操作系統誕生了,程序員要想運行程序再也不用自己手動加載一遍了。

    現在進程和操作系統都有了,一切看上去都很完美。

    5、從單核到多核,如何充分利用多核

    人類的一大特點就是生命不息折騰不止,從單核折騰到了多核。

    這時,假設我們想寫一個程序并且要分利用多核該怎么辦呢?

    有的同學可能會說不是有進程嗎,多開幾個進程不就可以了?

    聽上去似乎很有道理,但是主要存在這樣幾個問題:

    • 1)進程是需要占用內存空間的(從上一節能看到這一點),如果多個進程基于同一個可執行程序,那么這些進程其內存區域中的內容幾乎完全相同,這顯然會造成內存的浪費;
    • 2)計算機處理的任務可能是比較復雜的,這就涉及到了進程間通信,由于各個進程處于不同的內存地址空間,進程間通信天然需要借助操作系統,這就在增大編程難度的同時也增加了系統開銷。

    該怎么辦呢?

    6、從進程到線程

    讓我再來仔細的想一想這個問題,所謂進程無非就是內存中的一段區域,這段區域中保存了CPU執行的機器指令以及函數運行時的堆棧信息,要想讓進程運行,就把main函數的第一條機器指令地址寫入PC寄存器,這樣進程就運行起來了。

    進程的缺點在于只有一個入口函數,也就是main函數,因此進程中的機器指令只能被一個CPU執行,那么有沒有辦法讓多個CPU來執行同一個進程中的機器指令呢?

    聰明的你應該能想到,既然我們可以把main函數的第一條指令地址寫入PC寄存器,那么其它函數和main函數又有什么區別呢?

    答案是沒什么區別,main函數的特殊之處無非就在于是CPU執行的第一個函數,除此之外再無特別之處,我們可以把PC寄存器指向main函數,就可以把PC寄存器指向任何一個函數。

    當我們把PC寄存器指向非main函數時,線程就誕生了。

    至此我們解放了思想,一個進程內可以有多個入口函數,也就是說屬于同一個進程中的機器指令可以被多個CPU同時執行。

    注意:這是一個和進程不同的概念,創建進程時我們需要在內存中找到一塊合適的區域以裝入進程,然后把CPU的PC寄存器指向main函數,也就是說進程中只有一個執行流。

    但是現在不一樣了,多個CPU可以在同一個屋檐下(進程占用的內存區域)同時執行屬于該進程的多個入口函數,也就是說現在一個進程內可以有多個執行流了。

    總是叫執行流好像有點太容易理解了,再次祭出”弄不懂原則“,起個不容易懂的名字,就叫線程吧。

    這就是線程的由來。

    操作系統為每個進程維護了一堆信息,用來記錄進程所處的內存空間等,這堆信息記為數據集A。

    同樣的,操作系統也需要為線程維護一堆信息,用來記錄線程的入口函數或者棧信息等,這堆數據記為數據集B。

    顯然數據集B要比數據A的量要少,同時不像進程,創建一個線程時無需去內存中找一段內存空間,因為線程是運行在所處進程的地址空間的,這塊地址空間在程序啟動時已經創建完畢,同時線程是程序在運行期間創建的(進程啟動后),因此當線程開始運行的時候這塊地址空間就已經存在了,線程可以直接使用。這就是為什么各種教材上提的創建線程要比創建進程快的原因(當然還有其它原因)。

    值得注意的是,有了線程這個概念后,我們只需要進程開啟后創建多個線程就可以讓所有CPU都忙起來,這就是所謂高性能、高并發的根本所在。

    很簡單,只需要創建出數量合適的線程就可以了。

    另外值得注意的一點是:由于各個線程共享進程的內存地址空間,因此線程之間的通信無需借助操作系統,這給程序員帶來極大方便的同時也帶來了無盡的麻煩,多線程遇到的多數問題都出自于線程間通信簡直太方便了以至于非常容易出錯。出錯的根源在于CPU執行指令時根本沒有線程的概念,多線程編程面臨的互斥與同步問題需要程序員自己解決,關于互斥與同步問題限于篇幅就不詳細展開了,大部分的操作系統資料都有詳細講解。

    最后需要提醒的是:雖然前面關于線程講解使用的圖中用了多個CPU,但不是說一定要有多核才能使用多線程,在單核的情況下一樣可以創建出多個線程,原因在于線程是操作系統層面的實現,和有多少個核心是沒有關系的,CPU在執行機器指令時也意識不到執行的機器指令屬于哪個線程。即使在只有一個CPU的情況下,操作系統也可以通過線程調度讓各個線程“同時”向前推進,方法就是將CPU的時間片在各個線程之間來回分配,這樣多個線程看起來就是“同時”運行了,但實際上任意時刻還是只有一個線程在運行。

    7、線程與內存

    在前面的討論中我們知道了線程和CPU的關系,也就是把CPU的PC寄存器指向線程的入口函數,這樣線程就可以運行起來了,這就是為什么我們創建線程時必須指定一個入口函數的原因。

    無論使用任何編程語言,創建一個線程大體相同:

    // 設置線程入口函數DoSomething

    thread = CreateThread(DoSomething);

    // 讓線程運行起來

    thread.Run();

    那么線程和內存又有什么關聯呢?

    我們知道函數在被執行的時產生的數據包括:函數參數、局部變量、返回地址等信息。這些信息是保存在棧中的,線程這個概念還沒有出現時進程中只有一個執行流,因此只有一個棧,這個棧的棧底就是進程的入口函數,也就是main函數。

    假設main函數調用了funA,funcA又調用了funcB,如圖所示:

    那么有了線程以后了呢?

    有了線程以后一個進程中就存在多個執行入口,即同時存在多個執行流,那么只有一個執行流的進程需要一個棧來保存運行時信息,那么很顯然有多個執行流時就需要有多個棧來保存各個執行流的信息,也就是說操作系統要為每個線程在進程的地址空間中分配一個棧,即每個線程都有獨屬于自己的棧,能意識到這一點是極其關鍵的。

    同時我們也可以看到,創建線程是要消耗進程內存空間的,這一點也值得注意。

    8、線程的使用

    現在有了線程的概念,那么接下來作為程序員我們該如何使用線程呢?

    從生命周期的角度講,線程要處理的任務有兩類:長任務和短任務。

    1)長任務(long-lived tasks):

    顧名思義,就是任務存活的時間很長,比如以我們常用的word為例,我們在word中編輯的文字需要保存在磁盤上,往磁盤上寫數據就是一個任務,那么這時一個比較好的方法就是專門創建一個寫磁盤的線程,該寫線程的生命周期和word進程是一樣的,只要打開word就要創建出該寫線程,當用戶關閉word時該線程才會被銷毀,這就是長任務。

    這種場景非常適合創建專用的線程來處理某些特定任務,這種情況比較簡單。

    有長任務,相應的就有短任務。

    2)短任務(short-lived tasks):

    這個概念也很簡單,那就是任務的處理時間很短,比如一次網絡請求、一次數據庫查詢等,這種任務可以在短時間內快速處理完成。因此短任務多見于各種Server,像web server、database server、file server、mail server等,這也是互聯網行業的同學最常見的場景,這種場景是我們要重點討論的。

    這種場景有兩個特點:一個是任務處理所需時間短;另一個是任務數量巨大。

    如果讓你來處理這種類型的任務該怎么辦呢?

    你可能會想,這很簡單啊,當server接收到一個請求后就創建一個線程來處理任務,處理完成后銷毀該線程即可,So easy。

    這種方法通常被稱為thread-per-request,也就是說來一個請求就創建一個線程:

    如果是長任務,那么這種方法可以工作的很好,但是對于大量的短任務這種方法雖然實現簡單但是有缺點。

    具體是以下這樣的缺點:

    • 1)從前幾節我們能看到,線程是操作系統中的概念(這里不討論用戶態線程實現、協程之類),因此創建線程天然需要借助操作系統來完成,操作系統創建和銷毀線程是需要消耗時間的;
    • 2)每個線程需要有自己獨立的棧,因此當創建大量線程時會消耗過多的內存等系統資源。

    這就好比你是一個工廠老板(想想都很開心有沒有),手里有很多訂單,每來一批訂單就要招一批工人,生產的產品非常簡單,工人們很快就能處理完,處理完這批訂單后就把這些千辛萬苦招過來的工人辭退掉,當有新的訂單時你再千辛萬苦的招一遍工人,干活兒5分鐘招人10小時,如果你不是勵志要讓企業倒閉的話大概是不會這么做到的。

    因此一個更好的策略就是招一批人后就地養著,有訂單時處理訂單,沒有訂單時大家可以閑呆著。

    這就是線程池的由來。

    9、從多線程到線程池

    線程池的概念是非常簡單的,無非就是創建一批線程,之后就不再釋放了,有任務就提交給這些線程處理,因此無需頻繁的創建、銷毀線程,同時由于線程池中的線程個數通常是固定的,也不會消耗過多的內存,因此這里的思想就是復用、可控。

    10、線程池是如何工作的

    可能有的同學會問,該怎么給線程池提交任務呢?這些任務又是怎么給到線程池中線程呢?

    很顯然,數據結構中的隊列天然適合這種場景,提交任務的就是生產者,消費任務的線程就是消費者,實際上這就是經典的生產者-消費者問題。

    現在你應該知道為什么操作系統課程要講、面試要問這個問題了吧,因為如果你對生產者-消費者問題不理解的話,本質上你是無法正確的寫出線程池的。

    限于篇幅在這里不打算詳細的講解生產者消費者問題,參考操作系統相關資料就能獲取答案。這里我打算講一講一般提交給線程池的任務是什么樣子的。

    一般來說提交給線程池的任務包含兩部分:

    • 1) 需要被處理的數據;
    • 2) 處理數據的函數。

    偽碼描述一下:

    struct task {

        void* data;     // 任務所攜帶的數據

        handler handle; // 處理數據的方法

    }

    (注意:你也可以把代碼中的struct理解成class,也就是對象)

    線程池中的線程會阻塞在隊列上,當生產者向隊列中寫入數據后,線程池中的某個線程會被喚醒,該線程從隊列中取出上述結構體(或者對象),以結構體(或者對象)中的數據為參數并調用處理函數。

    偽碼如下:

    while(true) {

      struct task = GetFromQueue(); // 從隊列中取出數據

      task->handle(task->data);     // 處理數據

    }

    以上就是線程池最核心的部分。

    理解這些你就能明白線程池是如何工作的了。

    11、線程池中線程的數量

    現在線程池有了,那么線程池中線程的數量該是多少呢?

    在接著往下看前先自己想一想這個問題。如果你能看到這里說明還沒有睡著。

    要知道線程池的線程過少就不能充分利用CPU,線程創建的過多反而會造成系統性能下降,內存占用過多,線程切換造成的消耗等等。因此線程的數量既不能太多也不能太少,那到底該是多少呢?

    回答這個問題,你需要知道線程池處理的任務有哪幾類,有的同學可能會說你不是說有兩類嗎?長任務和短任務,這個是從生命周期的角度來看的,那么從處理任務所需要的資源角度看也有兩種類型,這就是沒事兒找抽型。。。啊不,是CPU密集型和I/O密集型。

    1)CPU密集型:

    所謂CPU密集型就是說處理任務不需要依賴外部I/O,比如科學計算、矩陣運算等等。在這種情況下只要線程的數量和核數基本相同就可以充分利用CPU資源。

    2)I/O密集型:

    這一類任務可能計算部分所占用時間不多,大部分時間都用在了比如磁盤I/O、網絡I/O等。

    這種情況下就稍微復雜一些了,你需要利用性能測試工具評估出用在I/O等待上的時間,這里記為WT(wait time),以及CPU計算所需要的時間,這里記為CT(computing time),那么對于一個N核的系統,合適的線程數大概是 N * (1 + WT/CT) ,假設I/O等待時間和計算時間相同,那么你大概需要2N個線程才能充分利用CPU資源,注意這只是一個理論值,具體設置多少需要根據真實的業務場景進行測試。

    當然充分利用CPU不是唯一需要考慮的點,隨著線程數量的增多,內存占用、系統調度、打開的文件數量、打開的socker數量以及打開的數據庫鏈接等等是都需要考慮的。

    因此這里沒有萬能公式,要具體情況具體分析。

    12、線程池不是萬能的

    線程池僅僅是多線程的一種使用形式,因此多線程面臨的問題線程池同樣不能避免,像死鎖問題、race condition問題等等,關于這一部分同樣可以參考操作系統相關資料就能得到答案,所以基礎很重要呀老鐵們。

    13、線程池使用的最佳實踐

    線程池是程序員手中強大的武器,互聯網公司的各個server上幾乎都能見到線程池的身影。

    但使用線程池前你需要考慮:

    • 1)充分理解你的任務,是長任務還是短任務、是CPU密集型還是I/O密集型,如果兩種都有,那么一種可能更好的辦法是把這兩類任務放到不同的線程池中,這樣也許可以更好的確定線程數量;
    • 2)如果線程池中的任務有I/O操作,那么務必對此任務設置超時,否則處理該任務的線程可能會一直阻塞下去;
    • 3)線程池中的任務最好不要同步等待其它任務的結果。

    14、本文小結

    本文我們從CPU開始一路來到常用的線程池,從底層到上層、從硬件到軟件。

    注意:這里通篇沒有出現任何特定的編程語言,線程不是語言層面的概念(依然不考慮用戶態線程),但是當你真正理解了線程后,相信你可以在任何一門語言下用好多線程,你需要理解的是道,此后才是術。

    希望這篇文章對大家理解線程以及線程池有所幫助。

    接下的一篇將是與線程池密切配合實現高性能、高并發的又一關鍵技術《從根上理解高性能、高并發(二):深入操作系統,理解I/O與零拷貝技術》,敬請期待。

    附錄:更多高性能、高并發方面的文章

    [1] 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紅包的功能邏輯、容災、運維、架構等

    社交軟件紅包技術解密(十):手Q客戶端針對2020年春節紅包的技術實踐

    社交軟件紅包技術解密(十一):解密微信紅包隨機算法(含代碼實現)

    即時通訊新手入門:一文讀懂什么是Nginx?它能否實現IM的負載均衡?

    即時通訊新手入門:快速理解RPC技術——基本概念、原理和用途

    多維度對比5款主流分布式MQ消息隊列,媽媽再也不擔心我的技術選型了

    從游擊隊到正規軍(一):馬蜂窩旅游網的IM系統架構演進之路

    從游擊隊到正規軍(二):馬蜂窩旅游網的IM客戶端架構演進和實踐總結

    從游擊隊到正規軍(三):基于Go的馬蜂窩旅游網分布式IM系統技術實踐

    IM開發基礎知識補課(六):數據庫用NoSQL還是SQL?讀這篇就夠了!

    瓜子IM智能客服系統的數據架構設計(整理自現場演講,有配套PPT)

    阿里釘釘技術分享:企業級IM王者——釘釘在后端架構上的過人之處

    微信后臺基于時間序的新一代海量數據存儲架構的設計實踐

    IM開發基礎知識補課(九):想開發IM集群?先搞懂什么是RPC!

    阿里技術分享:電商IM消息平臺,在群聊、直播場景下的技術實踐

    >> 更多同類文章 ……

    [2] 其它高性能、高并發設計相關文章:

    騰訊資深架構師干貨總結:一文讀懂大型分布式系統設計的方方面面

    快速理解高性能HTTP服務端的負載均衡技術原理

    子彈短信光鮮的背后:網易云信首席架構師分享億級IM平臺的技術實踐

    知乎技術分享:從單機到2000萬QPS并發的Redis高性能緩存實踐之路

    新手入門:零基礎理解大型分布式架構的演進歷史、技術原理、最佳實踐

    阿里技術分享:深度揭秘阿里數據庫技術方案的10年變遷史

    阿里技術分享:阿里自研金融級數據庫OceanBase的艱辛成長之路

    達達O2O后臺架構演進實踐:從0到4000高并發請求背后的努力

    優秀后端架構師必會知識:史上最全MySQL大表優化方案總結

    小米技術分享:解密小米搶購系統千萬高并發架構的演進和實踐

    一篇讀懂分布式架構下的負載均衡技術:分類、原理、算法、常見方案等

    通俗易懂:如何設計能支撐百萬并發的數據庫架構?

    多維度對比5款主流分布式MQ消息隊列,媽媽再也不擔心我的技術選型了

    從新手到架構師,一篇就夠:從100到1000萬高并發的架構演進之路

    美團技術分享:深度解密美團的分布式ID生成算法

    12306搶票帶來的啟示:看我如何用Go實現百萬QPS的秒殺系統(含源碼)

    >> 更多同類文章 ……

    本文已同步發布于:http://www.52im.net/thread-3272-1-1.html



    作者:Jack Jiang (點擊作者姓名進入Github)
    出處:http://www.52im.net/space-uid-1.html
    交流:歡迎加入即時通訊開發交流群 215891622
    討論:http://www.52im.net/
    Jack Jiang同時是【原創Java Swing外觀工程BeautyEye】【輕量級移動端即時通訊框架MobileIMSDK】的作者,可前往下載交流。
    本博文 歡迎轉載,轉載請注明出處(也可前往 我的52im.net 找到我)。


    只有注冊用戶登錄后才能發表評論。


    網站導航:
     
    Jack Jiang的 Mail: jb2011@163.com, 聯系QQ: 413980957, 微信: hellojackjiang
    主站蜘蛛池模板: 免费无码又爽又高潮视频| 免费做爰猛烈吃奶摸视频在线观看| 久久久久国色av免费看| 18女人腿打开无遮掩免费| 女人18毛片特级一级免费视频| 四虎亚洲国产成人久久精品 | 午夜理伦剧场免费| 18禁成年无码免费网站无遮挡| 国产做床爱无遮挡免费视频| 在线亚洲精品自拍| 亚洲无成人网77777| 国产亚洲日韩在线a不卡| 永久免费av无码入口国语片| 免费精品国偷自产在线在线| 免费国产成人午夜电影| 国产AV无码专区亚洲AV毛网站| 久久久久se色偷偷亚洲精品av| 成人国产网站v片免费观看 | 丰满少妇作爱视频免费观看| 污污网站18禁在线永久免费观看| 最近2019中文免费字幕| 亚洲综合另类小说色区| 亚洲一区二区影视| 国产免费牲交视频免费播放| 男女超爽刺激视频免费播放| 亚洲人成网站观看在线播放| 亚洲校园春色小说| 日本黄页网址在线看免费不卡| 又大又硬又爽又粗又快的视频免费| 免费一级毛片一级毛片aa| 亚洲日本va午夜中文字幕一区| 麻豆亚洲AV成人无码久久精品 | 国产亚洲成AV人片在线观黄桃| 亚洲AV成人噜噜无码网站| 中文字幕免费在线播放| 成**人免费一级毛片| 亚洲第一视频网站| 男人j进女人p免费视频| 一二三四影视在线看片免费 | 亚洲色欲久久久综合网东京热 | 亚洲另类小说图片|