Java面試核心知識點

from & refer: https://download.csdn.net/download/zhiyuan411/12085383

1. 目錄

文章目錄

2. JVM

2.1. 線程

Hotspot JVM 中的 Java 線程與原生操作系統線程有直接的映射關係。

2.2. JVM 內存區域

線程私有數據區域生命週期與線程相同, 依賴用戶線程的啓動/結束 而 創建/銷燬。
線程共享區域隨虛擬機的啓動/關閉而創建/銷燬。

2.2.1. 程序計數器(線程私有)
2.2.2. 虛擬機棧(線程私有)
2.2.3. 本地方法區(線程私有)
本地方法區和 Java Stack 作用類似, 區別是虛擬機棧爲執行 Java 方法服務, 而本地方法棧則爲 Native 方法服務。
HotSpot VM 直接就把本地方法棧和虛擬機棧合二爲一。
2.2.4. 堆(Heap-線程共享)-運行時數據區
2.2.5. 方法區/永久代(線程共享)

2.3. JVM 運行時內存

2.3.1. 新生代
是用來存放新生的對象。一般佔據堆的 1/3 空間。
由於頻繁創建對象,所以新生代會頻繁觸發MinorGC 進行垃圾回收。
新生代又分爲 Eden 區、ServivorFrom、ServivorTo 三個區。

2.3.1.1. Eden 區
Java 新對象的出生地(如果新創建的對象佔用內存很大,則直接分配到老 年代)。
當 Eden 區內存不夠的時候就會觸發 MinorGC,對新生代區進行 一次垃圾回收。

2.3.1.2. ServivorFrom
上一次 GC 的倖存者,作爲這一次 GC 的被掃描者。

2.3.1.3. ServivorTo
保留了一次 MinorGC 過程中的倖存者。

2.3.1.4. MinorGC 的過程(複製->清空->互換)
MinorGC 採用複製算法。

2.3.2 老年代
主要存放應用程序中生命週期長的內存對象。
老年代的對象比較穩定,所以 MajorGC 不會頻繁執行。
MajorGC 採用標記清除算法。MajorGC 的耗時比較長,還會產生內存碎片。
當老年代也滿了裝不下的 時候,就會拋出 OOM(Out of Memory)異常。

2.3.3 永久代
指內存的永久保存區域,主要存放 Class 和 Meta(元數據)的信息,Class 在被加載的時候被 放入永久區域,它和和存放實例的區域不同,GC 不會在主程序運行期對永久區域進行清理。
所以這也導致了永久代的區域會隨着加載的 Class 的增多而脹滿,最終拋出 OOM 異常。

在 Java8 中,永久代已經被移除,被一個稱爲“元數據區”(元空間)的區域所取代。
元空間與永久代之間最大的區別在於:元空間並不在虛擬機中,而是使用 本地內存。

2.4. 垃圾回收與算法

2.4.1. 如何確定垃圾
引用計數法:一個對象如果沒有任何與之關聯的引用,即他們的引用計數爲 0,則說明對象不太可能再被用到,那麼這個對象就是可回收對象。
可達性分析:用於解決引用計數法的循環引用問題。如果在“GC roots”和一個對象之間沒有可達路徑,則稱該對象是不可達的。兩次標記後仍然是不可達對象,則將面臨回收。

2.4.2. 標記清除算法(Mark-Sweep)
標記階段標記出所有需要回收的對象,清 除階段回收被標記的對象所佔用的空間。該算法最大的問題就是內存碎片化嚴重。

2.4.3. 複製算法(copying)
按內存容量將內存劃分爲等大小 的兩塊。每次只使用其中一塊,當這一塊內存滿後將尚存活的對象複製到另一塊上去,把已使用 的內存清掉。
該算法最大的問題是可用內存被壓縮到了原 本的一半。且存活對象增多的話,Copying 算法的效率會大大降低。

2.4.4. 標記整理算法(Mark-Compact)
標記階段和 Mark-Sweep 算法相同,標記後不是清 理對象,而是將存活對象移向內存的一端。然後清除端邊界外的對象。

2.4.5. 分代收集算法
分代收集法是目前大部分 JVM 所採用的方法,其核心思想是根據對象存活的不同生命週期將內存 劃分爲不同的域,不同區域選擇不同的算法。
一般情況下將 GC 堆劃分爲老生代(Tenured/Old Generation)和新生代(Young Generation)。
新生代都採取 Copying 算法。老年代採用 Mark-Compact 算法。

2.5. JAVA 四中引用類型

2.5.1. 強引用
最常見的就是強引用,把一個對象賦給一個引用變量,這個引用變量就是一個強引 用。

2.5.2. 軟引用
軟引用需要用 SoftReference 類來實現。

2.5.3. 弱引用
弱引用需要用 WeakReference 類來實現。

2.5.4. 虛引用
虛引用需要 PhantomReference 類來實現。

2.6. GC 分代收集算法 VS 分區收集算法

當前主流 VM 垃圾收集都採用”分代收集”(Generational Collection)算法。
分區算法則將整個堆空間劃分爲連續的不同小區間, 每個小區間獨立使用, 獨立回收. 這樣做的好處是可以控制一次回收多少個小區間, 根據目標停頓時間, 每次合理地回收若干個小區間(而不是整個堆), 從而減少一次 GC 所產生的停頓。

2.7. GC 垃圾收集器

java 虛擬中針對新生代和年老代分別提供了多種不同的垃圾收集器。

2.7.1. Serial 垃圾收集器(單線程、複製算法)
2.7.2. ParNew垃圾收集器(Serial+多線程)
2.7.3. Parallel Scavenge 收集器(多線程複製算法、高效、自適應調節策略)
2.7.4. SerialOld收集器(單線程標記整理算法)
2.7.5. ParallelOld收集器(多線程標記整理算法)
2.7.6. CMS收集器(多線程標記清除算法、最短垃圾回收停頓時間)
2.7.7. G1收集器(標記整理算法、低停頓垃圾回收)

2.8. JAVA IO/NIO

2.8.1. 阻塞 IO 模型
讀寫數據過程中用戶線程會發生阻塞現象。

2.8.2. 非阻塞 IO 模型
用戶線程需要不斷地詢問內核數據是否就緒,也就說非阻塞 IO 不會交出 CPU,而會一直佔用 CPU。

2.8.3. 多路複用 IO 模型
多路複用 IO 模型是目前使用得比較多的模型。Java NIO 實際上就是多路複用 IO。
有一個線程(在內核進行)不斷去輪詢多個 socket 的狀態,只有當 socket 真正有讀寫事件時,才真正調用實際的 IO 讀寫操作。

2.8.4. 信號驅動 IO 模型
當用戶線程發起一個 IO 請求操作,會給對應的 socket 註冊一個信號函 數,然後用戶線程會繼續執行,當內核數據就緒時會發送一個信號給用戶線程,用戶線程接收到 信號之後,便在信號函數中調用 IO 讀寫操作來進行實際的 IO 請求操作。

2.8.5. 異步 IO 模型
異步 IO 模型纔是最理想的 IO 模型。異步 IO 是需要操作系統的底層支持,在 Java 7 中,提供了 Asynchronous IO。
用戶線程只需要先發起一個請求,當接收內核返回的成功信號時表示 IO 操作已經完成,可以直接去使用數據了。

2.8.6. JAVA IO包
字節流使用InputStream/OutputStream及其子類來處理;字符流使用Reader/Writer及其子類來處理。

2.8.7. JAVA NIO包
NIO 主要有三大核心部分:Channel(通道),Buffer(緩衝區), Selector。
NIO 基於 Channel 和 Buffer(緩衝區)進行操作,數據總是從通道讀取到緩衝區 中,或者從緩衝區寫入到通道中。Selector(選擇區)用於監聽多個通道的事件(比如:連接打開, 數據到達)。

NIO 和傳統 IO 之間第一個最大的區別是,IO 是面向流的,NIO 是面向緩衝區的。

2.9. JVM 類加載機制

JVM 類加載機制分爲五個部分:加載,驗證,準備,解析,初始化。

  • 加載階段會在內存中生成一個代表這個類的 java.lang.Class 對 象,作爲方法區這個類的各種數據的入口。可以從class文件,war包等處進行加載。在加載階段可以自定義類加載 器以外
  • 驗證階段的主要目的是爲了確保 Class 文件的字節流中包含的信息是否符合當前虛擬機的要求,並且不會危害虛擬機自身的安全。
  • 準備階段是正式爲類變量分配內存並設置類變量的初始值階段,即在方法區中分配這些變量所使用的內存空間。
  • 解析階段是指虛擬機將常量池中的符號引用替換爲直接引用的過程。符號引 用的字面量形式明確定義在 Java 虛擬機規範的 Class 文件格式中,不一定在內存中存在。直接引用可以是指向目標的指針,相對偏移量或是一個能間接定位到目標的句柄,一定在內存中存在。
  • 到了初始階段,纔開始真正執行類中定義的 Java 程序代碼。執行類構造器方法的過程。方法是由編譯器自動收集類中的類變量的賦值操作和靜態語句塊中的語句合併而成的。

虛擬機設計團隊把加載動作放到 JVM 外部實現,以便讓應用程序決定如何獲取所需的類,JVM 提 供了 3 種類加載器,從上到下依次爲: 啓動類加載器(Bootstrap ClassLoader,JAVA_HOME\lib 目錄),擴展類加載器(Extension ClassLoader,JAVA_HOME\lib\ext 目錄),應用程序類加載器(Application ClassLoader,用戶路徑(classpath)上的類庫)。
我們也可以通過繼承 java.lang.ClassLoader實現自定義的類加載器。

JVM 通過雙親委派模型進行類的加載:當一個類收到了類加載請求,他首先不會嘗試自己去加載這個類,而是把這個請求委派給父類去完成,每一個層次類加載器都是如此。只有當父類加載器反饋自己無法完成這個請求的時候(在它的加載路徑下沒有找到所需加載的Class),子類加載器纔會嘗試自己去加載。

OSGI(Open Service Gateway Initiative),是面向 Java 的動態模型系統,可以實現模塊級 的熱插拔功能,同時也引入了額外的複雜度,因爲它不遵守了類加載的雙親委託模型。

3. JAVA集合

3.1. 接口繼承關係和實現

集合類存放於 Java.util 包中,主要有 3 種:

  • Collection:Collection 是集合 List、Set、Queue 的最基本的接口。
  • Iterator:迭代器,可以通過迭代器遍歷集合中的數據
  • Map:是映射表的基礎接口

3.2. LIST

3.2.1. ArrayList(數組)
3.2.2. Vector(數組實現、線程同步)
3.2.3. LinkList(鏈表)

3.3. SET

3.3.1.1. HashSet(Hash 表)
3.3.1.2. TreeSet(二叉樹)
3.3.1.3. LinkHashSet(HashSet+LinkedHashMap)

3.4. MAP

3.4.1. HashMap(數組+鏈表+紅黑樹)
Java8 對 HashMap 進行了一些修改,最大的不同就是利用了紅黑樹,所以其由 數組+鏈表+紅黑樹 組成。

3.4.2. ConcurrentHashMap
支持併發操作。
ConcurrentHashMap 是一個 Segment 數組,Segment 通過繼承 ReentrantLock 來進行加鎖。每個 Segment 很像之前介紹的 HashMap,不過它要保證線程安全,所以實現會更復雜。
Java8 對 ConcurrentHashMap 進行了比較大的改動, Java8 也引入了紅黑樹。

3.4.3. HashTable(線程安全)
遺留類,任一時間只有一個線程能寫 Hashtable,併發性不如 ConcurrentHashMap。

3.4.4. TreeMap(可排序)
用 Iterator 遍歷 TreeMap 時,得到的記錄是排過序的。

3.4.5. LinkHashMap(記錄插入順序)
用 Iterator 遍歷LinkedHashMap 時,得到的記錄是按照插入順序排序的。

4. JAVA 多線程併發

4.1.1. JAVA 併發知識庫

同步調用,異步回調和Future模式

  • 同步調用,會順序執行多個多線程任務;
  • 異步回調,在執行時是並行執行多個線程任務,多個線程任務的執行完成後也會異步調用回調函數來處理結果;
  • Future模式,啓動時開始並行執行多個線程任務,在最後調用Future的get()獲取結果時是同步阻塞方式等待返回結果。

4.1.2. JAVA 線程實現/創建方式

4.1.2.1. 繼承 Thread 類
Thread 類本質上是實現了 Runnable 接口的一個實例,代表一個線程的實例。
啓動線程的方法就是通過 Thread 類的 start()實例方法,它將啓動一個新線 程,並執行 run()方法。

4.1.2.2. 實現 Runnable 接口
啓動 自定義的MyThread類,需要首先實例化一個 Thread,並傳入自己的 MyThread 實例。

4.1.2.3. ExecutorService、Callable<Class>、Future 有返回值線程
有返回值的任務必須實現 Callable 接口,類似的,無返回值的任務必須 Runnable 接口。
執行 Callable 任務後,可以獲取一個 Future 的對象,在該對象上調用 get 就可以獲取到 Callable 任務 返回的 Object 了。

4.1.2.4. 基於線程池的方式
線程和數據庫連接這些資源都是非常寶貴的資源。那麼每次需要的時候創建,不需要的時候銷 毀,是非常浪費資源的。那麼我們就可以使用緩存的策略,也就是使用線程池。

4.1.3. 4 種線程池

4.1.3.1. newCachedThreadPool
調用 execute 將重用以前構造的線程(如果線程可用)。如果現有線程沒有可用的,則創建一個新線程並添加到池中。
終止並從緩存中移除那些已有 60 秒鐘未被使用的線程。

4.1.3.2. newFixedThreadPool
創建一個可重用固定線程數的線程池,以共享的無界隊列方式來運行這些線程。

4.1.3.3. newScheduledThreadPool
創建一個線程池,它可安排在給定延遲後運行命令或者定期地執行。

4.1.3.4. newSingleThreadExecutor
這個線程池只有一個線程, 並可以在線程死後(或發生異常時)重新啓動一個線程來替代原來的線程繼續執行下去。

4.1.4. 線程生命週期(狀態)

在線程的生命週期中,它要經過新建(New)、就緒(Runnable)、運行(Running)、阻塞 (Blocked)和死亡(Dead)5 種狀態。
啓動後,線程狀態也會多次在運行、阻塞之間切換。

  • 當程序使用 new 關鍵字創建了一個線程之後,該線程就處於新建狀態,此時僅由 JVM 爲其分配 內存,並初始化其成員變量的值
  • 當線程對象調用了 start()方法之後,該線程處於就緒狀態。Java 虛擬機會爲其創建方法調用棧和 程序計數器,等待調度運行。
  • 如果處於就緒狀態的線程獲得了 CPU,開始執行 run()方法的線程執行體,則該線程處於運行狀 態。
  • 阻塞狀態是指線程因爲 等待阻塞(o.wait->等待對列), 同步阻塞(lock->鎖池), 或者 其他阻塞(sleep/join),暫時停止運行。
  • 線程正常/異常結束或調用stop()後就是死亡狀態。

4.1.5. 終止線程 4 種方式

4.1.5.1. 正常運行結束
4.1.5.2. 使用退出標誌退出線程(volatile定義標誌變量)
4.1.5.3. Interrupt() 方法結束線程
4.1.5.4. stop() 方法終止線程(線程不安全)

4.1.6. sleep 與 wait 區別

sleep()方法導致了程序暫停執行指定的時間,該方法是屬於 Thread 類的,線程也不會釋放對象鎖。
調用 wait()方法的時候,線程會放棄對象鎖,進入等待此對象的等待鎖定池,只有針對此對象調用 notify()方法後本線程才進入對象鎖定池準備獲取對象鎖進入運行狀態。

4.1.7. start 與 run 區別

  • start()方法來啓動線程,真正實現了多線程運行。這時無需等待 run 方法體代碼執行完畢,
    可以直接繼續執行下面的代碼。
  • 通過調用 Thread 類的 start()方法來啓動一個線程, 這時此線程是處於就緒狀態。
  • run()稱爲線程體,它包含了要執行的這個線程的內容,線程就進入了運行狀態。

4.1.8. JAVA 後臺線程

守護線程–也稱“服務線程”,他是後臺線程,它有一個特性,即爲用戶線程 提供 公共服務,它的優先級比較低,在沒有用戶線程可服務時會自動離開(線程則是 JVM 級別的)。
通過 setDaemon(true)來設置一個用戶線程爲“守護線程”;在 Daemon 線程中產生的新線程也是 Daemon 的。

4.1.9. JAVA 鎖

4.1.9.1. 樂觀鎖
讀時不會上鎖;寫時會加鎖:先讀出當前版本號,然後比較跟上一次的版本號,如果一樣則更新,失敗則重複讀-比較-寫的操作。
java 中的樂觀鎖基本都是通過 CAS 操作實現的,CAS 是一種更新的原子操作,比較當前值跟傳入值是否一樣,一樣則更新,否則失敗。

4.1.9.2. 悲觀鎖
讀時會加鎖。
java 中的悲觀鎖就是 Synchronized。AQS 框架下的鎖則是先嚐試 cas 樂觀鎖去獲取鎖,獲取不到,
纔會轉換爲悲觀鎖,如 RetreenLock。

4.1.9.3. 自旋鎖
如果持有鎖的線程能在很短時間內釋放鎖資源,那麼那些等待競爭鎖的線程就不需要做內核態和用戶態之間的切換進入阻塞掛起狀態,它們只需要等一等(自旋),等持有鎖的線程釋放鎖後即可立即獲取鎖,這樣就避免用戶線程和內核的切換的消耗。 超過自旋等待的最大時間後進入阻塞狀態。
適用於鎖的競爭不激烈,且佔用鎖時間非常短的代碼塊。
在 JDK 1.6 引入了適應性自旋鎖,適應性自旋鎖意味着自旋的時間不在是固定的了,而是由前一次在同一個鎖上的自旋時間以及CPU負荷來決定。

4.1.9.4. Synchronized 同步鎖
synchronized 它可以把任意一個非 NULL 的對象當作鎖。他屬於獨佔式的悲觀鎖,同時屬於可重入鎖,非公平鎖。
Java1.6,1.7 與 1.8 中,均對該關鍵字的實現機理做了優化。例如適應自旋、偏向鎖和輕量級鎖等,效率有了本質上的提高。

4.1.9.5. ReentrantLock
ReentrantLock 是一種獨佔式的可重入鎖,除了能完成 synchronized 所能完成的所有工作外,還提供了諸如可響應中斷鎖、可輪詢鎖請求、定時鎖等避免多線程死鎖的方法。默認爲非公平鎖,可以初始化時設置是否公平鎖。

4.1.9.6. Semaphore 信號量
Semaphore 是一種基於計數的信號量。它可以設定一個閾值,基於此,多個線程競爭獲取許可信
號,做完自己的申請後歸還,超過閾值後,線程申請許可信號將會被阻塞。
Semaphore 可以用來構建一些對象池,資源池之類的。計數爲 1 的 Semaphore,可以作爲一種類似互斥鎖的機制。
Semaphore 基本能完成 ReentrantLock 的所有工作,使用方法也與之類似。

4.1.9.7. AtomicInteger
AtomicInteger,是一個提供原子操作的 Integer 的類,常見的還有AtomicBoolean、AtomicInteger、AtomicLong、AtomicReference 等,他們的實現原理相同,區別在與運算對象類型的不同。令人興奮地,還可以通過 AtomicReference<V>將一個對象的所有操作轉化成原子操作。這樣使用更方便,效率也更高。

4.1.9.8. 可重入鎖(遞歸鎖)
可重入鎖,也叫做遞歸鎖,指的是同一線程 外層函數獲得鎖之後 ,內層遞歸函數仍然有獲取該鎖的代碼,但不受影響。

4.1.9.9. 公平鎖與非公平鎖
公平鎖:加鎖前檢查是否有排隊等待的線程,優先排隊等待的線程,先來先得。
非公平鎖:加鎖時不考慮排隊等待問題,直接嘗試獲取鎖,獲取不到自動到隊尾等待。

4.1.9.10. ReadWriteLock 讀寫鎖
讀寫鎖分爲讀鎖和寫鎖,多個讀鎖不互斥,讀鎖與寫鎖互斥。

4.1.9.11. 共享鎖和獨佔鎖
獨佔鎖模式下,每次只能有一個線程能持有鎖。
共享鎖則允許多個線程同時獲取鎖,併發訪問 共享資源。

4.1.9.12. 重量級鎖(Mutex Lock)
依賴於操作系統 Mutex Lock 所實現的鎖我們稱之爲“重量級鎖”。因爲操作系統實現線程之間的切換這就需要從用戶態轉換到核心態,這個成本非常高,時間非常長。
Synchronized 是通過對象內部的一個叫做監視器鎖(monitor)來實現的。但是監視器鎖本質又是依賴於底層的操作系統的 Mutex Lock 來實現的。JDK 中對 Synchronized 做的種種優化,其核心都是爲了減少這種重量級鎖的使用。

4.1.9.13. 輕量級鎖
鎖的狀態總共有四種:無鎖狀態、偏向鎖、輕量級鎖和重量級鎖。
隨着鎖的競爭,鎖可以從偏向鎖升級到輕量級鎖,再升級的重量級鎖(但是鎖的升級是單向的,也就是說只能從低到高升級,不會出現鎖的降級)。

輕量級鎖不使用操作系統互斥量來實現,而是依賴多次 CAS 原子指令。
輕量級鎖並不是用來代替重量級鎖的,它的本意是在線程交替執行同步塊的前提下,減少傳統的重量級鎖使用產生的性能消耗。

4.1.9.14. 偏向鎖
偏向鎖的目的是在某個線程獲得鎖之後,消除這個線程鎖重入(CAS)的開銷,看起來讓這個線程得到了偏護。
引入偏向鎖是爲了在只有一個線程執行同步塊的情況下儘量減少不必要的輕量級鎖執行路徑。

4.1.9.15. 分段鎖
是一種思想,ConcurrentHashMap 是學習分段鎖的最好實踐。

4.1.9.16. 鎖優化

  • 減少鎖持有時間:只用在有線程安全要求的程序上加鎖。
  • 減小鎖粒度: 將被很多線程訪問的大對象拆成小對象,大大增加並行度,降低鎖競爭。
  • 鎖分離:例如讀寫鎖。
  • 鎖粗化:如果對同一個鎖不停的進行請求、同步和釋放,其本身也會消耗系統寶貴的資源,反而不利於性能的優化 。
  • 鎖消除:在即時編譯器時,如果發現不可能被共享的對象,則可以消除這些對象的鎖操作。

4.1.10. 線程基本方法

4.1.10.1. 線程等待(wait)
調用該方法的線程進入 WAITING 狀態,並釋放對象的鎖。
只有等待另外線程的通知或被中斷纔會返回。

4.1.10.2. 線程睡眠(sleep)
線程進入 TIMED-WATING 狀態,並不釋放當前佔有的鎖。

4.1.10.3. 線程讓步(yield)
當前線程讓出 CPU 執行時間片,與其他線程一起重新競爭 CPU 時間片。

4.1.10.4. 線程中斷(interrupt)
中斷一個線程,其本意是給這個線程一個通知信號,會影響這個線程內部的一箇中斷標識位。對線程的行爲影響會因爲線程對該標識位處理方法的實現而不同。

4.1.10.5. Join 等待其他線程終止
等待其他線程終止,在當前線程中調用一個線程的 join() 方法,則當前線程轉爲阻塞狀態,等到指定線程結束,當前線程再由阻塞狀態變爲就緒狀態。
很多情況下,主線程生成並啓動了子線程,需要用到子線程返回的結果,也就是需要主線程需要在子線程結束後再結束,這時候就要用到 join() 方法。

4.1.10.7. 線程喚醒(notify)
喚醒在此對象監視器上等待的單個線程,如果所有線程都在此對象上等待,則會選擇喚醒其中任意一個線程,被喚醒的線程將以常規方式與在該對象上主動同步的其他所有線程進行競爭。
類似的方法還有 notifyAll() ,喚醒再次監視器上等待的所有線程。

4.1.11. 線程上下文切換

巧妙地利用了時間片輪轉的方式, CPU 給每個任務都服務一定的時間,然後把當前任務的狀態保存下來,在加載下一任務的狀態後,繼續服務下一任務,任務的狀態保存及再加載, 這段過程就叫做上下文切換。
上下文切換可以認爲是內核(操作系統的核心)在 CPU 上對於進程(包括線程)進行切換,上下文切換過程中的信息是保存在進程控制塊(PCB, process control block)中的。PCB 還經常被稱作“切換楨”(switchframe)。
除了當前執行任務的時間片用完,IO阻塞,用戶代碼掛起當前任務,硬件中斷等都會引起上下文切換。

4.1.12. 同步鎖與死鎖

4.1.13. 線程池原理

線程池做的工作主要是:線程複用;控制最大併發數;管理線程。

線程池的實現原理:繼承重寫Thread 類,在其 start 方法中添加不斷循環調用傳遞過來的 Runnable 對象。 循環方法中不斷獲取 Runnable 是用 Queue 實現的,在獲取下一個 Runnable 之前可以是阻塞的。

4.1.14. JAVA 阻塞隊列原理

在阻塞隊列中:
當隊列中沒有數據的情況下,消費者端的所有線程都會被自動阻塞(掛起),直到有數據放 入隊列。
當隊列中填滿數據的情況下,生產者端的所有線程都會被自動阻塞(掛起),直到隊列中有 空的位置,線程被自動喚醒。

Java 中的阻塞隊列:(無界隊列是指隊列沒有固定大小,不存在隊列滿負荷情況)

  • ArrayBlockingQueue :由數組結構組成的有界阻塞隊列。(公平、非公平)
  • LinkedBlockingQueue :由鏈表結構組成的有界阻塞隊列。(兩個獨立鎖提高併發、默認一個類似無限大小的容量Integer.MAX_VALUE)
  • PriorityBlockingQueue :支持優先級排序的無界阻塞隊列。(compareTo 排序實現優先)
  • DelayQueue:使用優先級隊列實現的無界阻塞隊列。(緩存失效、定時任務 )
  • SynchronousQueue:不存儲元素的阻塞隊列。(不存儲數據、可用於傳遞數據)
  • LinkedTransferQueue:由鏈表結構組成的無界阻塞隊列。
  • LinkedBlockingDeque:由鏈表結構組成的雙向阻塞隊列。(兩端插入和移出)

4.1.15. CyclicBarrier、CountDownLatch、Semaphore 的用法

4.1.15.1. CountDownLatch(線程計數器 )
4.1.15.2. CyclicBarrier(迴環柵欄-調用await()等待至某個狀態再全部同時執行,這個狀態稱爲 barrier 狀態)
4.1.15.3. Semaphore(信號量-控制同時訪問的線程個數)

CountDownLatch 和 CyclicBarrier 都能夠實現線程之間的等待。Semaphore更類似於鎖,用於控制對某組資源的訪問權限。

4.1.16. volatile 關鍵字的作用(變量可見性、禁止重排序)

volatile 變量具備兩種特性:

  • volatile 變量不會被緩存在寄存器或者對多CPU的其他處理器不可見的 地方,因此在讀取 volatile 類型的變量時總會返回最新寫入的值。
  • volatile 禁止了指令重排。

volatile 變量是一 種比 sychronized 關鍵字更輕量級的同步機制。
volatile 適合這種場景:一個變量被多個線程共 享,線程直接給這個變量賦值;且寫操作不能依賴於當前值(如i++),不依賴於其他volatile變量。

4.1.17. 如何在兩個線程之間共享數據

Java 裏面進行多線程通信的主要方式就是共享內存的方式。
共享內存要保證:可見性和有序性原子性。Java 內存模型(JMM)解決了可見性和有序性的問題,而鎖解決了原子性的問題。

常規實現方法:

  • 將數據抽象成一個類,並將對這個數據的操作作爲這個類的方法,這麼設計可以和容易做到同步,只要在方法上加 synchronized。
  • 將 Runnable 對象作爲一個類的內部類,共享數據作爲這個類的成員變量,每個線程對共享數據的操作方法也封裝在外部類,以便實現對數據的各個操作的同步和互斥,作爲內部類的各個 Runnable 對象調用外部類的這些方法。

4.1.18. ThreadLocal 作用(線程本地存儲)

ThreadLocal 的作用是提供線程內的局部變量,這種變量在線程的生命週期內起作用,減少同一個線程內多個函數或者組件之間一些公共變量的傳遞的複雜度。

4.1.19. synchronized 和 ReentrantLock 的區別

  • ReentrantLock 通過方法 lock()與 unlock()來進行加鎖與解鎖操作(必須在 finally 控制塊中手動進行解鎖操),synchronized 會被 JVM 自動加鎖與解鎖。
  • ReentrantLock 是 API 級別的(JDK中實現),synchronized 是 JVM 級別的。
  • ReentrantLock 相比 synchronized 的優勢是可中斷、公平鎖、多個鎖。這種情況下需要使用 ReentrantLock。

4.1.20. ConcurrentHashMap 併發

4.1.21. Java 中用到的線程調度

4.1.21.1. 搶佔式調度
搶佔式調度指的是每條線程執行的時間、線程的切換都由系統控制。一個線程的堵塞不會導致整個進程堵塞。

4.1.21.2. 協同式調度
協同式調度指某一線程執行完後主動通知系統切換到另一線程上執行。線程的執行時間由線程本身控制,線程切換可以預知,不存在多線程同步問題,但處理不當可能會導致整個進程堵塞。

4.1.21.3. JVM 的線程調度實現(搶佔式調度)
java 使用的線程調使用搶佔式調度,Java 中線程會按優先級分配 CPU 時間片運行。

4.1.22. 進程調度算法

4.1.22.1. 優先調度算法
例如:先來先服務調度算法(FCFS),短作業(進程)優先調度算法

4.1.22.2. 高優先權優先調度算法
例如:非搶佔式優先權算法,搶佔式優先權調度算法,高響應比優先調度算法(作業的優先級隨着等待時
間的增加而提高)

4.1.22.3. 基於時間片的輪轉調度算法
例如:時間片輪轉法,多級反饋隊列調度算法(設置多個就緒隊列,併爲各個隊列賦予不同的優先級和時間片大小)

4.1.23. 什麼是CAS(比較並交換-樂觀鎖機制-鎖自旋)

CAS(Compare And Swap/Set)比較並交換,CAS 算法的過程是這樣:
它包含 3 個參數 CAS(V,E,N)。V 表示要更新的變量(內存值),E 表示預期值(舊的),N 表示新值。當且僅當 V 值等於 E 值時,纔會將 V 的值設爲 N,如果 V 值和 E 值不同,則說明已經有其他線程做了更新,則當 前線程什麼都不做。最後,CAS 返回當前 V 的真實值。
並且,通過版本號的方式來保證了完成讀數據和比較並交換的操作的原子性。

4.1.24. 什麼是AQS(抽象的隊列同步器)

AbstractQueuedSynchronizer 類如其名,抽象的隊列式的同步器,AQS 定義了一套多線程訪問共享資源的同步器框架,許多同步類實現都依賴於它,如常用的 ReentrantLock/Semaphore/CountDownLatch。

5. JAVA基礎

5.1.1. JAVA 異常分類及處理

Throwable 是 Java 語言中所有錯誤或異常的超類。下一層分爲 Error 和 Exception。
Exception 又有兩個分支,一個是運行時異常 RuntimeException(可能在 Java 虛擬機正常運行期間拋出的異常的超類),一個是 CheckedException(一般是外部錯誤,這種異常都發生在編譯階段,Java 編譯器會強制程序去捕獲此類異常)。

5.1.2. JAVA 反射

反射機制是指在運行狀態中,對於任意一個類都能夠知道這個類所有的屬性和方法;並且對於任意一個對象,都能夠調用它的任意一個方法。
反序列化是基於反射實現的。

// 獲取 Person 類的 Class 對象
// Person p=new Person();
// Class clazz=p.getClass();
// Class clazz=Person.class;
// Class clazz=Class.forName("類的全路徑"); (最常用,安全&性能最好)
Class clazz=Class.forName("reflection.Person");
// 獲取 Person 類的所有方法信息
Method[] method=clazz.getDeclaredMethods(); for(Method m:method){
System.out.println(m.toString()); }
// 獲取 Person 類的所有成員屬性信息
Field[] field=clazz.getDeclaredFields(); for(Field f:field){
System.out.println(f.toString()); }
// 獲取 Person 類的所有構造方法信息
Constructor[] constructor=clazz.getDeclaredConstructors(); for(Constructor c:constructor){
System.out.println(c.toString());
}

5.1.3. JAVA 註解

Annatation(註解)是一個接口,程序可以通過反射來獲取指定程序中元素的 Annotation 對象,然後通過該Annotation 對象來獲取註解中的元數據信息。

元註解的作用是負責註解其他註解。 Java5.0 定義了 4 個標準的 meta-annotation 類型,它們被用來提供對其它 annotation 類型作說明。

  • @Target 定義修飾的對象範圍
  • @Retention 定義被保留的時間長短,支持保留到源文件時,編譯時,或者運行時
  • @Documented 描述-javadoc
  • @Inherited 闡述了某個被標註的類型是被繼承的

內置的3種標準註解:

  • @Override 表示當前的方法定義將覆蓋超類中的方法。如果方法簽名對不上被覆蓋的方法,編譯器就會發出錯誤提示。
  • @Deprecated 如果使用了註解爲它的元素,那麼編譯器會發出警告信息。
  • @SuppressWarnings 關閉不當的編譯器警告信息。

自定義註解的實例:

// 1: 定義註解
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
// 使用關鍵字 @interface。在底層實現上,所有定義的註解都會自動繼承java.lang.annotation.Annotation接口
public @interface FruitProvider {
    public int id() default -1;
    public String name() default "";
    public String address() default "";
}

// 2: 使用註解
public class Apple {
    @FruitProvider(id = 1, name = "陝西紅富士集團", address = "陝西省西安市延安路")
    private String appleProvider;

    public void setAppleProvider(String appleProvider) {
        this.appleProvider = appleProvider;
    }

    public String getAppleProvider() {
        return appleProvider;
    }
}

// 3: 解釋註解(註解處理器)
public class FruitInfoUtil {
    public static void getFruitInfo(Class<?> clazz) {
        String strFruitProvicer = "供應商信息:";
        //通過反射獲取處理註解,因爲我們的註解是定義在Filed上的
        Field[] fields = clazz.getDeclaredFields(); 
        for (Field field : fields) {
            if (field.isAnnotationPresent(FruitProvider.class)) {
                FruitProvider fruitProvider = (FruitProvider) field.getAnnotation(FruitProvider.class); //註解信息的處理地方
                strFruitProvicer = " 供應商編號:" + fruitProvider.id() + " 供應商名稱:"
                        + fruitProvider.name() + " 供應商地址:" + fruitProvider.address();
                System.out.println(strFruitProvicer);
            }
        }
    }
}

// 測試
public class FruitRun {
    public static void main(String[] args) {
        FruitInfoUtil.getFruitInfo(Apple.class);
/***********輸出結果***************/
// 供應商編號:1 供應商名稱:陝西紅富士集團 供應商地址:陝西省西安市延
    }
}

5.1.4. JAVA 內部類

根據定義的方式不同,內部類分爲靜態內部類,成員內部類,局部內部類,匿名內部類四種。

5.1.4.1. 靜態內部類

  • HashMap 內部就有一個靜態內部類 Entry。
  • 靜態內部類可以訪問外部類所有的靜態變量和方法,即使是 private 的也一樣。
  • 靜態內部類和一般類一致,可以定義靜態變量、方法,構造方法等。
  • 其它類使用靜態內部類需要使用“外部類.靜態內部類”方式。

5.1.4.2. 成員內部類

  • 成員內部類不能定義靜態方法和變量(final 修飾的除外)。
  • 成員內部類可以王文外部類所有的變量和方法,包括private和static的。

5.1.4.3. 局部內部類(定義在方法中的類)

5.1.4.4. 匿名內部類(要繼承一個父類或者實現一個接口、直接使用 new 來生成一個對象的引用)

5.1.5. JAVA 泛型

泛型的本質是參數化類型,也就是說所操作的數據類型被指定爲一個參數。

Java 中的泛型基本上都是在編譯器這個層次來實現的。在生成的 Java 字節代碼中是不包含泛型中的類型信息的。使用泛型的時候加上的類型參數,會被編譯器在編譯的時候去掉。這個過程就稱爲類型擦除。

類型通配符一般是使用?代替具體的類型參數,?泛型對象是隻讀的。

  • <? extends T>表示該通配符所代表的類型是 T 類型的子類。
  • <? super T>表示該通配符所代表的類型是 T 類型的父類。

5.1.6. JAVA 序列化(創建可複用的 Java 對象)

  • 保存(持久化)對象及其狀態到磁盤,或者遠程方法調用時,需要序列化。
  • 序列化對象以字節數組保存,類的靜態成員不保存。
  • 只要一個類實現了 java.io.Serializable 接口,那麼它就可以被序列化。
  • 在類中增加 writeObject 和 readObject 方法可以實現自定義序列化策略。
  • 通過 ObjectOutputStream 和 ObjectInputStream 對對象進行序列化及反序列化。
  • Transient 關鍵字阻止該變量被序列化到文件中(反序列化時會設置爲初始值)。
  • 虛擬機是否允許反序列化,不僅取決於類路徑和功能代碼是否一致,一個非常重要的一點是兩個類的序列化 ID 是否一致(就是 private static final long serialVersionUID)

5.1.7. JAVA 對象的複製

5.1.7.1. 直接賦值複製
A a1 = a2,這實際上覆制的是引用,也就是說 a1 和 a2 指向的是同一個對象。因此,當 a1 變化的時候,a2 裏面的成員變量也會跟着變化。
同理,對象作爲參數傳遞時,是把對象在內存中的地址拷貝了一份傳給了參數。
String類的特殊性在於其操作符重載時new了新對象,所以,有類似於基本類型一樣的傳值效果:String str = "Hello";等價於String str = new String("Hello");str = str + " world!";等價於str = new String((new StringBuffer(str)).append(" world!"));

5.1.7.2. 淺複製(複製引用但不復制引用的對象)
如果字段是值類型的,那麼對該字段執行復制;如果該字段是引用類型的話,則複製引用但不復制引用的對象。

5.1.7.3. 深複製(複製對象和其應用對象)
深拷貝不僅複製對象本身,而且複製對象包含的引用指向的所有對象。
可以利用序列化/反序列化機制來深複製一個對象:先使對象實現 Serializable 接口,然後把這個對象寫到一個流裏,再從流裏讀出來,便可以重建對象。

6. SPRING

6.1.1. Spring 特點

Spring 是一個全面的、企業應用開發一站式的解決方案,貫穿表現層、業務層、持久層。但是 Spring 仍然可以和其他的框架無縫整合。
Spring 具有輕量級,控制反轉,面向切面,是容器和框架集合的特點。

2014 年至 2017 年期間發佈了許多 Spring 框架 4.xx 系列版本,包含了對Java 8 的全面支持;Spring 5.0 GA版本於2017年9月28日發佈,不再支持JDK8以下的版本。

6.1.2. Spring 核心組件

6.1.3. Spring 常用模塊

  1. 核心容器
    核心容器提供 Spring 框架的基本功能。核心容器的主要組件是 BeanFactory,它使用控制反轉(IOC)模式將應用程序的配置和依賴性規範與實際的應用程序代碼分開。
  2. Spring 上下文
    Spring 上下文是一個配置文件,向Spring 框架提供上下文信息。Spring 上下文包括企業服務,例如 JNDI、EJB、電子郵件、國際化、校驗和調度功能。
  3. Spring AOP
    通過配置管理特性,Spring AOP 模塊直接將面向切面的編程功能集成到了 Spring 框架中。可以將一些通用任務,如安全、事務、日誌等集中進行管理,提高了複用性和管理的便捷性。
  4. Spring DAO
    爲JDBC DAO 抽象層提供了有意義的異常層次結構,並遵從通用的DAO 異常層次結構。
  5. Spring ORM
    Spring 框架插入了若干個ORM 框架,從而提供了ORM 的對象關係工具,其中包括JDO、Hibernate 和 iBatis SQL Map。所有這些都遵從 Spring 的通用事務和DAO 異常層次結構。
  6. Spring Web 模塊
    Web 上下文模塊建立在應用程序上下文模塊之上,爲基於 Web 的應用程序提供了上下文。所以,Spring 框架支持與Jakarta Struts 的集成。
  7. Spring MVC 框架
    MVC 框架是一個全功能的構建 Web 應用程序的 MVC 實現。MVC 框架是高度可配置的,並容納了大量視圖技術,其中包括 JSP、Velocity 和POI。

6.1.4. Spring 主要包

  • org.springframework.core
    Spring 的核心工具包,其他包依賴此包。
  • org.springframework.beans
    所有應用都用到,包含訪問配置文件,創建和管理 bean 等。
  • org.springframework.aop
    Spring 的面向切面編程,提供AOP (面向切面編程)的實現。
    -org.springframework.web.context
    提供在基礎 IOC 功能上的擴展服務,此外還提供許多企業級服務的支持,有右鍵服務、任務調度、JNDI 定位、EJB 集成、遠程訪問、緩存以及多種視圖層框架的支持。
  • org.springframework.web.mvc
    包含SpringMVC 應用開發時所需要的核心類。
  • org.springframework.transaction
    爲JDBC、Hibernate、JDO、JPA 提供一致的聲明式和編程式事務管理
  • org.springframework.web
    包含 Web 應用開發時,用到Spring 框架時所需的核心類。
  • org.springframework.aspects
    Spring 提供的對AspectJ 框架的整合。
  • org.springframework.test
    對 JUNIT 等測試框架的簡單封裝。
  • org.springframework.asm
    spring3.0 開始提供自己獨立的 asm.jar 包。
  • org.springframework.context.support
    Spring context 的擴展支持,用於 MVC 方面。
  • org.springframework.expression
    Spring 表達式語言。
  • org.springframework.instrument.tomcat
    Spring 對 tomcat 連接池的集成。
  • org.springframework.instrument
    Spring 對服務器的代理接口。
  • org.springframework.jdbc
    對 JDBC 的簡單封裝。
  • org.springframework.jms
    爲簡化 jms api 的使用而做的簡單封裝。
  • org.springframework.orm
    整合第三方的 orm 實現,如 hibernate、ibatis、jdo、jpa等。
  • org.springframework.oxm
    Spring 對於 object/xml 映射的支持,可以讓 JAVA 與XML 來回切換。
  • org.springframework.web.portlet
    Spring MVC 的增強。
  • org.springframework.web.servlet
    對 J2EE6.0 servlet3.0 的支持。
  • org.springframework.web.struts
    整合對 Struts 框架的支持,更方便更容易的集成Struts 框架。

6.1.5. Spring 常用註解

Spring的一個核心功能是IOC,就是將Bean初始化加載到容器中,Bean是如何加載到容器的,可以使用Spring註解方式,就和XML配置方式一樣。

  • 聲明bean的註解
    • @Component 組件,沒有明確的角色
    • @Service 在業務邏輯層使用(service層)
    • @Repository 在數據訪問層使用(dao層)
    • @Controller 在展現層使用,控制器的聲明(C)
  • 注入bean的註解
    • @Autowired:由Spring提供(默認按照類型裝配)
    • @Resource:由JSR-250提供(使用性更爲靈活,可指定名稱,也可以指定類型)
  • java配置類相關注解
    • @Configuration 聲明當前類爲配置類,作爲 bean 定義的資源,相當於xml形式的Spring配置(類上)
    • @Bean 註解在方法上,聲明當前方法的返回值爲一個bean,替代xml中的方式;並可以使用@Scope註解等來詳述該bean的作用域範圍等(方法上)
    • @ComponentScan 用於對Component進行掃描,相當於xml中的(類上)
  • SpringMVC部分
    • @RequestMapping 用於映射Web請求,包括訪問路徑url和參數(類或方法上)
    • @ResponseBody 支持將返回值放在response內,而不是一個頁面,通常用於返回json數據(返回值旁或方法上)
    • @RequestBody 使得方法參數被綁定到HTTP請求Body上。(放在參數前)
    • @PathVariable 將修飾的參數變爲可供使用的uri變量,可用於動態綁定,支持Restful接口。例如,@RequestMapping(“/hello/{name}”)可以定義參數@PathVariable String name來提取。(放在參數前)
    • @RestController 該註解爲一個組合註解,相當於@Controller和@ResponseBody的組合,用於支持REST的API。
    • @ModelAttribute 當它作用在方法上時,標明該方法的目的是添加一個或多個模型屬性(model attributes),並且該方法會在@RequestMapping修飾的方法調用之前被調用。
  • 其他
    • @Transactional 處理dao層或service層的事務操作時使用該註解,譬如刪除失敗時的回滾操作。(方法上)

6.1.6. Spring 第三方結合

持久層:

  • Hibernate:一個標準的ORM框架(對象關係映射),即完成數據庫表和持久化類之間的映射
  • Mybatis:僅支持基本的字段映射;針對的SQL-Maping,專注sql本身,需要程序員自己編寫sql語句

6.1.7. Spring IOC 原理

6.1.7.1. 概念
Spring 通過一個配置文件描述 Bean 及 Bean 之間的依賴關係,利用 Java 語言的反射功能實例化 Bean 並建立 Bean 之間的依賴關係。 Spring 的 IoC 容器在完成這些底層工作的基礎上,還提供 了 Bean 實例緩存、生命週期管理、 Bean 實例代理、事件發佈、資源裝載等高級服務。

6.1.7.3. IOC 容器實現

  • BeanFactory 是 Spring 框架的基礎設施,面向 Spring 本身;
  • ApplicationContext 派生自 BeanFactory, 面向開發者,提供了資源加載,消息源處理,事件機制,生命週期管理等必要功能;
  • WebApplicationContext是實現ApplicationContext接口的子類,是專門爲WEB應用準備的。

6.1.7.4. Spring Bean 作用域

  • singleton:單例模式,缺省作用域,多線程下不安全
  • prototype:原型模式,每次使用時創建,每個 Bean 實例都有自己的屬性和狀態
  • Request:一次 request 一個實例
  • session:一次 Http Session 一個實例
  • global Session:一個全局的 Http Session 一個實例,僅在 使用 portlet context 時有效

6.1.7.5. Spring Bean 生命週期

  1. 實例化(new操作)
  2. IOC 依賴注入(配置,設置屬性)
  3. 可選:setBeanName 實現
  4. 可選:BeanFactoryAware 實現
  5. 可選:ApplicationContextAware 實現
  6. 可選:postProcessBeforeInitialization 接口實現-初始化預處理
  7. 可選:init-method 自定義初始化
  8. 可選:postProcessAfterInitialization
  9. 可選:Destroy 過期自動清理階段
  10. 可選:destroy-method 自定義清理

6.1.7.6. Spring 依賴注入四種方式

  • 構造器注入
  • setter 方法注入
  • 靜態工廠注入
  • 實例工廠

6.1.7.7. 5 種不同方式的自動裝配
Spring 裝配包括手動裝配和自動裝配,手動裝配是有基於 xml 裝配、構造方法、setter 方法等,自動裝配方式來進行依賴注入的方法如下:

  • no:默認的方式是不進行自動裝配
  • byName:通過參數名 自動裝配,容器試圖匹配、裝配和該 bean 的屬性具有相同名字的 bean
  • byType:通過參數類型自動裝配,容器試圖匹配、裝配和該 bean 的屬性具有相同類型的 bean
  • constructor:這個方式類似於 byType, 但是要提供給構造器參數
  • autodetect:首先嚐試使用 constructor 來自動裝配,如果無法工作,則使用 byType 方式

5.1.8. Spring AOP 原理

業務處理的主要流 程是核心關注點,與之關係不大的部分是橫切關注點(比如,比如權限認證、日誌、事務)。
AOP 的作用在於將核心關注點和橫切關注點分離開來。

所謂"切面",簡單說就是那些與業務無關,卻爲業務模塊所共同調用的邏輯或責任封裝起來的可重用模塊。
切面便於減少系統的重複代碼,降低模塊之間的耦合度,並有利於未 來的可操作性和可維護性。

Spring 提供了兩種方式來生成代理對象: JDKProxy 和 CGLib,默認的策略是如果目標類是接口, 則使用 JDK 動態代理技術,否則使用 CGLib 來生成代理。

AOP實例:

@Aspect
public class TransactionDemo {
    // 基於註解的方式:@Pointcut("@annotation(com.annotations.MyAnnotation)")
    @Pointcut(value = "execution(* com.core.service.*.*.*(..))")
    public void point() {
    }

    @Before(value = "point()")
    public void before() {
        System.out.println("transaction begin");
    }

    @AfterReturning(value = "point()")
    public void after() {
        System.out.println("transaction commit");
    }

    @Around("point()")
    public void around(ProceedingJoinPoint joinPoint) throws Throwable {
        System.out.println("transaction begin");
        joinPoint.proceed();
        System.out.println("transaction commit");
    }
}

5.1.9. Spring MVC原理

Spring 的模型-視圖-控制器(MVC)框架是圍繞一個 DispatcherServlet 來設計的,流程如下:

  1. Http 請求到 DispatcherServlet
  2. DispatcherServlet 查詢一個或多個 HandlerMapping 尋找處理器
  3. DispatcherServlet 調用處理器 Controller
  4. Controller 調用業務邏輯處理後,給DispatcherServlet返回 ModelAndView
  5. DispatcherServlet 查詢一個或多個 ViewResoler 視圖解析器,找到 ModelAndView 指定的視圖,並將模型數據傳給View顯示
  6. View負責將結果顯示到客戶端,完成Http響應

5.1.10. Spring Boot 原理

Spring Boot只是Spring框架的擴展,它消除了設置Spring應用程序所需的XML配置,使開發、測試和部署更加快捷、方便和高效。

5.1.11. JPA 原理(Java Persistence API)

6.1.11.2. 本地事務
緊密依賴於底層資源管理器(例如數據庫連接 ),舉例:

    public void transferAccount() {
        Connection conn = null;
        Statement stmt = null;
        try {
            conn = getDataSource().getConnection();
            // 將自動提交設置爲 false,若設置爲 true 則數據庫將會把每一次數據更新認定爲一個事務並自動提交
            conn.setAutoCommit(false);
            stmt = conn.createStatement();
            stmt.execute("update t_account set amount = amount - 500 where account_id = 'A'");
            stmt.execute("update t_account set amount = amount + 500 where account_id = 'B'");
            // 提交事務 
            conn.commit();
            // 至此,事務提交成功: 轉賬的兩步操作同時成功 
        } catch (SQLException sqle) {
            // 發生異常,回滾在本事務中的操作
            conn.rollback();
            // 至此,事務回滾成功: 轉賬的兩步操作完全撤銷 
            stmt.close();
            conn.close();
        }
    }

6.1.11.1. 分佈式事務
Java 事務編程接口(JTA:Java Transaction API)和 Java 事務服務 (JTS;Java Transaction Service) 爲 J2EE 平臺提供了分佈式事務服務。
分佈式事務(Distributed Transaction)包括事務 管理器(Transaction Manager)和一個或多個支持 XA 協議的資源管理器 ( Resource Manager )。
在實現上,使用兩階段提交(準備階段和提交階段)來保證分佈式事務的原子性。

在使用中,我們可以將資源管理器看做任意類型的持久化數據存儲。
事務管理器承擔着所有事務 參與單元的協調與控制,我們不必關心。
實例如下:

    public void transferAccount() {
        UserTransaction userTx = null;
        Connection connA = null;
        Statement stmtA = null;
        Connection connB = null;
        Statement stmtB = null;
        try {
            // 獲得 Transaction 管理對象
            userTx = (UserTransaction) getContext().lookup("java:comp/UserTransaction");
            connA = getDataSourceA().getConnection();
            connB = getDataSourceB().getConnection();
            // 啓動事務
            userTx.begin();
            stmtA = connA.createStatement();
            stmtA.execute("update t_account set amount = amount - 500 where account_id = 'A'");
            stmtB = connB.createStatement();
            stmtB.execute("update t_account set amount = amount + 500 where account_id = 'B'");
            // 提交事務
            userTx.commit();
            // 至此,事務提交成功: 轉賬的兩步操作同時成功(數據庫 A 和數據庫 B 中的數據被同時更新)
        } catch (SQLException sqle) {
            // 發生異常,回滾在本事務中的操作
            userTx.rollback();
            // 至此,事務回滾成功: 數據庫 A 和數據庫 B 中的數據更新被同時撤銷
        } catch (Exception ne) {
        }
    }

5.1.12. Mybatis 緩存

Mybatis 中,默認開啓一級緩存且不能關閉。

  • 一級緩存 是指 SqlSession 級別的緩存,當在同一個 SqlSession 中進行相同的 SQL 語句查詢時,第二次以 後的查詢不會從數據庫查詢,而是直接從緩存中獲取,一級緩存最多緩存 1024 條 SQL。出現 commit 操作(增刪改),本 sqlsession 中的一級緩存區域全部清空。
  • 二級緩存 是不同的 sqlsession 可以共享的 mapper 級別的緩存。

7. 微服務

7.1.1. 服務註冊發現

當下用於服 務註冊的工具非常多,如 ZooKeeper,還有Consul,Eureka,SmartStack,Etcd 等。

7.1.1.1. 客戶端註冊(zookeeper)
客戶端註冊是服務自身要負責註冊與註銷的工作。
期間客戶端還需要和註冊中心保持心跳,心跳可以由客戶端或者註冊中心負責。

7.1.1.2. 第三方註冊(獨立的服務 Registrar)
第三方註冊由一個獨立的服務 Registrar 負責註冊與註銷。
當服務啓動後以某種方式通知 Registrar, 然後 Registrar 負責向註冊中心發起註冊工作。同時 Registrar 要維護與服務之間的心跳,當服務不 可用時,向註冊中心註銷服務。

7.1.1.3. 客戶端發現
客戶端發現是指客戶端負責查詢可用服務的實際地址並請求服務,以及負責負載均衡的工作。

7.1.1.4. 服務端發現
服務端發現需要額外的 Router 服務,請求先打到 Router,然後 Router 負責查詢服務與負載均衡。

7.1.2. API 網關

API Gateway 負責請求轉發、聚合結果和協議轉換。
所有來自客戶端的請求都要先經過 API Gateway,然後路由這些請求到對應的一個或多個微服務,並聚合結果。
還可能有 其他功能,如安全認證、監控、負載均衡、緩存、請求分片和管理、靜態響應處理等。

7.1.3. 配置中心

配置中心一般用作系統的參數配置,它需要滿足如下幾個要求:高效獲取、實時感知、分佈式訪
問。
基於 zookeeper 配置中心的實現架構中,採取數據加載到內存方式解決高效獲取的問題,藉助 zookeeper 的節點監聽機制來實現實時感知。

7.1.4. 事件調度(kafka)

消息服務和事件的統一調度,常用用 kafka ,activemq 等。

7.1.5. 服務跟蹤(starter-sleuth)

Spring Cloud Sleuth 通過在日誌中引入唯一標識 Trace ID,將請求過程的所有日誌關聯起來,可以跟蹤一個請求從一個微服務到下一個微服務的傳播過程。

7.1.6. 服務熔斷(Hystrix)

當 Hystrix Command 請求後端服務失敗數量超過一定比例(默認 50%), 斷路器會 切換到開路狀態(Open). 這時所有請求會直接失敗而不會發送到後端服務.
斷路器保持在開路狀態 一段時間後(默認 5 秒), 自動切換到半開路狀態(HALF-OPEN). 這時會判斷下一次請求的返回情況, 如果請求成功, 斷路器切回閉路狀態(CLOSED), 否則重新切換到開路狀態(OPEN).

服務熔斷可以避免服務雪崩效應,避免發送大量無效 請求影響系統吞吐量, 並且斷路器有自我檢測並恢復的能力。

7.1.7. API 管理

Swagger,完成定義接口及接口相關信息的描述文件,就可以自動生成接口文檔(多格式)和客戶端、服務端的代碼(多語言),以及在線接口調試頁面等等。

而有了Spring框架的支持,通過在項目中引入Springfox(前身爲Spring-swagger項目),可以掃描相關的代碼,反向生成描述文件,進而生成與代碼一致的接口文檔和客戶端代碼。

8. NETTY 與 RPC

8.1.1. Netty 原理

Netty 是一個高性能、異步事件驅動的 NIO 框架,基於 JAVA NIO 提供的 API 實現。
它提供了對 TCP、UDP 和文件傳輸的支持,作爲一個異步 NIO 框架,Netty 的所有 IO 操作都是異步非阻塞 的,通過 Future-Listener 機制,用戶可以方便的主動獲取或者通過通知機制獲得 IO 操作結果。

8.1.2. Netty 高性能

  • Netty 的 IO 線程 NioEventLoop 由於聚合了多路複用器 Selector,可以同時併發處理成百上千個客戶端 Channel,由於讀寫操作都是非阻塞的,這就可以充分提升 IO 線程的運行效率,避免由於頻繁 IO 阻塞導致的線程掛起。
  • Netty 採用了異步通信模式,一個 IO 線程可以併發處理 N 個客戶端連接和讀寫操作。
  • Netty 的接收和發送 ByteBuffer 採用 DIRECT BUFFERS,使用堆外直接內存進行 Socket 讀寫, 不需要進行字節緩衝區的二次拷貝。
  • Netty 提供了基於內存池的緩衝區重用機制,優化堆外直接內存的分配和回收。
  • 高效的 Reactor 線程模型,支持Reactor 多線程模型, 主從 Reactor 多線程模型。
  • Netty 採用了串行無鎖化設計。只要用戶不主動切換線程,一直會由 NioEventLoop 調用 到用戶的 Handler,期間不進行線程切換。這種串行化處理方式避免了多線程操作導致的鎖 的競爭,從性能角度看是最優的。
  • Netty 默認提供了對 Google Protobuf 的支持,通過擴展 Netty 的編解碼接口,用戶可以實現其它的高性能序列化框架。
  • 將每個連接和 cpu 綁定,並通過連接的 hash 值,來均衡軟中斷在多個 cpu 上,提升 網絡並行處理性能。

8.1.3. Netty RPC實現

8.1.3.2. 關鍵技術

  • 服務發佈與訂閱:服務端使用 Zookeeper 註冊服務地址,客戶端從 Zookeeper 獲取可用的服務 地址。
  • 通信:使用 Netty 作爲通信框架。
  • Spring:使用 Spring 配置服務,加載 Bean,掃描註解。
  • 動態代理:客戶端使用代理模式透明化服務調用。
  • 消息編解碼:使用 Protostuff 序列化和反序列化消息。

8.1.3.4. 消息編解碼
request消息數據結構:接口名稱+方法名+參數類型和參數值+超時時間+ requestID
response消息數據結構:返回值+狀態 code+requestID

8.1.3.1. 通訊過程

  1. AtomicLong自增來生成requestID
  2. 以requestID爲key來存放回調對象 callback 到全局 ConcurrentHashMap
  3. 執行 callback 的 get()方法試 圖獲取遠程返回的結果:synchronized 獲取回調對象 callback 的鎖並自旋 wait
  4. 監聽消息的線程收到消息,找到 callback 上的鎖並喚醒

8.1.4. RMI 實現方式

Java RMI(Java Remote Method Invocation,Java 遠程方法調用)是 Java 編程語言裏,一種用 於實現RPC(Remote Procedure Call,遠程過程調用)的應用程序編程接口。

8.1.5. Protocol Buffer

Protocol Buffer 是 google 的一個開源項目,它是用於結構化數據串行化的靈活、高效、自動的方法。具有語言無關,平臺無關,體積小,序列化速度快,擴展性和兼容性好等優點。缺點是它以二進制流方式存儲(不可讀),從而自解釋性差,通用性差。

8.1.6. Thrift

Apache Thrift 是 Facebook 實現的一種高效的、支持多種編程語言的遠程服務調用的框架。
它採用接口描述語言定義並創建服務,支持可擴展的跨語言服務開發,所包含的代碼生成引擎可以在多種語言中創建高效的、無縫的服務,其傳輸數據採用二進制格式, 對於高併發、大數據量和多語言的環境更有優勢。

9. 網絡

9.1.1. 網絡 7 層架構

  1. 物理層: 主要定義物理設備標準,主要作用是傳輸比特流。
  2. 數據鏈路層: 主要將數據進行 MAC 地址(網卡的地址)的封裝與解封裝。數據叫做幀,工作的設備是交換機。
  3. 網絡層: 主要將數據進行 IP 地址(例 192.168.0.1)的封裝與解封裝。數據叫做數據包,工作的設備是路由器。
  4. 傳輸層: 定義了一些傳輸數據的協議和端口號,如:TCP,UDP。 主要是將數據進行分段進行傳輸,到達目的地址後在進行重組。 常常把這一層數據叫做段。
  5. 會話層: 建立數據傳輸的通路,發起會話或或者接受會話請求。
  6. 表示層: 主要是進行對接收的數據進行解釋、加密與解密、壓縮與解壓縮等。
  7. 應用層: 主要是一些終端的應用軟件。

9.1.2. TCP/IP 原理

TCP/IP 協議不是 TCP 和 IP 這兩個協議的合稱,而是指因特網整個 TCP/IP 協議族:

  1. 網絡接口層:對應物理層+數據鏈路層
  2. 網絡層:IP協議
  3. 傳輸層:TCP、UDP
  4. 應用層:HTTP、FTP、DNS、TELNET、SMTP等

9.1.3. TCP 三次握手/四次揮手

9.1.3.1. 數據包說明

  1. 源端口號( 16 位)
  2. 目的端口號( 16 位)
  3. 順序號 seq( 32 位): 表示TCP中數據字節的順序號。初始順序號不一定爲0,順序號遞增到最大值再次從0開始。
  4. 確認號 ack( 32 位): 僅當 ACK 標誌爲 1 時有效,表示所期望收到的下一個順序號(上次已成功收到數據字節順序號加 1)。
  5. TCP 報頭長度( 4 位): 報頭中 32bit 字的數目,它實際上指明數據從哪裏開始(固定報頭20字節,可選項最長40字節)。
  6. 保留位( 6 位): 保留給將來使用,目前必須置爲 0 。
  7. 控制位( control flags , 6 位): 6 個標誌比特,多個可同時被設置爲 1 。
    • URG :爲 1 表示緊急指針有效,爲 0 則忽略緊急指針值。
    • ACK :爲 1 表示確認號有效,爲 0 表示報文中不包含確認信息,忽略確認號字段。
    • PSH :爲 1 表示是帶有 PUSH 標誌的數據,指示接收方應該儘快將這個報文段交給應用層而不用等待緩衝區裝滿。
    • RST :用於復位由於主機崩潰或其他原因而出現錯誤的連接。它還可以用於拒絕非法的報文段和拒絕連接請求。一般情況下,如果收到一個 RST 爲 1 的報文,那麼一定發生了某些問題。
    • SYN :同步序號,爲 1 表示連接請求,用於建立連接和使順序號同步( synchronize )。
    • FIN :用於釋放連接,爲 1 表示發送方已經沒有數據發送了,即關閉本方數據流。
  8. 窗口大小( 16 位): 源方可以接收的字節數,即源方接收窗口大小。
  9. 校驗和( 16 位): 對頭部和數據的整個 TCP 報文段的校驗和,強制性字段,發送端計算和存儲,並由接收端進行驗證。
  10. 緊急指針(16位): 僅當 URG 標誌置1時有效,和seq一起運算來指示緊急數據的字節號。例如,Telnet會將中止字符作爲緊急數據發送到遠程端。(緊急數據已經是廢棄的功能)
  11. 可選項: 最常見的可選字段是最長報文大小,又稱爲MSS(MaximumSegmentSize)。每個連接方通常都在通信的第一個報文段(爲建立連接而設置 SYN 標誌的那個段)中指明這個選項, 它指明本端所能接收的最大長度的報文段。可選項長度不一定是 32 位字的整數倍,所以要加填充位,使得報頭長度成爲整字數。
  12. 數據: 數據部分是可選的,例如:連接建立和終止;沒有數據發送,只是確認收到數據;處理超時等。

9.1.3.2. 三次握手

  1. 客戶端發送SYN包(seq=j)到服務器,並進入SYN_SENT狀態,等待服務器確認。
  2. 服務器收到SYN包,必須確認客戶的SYN(ack=j+1),同時自己也發送一個SYN包(seq=k),即SYN+ACK包,此時服務器進入SYN_RECV狀態。
  3. 客戶端收到服務器的SYN+ACK包,向服務器發送確認包ACK(ack=k+1),此包發送完畢,客戶端和服務器進入ESTABLISHED(TCP連接成功)狀態,完成三次握手。

9.1.3.3. 四次揮手

  1. 客戶端發送FIN包(seq=u),用來關閉客戶端到服務器的數據傳送,並進入FIN_WAIT_1狀態, 等待服務器確認。
  2. 服務器收到FIN包,發回一個 ACK包(ack=u+1),此包發送完畢,客戶端進入FIN_WAIT_2狀態,服務器進入CLOSE_WAIT狀態。此時,如果服務器還有未發送完的數據,可以繼續發送給客戶端。
  3. 服務器沒有數據需要發送了。發送FIN包(seq=v),用來關閉服務器到客戶端的數據傳送,並進入LAST_ACK狀態。
  4. 客戶端收到FIN包,發回一個 ACK包(ack=v+1),此包發送完畢,客戶端進入TIME_WAIT狀態,服務器進入CLOSED狀態。客戶端再等待2MSL(Maximum Segment Lifetime,報文最大生存時間)後,進入CLOSED狀態。

9.1.4. HTTP 原理

HTTP 是一個無狀態的協議。無狀態是指客戶端和服務器之間不需要建立持久的連接,客戶端發送請求,服務器返回響應,然後連接就被關閉了。

9.1.4.1. 傳輸流程

  1. 地址解析
  2. 封裝 HTTP 請求數據包
  3. 封裝成 TCP 包並建立連接
  4. 客戶端發送請求命令,請求信息組成:請求方法URI協議/版本,請求頭,請求正文
  5. 服務器響應,響應信息組成:狀態行,響應頭,響應正文
  6. 服務器關閉 TCP 連接。如果設置了Connection:keep-alive,則不會關閉TCP連接,從而節省了爲每個請求建立新連接所需的時間。

請求和響應信息示例:

curl -v -d '{"jsonData":"test"}' "http://www.baidu.com/index.html?param=test"

*   Trying 220.181.38.150...
* TCP_NODELAY set
* Connected to www.baidu.com (220.181.38.150) port 80 (#0)
> POST /index.html?param=test HTTP/1.1   ===== 請求方法URI協議/版本
> Host: www.baidu.com   ===== 請求頭,從此處開始的若干行
> User-Agent: curl/7.64.1
> Accept: */*
> Content-Length: 19
> Content-Type: application/x-www-form-urlencoded
>
* upload completely sent off: 19 out of 19 bytes   ===== 請求正文
< HTTP/1.1 200 OK   ===== 狀態行
< Accept-Ranges: bytes   ===== 響應頭,從此處開始的若干行
< Cache-Control: max-age=1
< Connection: Keep-Alive
< Content-Length: 7382
< Content-Type: text/html
< Date: Wed, 29 Jan 2020 13:57:28 GMT
< Etag: "1cd6-5480030886bc0"
< Expires: Wed, 29 Jan 2020 13:57:29 GMT
< Last-Modified: Wed, 08 Feb 2017 07:55:35 GMT
< P3p: CP=" OTI DSP COR IVA OUR IND COM "
< Server: Apache
< Set-Cookie: BAIDUID=4C832C030A4D45EE53C111F5FEE4B72C:FG=1; expires=Thu, 28-Jan-21 13:57:28 GMT; max-age=31536000; path=/; domain=.baidu.com; version=1
< Vary: Accept-Encoding,User-Agent
<
<!doctype html><html>從略...   ===== 響應正文
* Connection #0 to host www.baidu.com left intact
* Closing connection 0

9.1.4.2. HTTP 狀態

  • 1xx:消息響應
    • 100 繼續
  • 2xx:成功響應
    • 200 成功
  • 3xx:重定向
    • 301 永久移動
    • 302 臨時移動
    • 304 未修改(使用瀏覽器緩存數據)
  • 4xx:客戶端錯誤
    • 400 請求無效(Bad Request,如格式錯誤的請求語法)
    • 401 未授權(Unauthorized,缺失或者錯誤的認證)
    • 403 禁止訪問(Forbidden,服務器完成認證過程,但是客戶端請求沒有權限去訪問要求的資源)
    • 404 未找到資源
  • 5xx:服務器錯誤
    • 500 內部服務器錯誤
    • 502 網關錯誤(Bad Gateway,作爲網關或者代理工作的服務器如Nginx嘗試執行請求時,從上游服務器接收到無效的響應。一般是上游服務器掛了。)
    • 503 服務不可用(Service Unavailable,由於臨時的服務器維護或者過載,服務器當前無法處理請求。這個狀況是臨時的,並且將在一段時間以後恢復。)
    • 504 網關超時(Gateway Timeout,作爲網關或者代理工作的服務器如Nginx嘗試執行請求時,未能及時從上游服務器收到響應。)

9.1.4.3. HTTPS
HTTPS即 HTTP 下加入 SSL 層,其端口號是443。

  1. 建立連接獲取證書:客戶端發送支持的加密算法的請求,服務器端迴應本次通信使用的算法和證書。
  2. 證書驗證:判斷證書中的域名和當前請求域名一致,判斷簽發機構在可信列表,使用簽發機構公鑰來驗證簽名有效。
  3. 數據加密和傳輸:生成對稱祕鑰並使用證書裏服務器的公鑰進行加密後發送給服務器,服務器使用它的私鑰解密後獲得對稱密鑰,並在客戶端和服務器之間開始進行對稱加密的通信。

9.1.5. CDN 原理

CDN一般包含分發服務系統、負載均衡系統和管理系統:

  • 分發服務系統基本的工作單元就是各個 Cache 服務器,負責直接響應用戶請求,將內容快速分發到用戶;同時還 負責內容更新,保證和源站內容的同步。
  • 負載均衡系統負責對所有的用戶請求進行調度,確定提供給用戶的最終訪問 地址。
  • 管理系統實現對 CDN 系統的設備管理、拓撲管理、鏈路監控和故障管理等,以及CDN 系統的業務管理等。

10. 日誌

10.1.1. Slf4j

slf4j 的全稱是 Simple Loging Facade For Java,即它僅僅是一個爲 Java 程序提供日誌輸出的統一接口,並不是一個具體的日誌實現方案。
slf4j 在底層整合了衆多日誌框架(logback, log4j等),可以方便的切換底層的日誌框架,而不用修改代碼。

10.1.2. Log4j

Log4j 是 Apache 的一個開源項目,是目前最流行的日誌框架之一。
它使用Logger來控制啓用哪些日誌並設置級別,使用Appenders指定輸出端,使用Layout控制日誌格式。

10.1.3. LogBack

Logback 被認爲是 Log4J 的繼承人。
Logback原生實現了 SLF4J API(Log4J 還需要有一箇中間轉換層),並且速度更快,測試更充分,文檔更齊全。
還提供了配置文件自動熱加載,透明和快速的從I/O錯誤中恢復,自動壓縮字和刪除舊日誌等更多功能。

10.1.4. ELK

ELK 是軟件集合 Elasticsearch、Logstash、Kibana 的簡稱,由這三個軟件及其相關的組件可以打造大規模日誌實時處理系統。

  • Elasticsearch 是一個基於 Lucene 的、支持全文索引的分佈式存儲和索引引擎,主要負責將 日誌索引並存儲起來,方便業務方檢索查詢。
  • Logstash 是一個日誌收集、過濾、轉發的中間件,主要負責將各條業務線的各類日誌統一收集、過濾後,轉發給 Elasticsearch 進行下一步處理。
  • Kibana 是一個可視化工具,主要負責查詢 Elasticsearch 的數據並以可視化的方式展現給業務方,比如各類餅圖、直方圖、區域圖等。

11. ZOOKEEPER

11.1.1. Zookeeper 概念和角色

Zookeeper 是一個分佈式協調服務,可用於服務發現,配置管理等。

一個 Zookeeper 集羣同一時間只會有一個實際工作的 Leader,它會發起並維護與各 Follwer 及 Observer 間的心跳。所有的寫操作必須要通過 Leader 完成再由 Leader 將寫操作廣播給其它服務器進行同步,只要有超過半數節點寫入成功,該寫請求就會被提交。
一個 Zookeeper 集羣可能同時存在多個 Follower,負責直接處理並返回客戶端的讀請求,寫請求需要轉發給 Leader 處理,並在 Leader 處理寫請求時對請求進行投票。
Observer 與 Follower 類似,但是無投票權。加入更多 Observer 節點,提高伸縮性,同時不影響吞吐率。

11.1.2. Zookeeper 工作原理(原子廣播)

Zookeeper 的核心是原子廣播,這個機制保證了各個 server 之間的同步。實現這個機制的協議叫做 Zab 協議。

Zab 協議有兩種模式,它們分別是恢復模式和廣播模式。
當服務啓動或者在領導者崩潰後,Zab 就進入了恢復模式,當領導者被選舉出來,且大多數 server 的完成了和 leader 的狀態同步以後,恢復模式就結束了,就進入了廣播模式。

當 leader 崩潰或者 leader 失去大多數的 follower,這時候 zk 進入恢復模式,無法提供正常服務,直到恢復模式結束,此過程大約需要30s或者更多。

12. KAFKA

12.1.1. Kafka 概念

Kafka 是一種高吞吐量、分佈式、基於發佈/訂閱的消息系統。

12.1.2. Kafka 數據存儲設計

partition的數據文件包含屬性:offset,MessageSize,data。
數據文件分段segment存儲,順序讀寫,二分查找定位Message。
數據文件索引是分段索引,稀疏存儲保存在內存中。

12.1.3. 生產者設計

負載均衡:partition會均衡分佈到不同broker(Kafka服務器)上。
一次請求批量發送消息。
支持壓縮消息(GZIP或Snappy)。

12.1.4. 消費者設計

同一 Consumer Group 中的多個 Consumer 實例,不同時消費同一個 partition。

13. RabbitMQ

13.1.1. 概念

RabbitMQ 是一個由 Erlang 語言開發的 AMQP(Advanced Message Queue,高級消息隊列協議) 的開源實現。

13.1.3. Exchange 類型

Exchange 交換器,負責將消息路由給隊列,然後消費者從隊列中獲取消息。
目前有4種Exchange類型:

  • Direct: 消息的路由鍵(routing key)如果和 Binding(基於路由鍵將交換器和消息隊列連接起來的路由規則)中的路由鍵一致,就將消息發到對應的隊列中。它是完全匹配、單播的模式。
  • Fanout: 消息會分到該交換器所有綁定的隊列上去。fanout 類型轉發消息是最快的。
  • topic 交換器: 隊列需要綁定到一個模式上(支持通配符),如果消息的路由鍵和該模式相匹配,就將消息發送給對應的隊列。
  • headers: 類似Exchange,但是是匹配消息的 header,性能差很多,目前幾乎用不到。

14. HBASE

14.1.1. 概念

Hbase 是一個通過大量廉價的機器解決海量數據的高速存儲和讀取的分佈式數據庫解決方案。
HDFS 爲 Hbase 提供可靠的底層數據存儲服務,MapReduce 爲 Hbase 提供高性能的計算能力,Zookeeper 爲 Hbase 提供穩定服務和 Failover 機制。

14.1.2. 列式存儲

Hbase 是根據列族來存儲數據的。

14.1.3. Hbase 核心概念

  • Column Family 列族,列族下面可以包含任意多的列。官方推薦的是列族最好小於或者等於 3。
  • Hbase 使用 Rowkey 來唯一的區分某一行的數據。
  • Hbase 會將一個大表的數據基於 Rowkey 的不同範圍分配到不同的 Region 分區中。
  • 在 Hbase 中使用不同的 TimeStamp 來標識相同 rowkey 行對應的不同版本的數據。

14.1.5. Hbase 的寫邏輯

  1. Client 獲取數據寫入的 Region 所在的 RegionServer。(通過zookeeper -> META -> 數據)
  2. 請求寫 Hlog。(Hlog 存儲在 HDFS,當 RegionServer 出現異常,需要使用 Hlog 來恢復數據)
  3. 請求寫 MemStore。(MemStore 後續會逐漸刷到 HDFS 中)

15. MongoDB

MongoDB 是一個基於分佈式的面向文檔存儲的開源數據庫系統。

16. CASSANDRA

16.1.1. 概念

Apache Cassandra 是高度可擴展的,高性能的分佈式 NoSQL 數據庫。

16.1.4. Gossip 協議

在一個有界網絡中, 每個節點都隨機地與其他節點通信,經過一番雜亂無章的通信,最終所有節點的狀態都會達成一致。
因爲 Gossip 不要求節點知道所有其他節點,因此又具有去中心化的特點,節點之間完全對等。
Gossip是弱一致性,是“最終一致性”,適合的領域有: 失敗檢測、 路由同步、Pub/Sub、動態負載均衡。

16.1.6. 數據寫請求和協調者

協調者(coordinator)將 write 請求發送到擁有對應 row 的所有 replica 節點,只要節點可用便獲取並執行寫請求。
寫一致性級別(write consistency level)確定要有多少個 replica 節點必須返回成功的確認信息。

16.1.7. 數據讀請求和後臺修復

協調者首先與一致性級別確定的所有 replica 聯繫,獲得請求的數據,然後使用含 最新數據的 replica 向 client 返回結果。
協調者在後臺聯系和比較來自其餘擁有對應 row 的 replica 的數據,若不一致,會向過時的 replica 發寫請求用最新的數據進行更新 read repair。

16.1.8. 數據存儲(CommitLog、MemTable、SSTable)

寫請求分別到磁盤的 CommitLog 和內存的 MemTable, 並且 MemTable 的數據會不定時刷寫到磁盤 SSTable 上。

16.1.10. 數據讀寫

Cassandra 中,無論是 insert 還是 remove 操作,都是在已有的數據後面進行追加,而不修改已有的數據。
這種設計稱爲 Log structured 存儲,就是系統中的數據是以日誌的形式存在的。
這種設計使得數據的寫和刪除效率極高,錯誤恢復簡單;但是讀的複雜度很高。

刪除一個 column 其實只是插入一個關於這個 column 的墓碑(tombstone),並不直接刪除原 有的 column。

cassandra 讀取的數據是 memtable 中的數據和 SStables 中數據的合併結果。

17. 設計模式

17.1.1. 設計原則

  1. 開閉原則:對擴展開放,對修改關閉
  2. 里氏替換原則:繼承必須確保父類所擁有的性質在子類中仍然成立
  3. 依賴倒置原則:要面向接口編程,不要面向實現編程
  4. 單一職責原則:對象不應該承擔太多職責
  5. 接口隔離原則:拆分接口,只包含客戶感興趣的方法
  6. 最少知識原則:如果兩個軟件實體無須直接通信,那麼就不應當發生直接的相互調用,可以通過第三方轉發該調用
  7. 合成複用原則:在軟件複用時,要儘量先使用組合或者聚合等關聯關係來實現,其次才考慮使用繼承關係來實現

17.1.2. 工廠方法模式

定義一個用於創建產品的接口,由子類決定生產什麼產品。

17.1.3. 抽象工廠模式

提供一個創建產品族的接口,其每個子類可以生產一系列相關的產品。

17.1.4. 單例模式

某個類只能生成一個實例,該類提供了一個全局訪問點供外部獲取該實例,其拓展是有限多例模式。

17.1.5. 建造者模式

將一個複雜對象分解成多個相對簡單的部分,然後根據不同需要分別創建它們,最後構建成該複雜對象。

17.1.6. 原型模式

將一個對象作爲原型,通過對其進行復制而克隆出多個和原型類似的新實例。

17.1.7. 適配器模式

將一個類的接口轉換成客戶希望的另外一個接口,使得原本由於接口不兼容而不能一起工作的那些類能一起工作。

17.1.8. 裝飾器模式

動態的給對象增加一些職責,即增加其額外的功能。

17.1.9. 代理模式

爲某對象提供一種代理以控制對該對象的訪問。即客戶端通過代理間接地訪問該對象,從而限制、增強或修改該對象的一些特性。

17.1.10. 外觀模式

爲多個複雜的子系統提供一個一致的接口,使這些子系統更加容易被訪問。

17.1.11. 橋接模式

將抽象與實現分離,使它們可以獨立變化。它是用組合關係代替繼承關係來實現,從而降低了抽象和實現這兩個可變維度的耦合度。

17.1.12. 組合模式

將對象組合成樹狀層次結構,使用戶對單個對象和組合對象具有一致的訪問性。

17.1.13. 享元模式

運用共享技術來有效地支持大量細粒度對象的複用。

17.1.14. 策略模式

定義了一系列算法,並將每個算法封裝起來,使它們可以相互替換,且算法的改變不會影響使用算法的客戶。

17.1.15. 模板方法模式

定義一個操作中的算法骨架,而將算法的一些步驟延遲到子類中,使得子類可以不改變該算法結構的情況下重定義該算法的某些特定步驟。

17.1.16. 觀察者模式

多個對象間存在一對多關係,當一個對象發生改變時,把這種改變通知給其他多個對象,從而影響其他對象的行爲。

17.1.17. 迭代器模式

提供一種方法來順序訪問聚合對象中的一系列數據,而不暴露聚合對象的內部表示。

17.1.18. 責任鏈模式

把請求從鏈中的一個對象傳到下一個對象,直到請求被響應爲止。通過這種方式去除對象之間的耦合。

17.1.19. 命令模式

將一個請求封裝爲一個對象,使發出請求的責任和執行請求的責任分割開。

17.1.20. 備忘錄模式

在不破壞封裝性的前提下,獲取並保存一個對象的內部狀態,以便以後恢復它。

17.1.21. 狀態模式

允許一個對象在其內部狀態發生改變時改變其行爲能力。

17.1.22. 訪問者模式

在不改變集合元素的前提下,爲一個集合中的每個元素提供多種訪問方式,即每個元素有多個訪問者對象訪問。

17.1.23. 中介者模式

定義一箇中介對象來簡化原有對象之間的交互關係,降低系統中對象間的耦合度,使原有對象之間不必相互瞭解。

17.1.24. 解釋器模式

提供如何定義語言的文法,以及對語言句子的解釋方法,即解釋器。

18. 負載均衡

18.1.1. 四層負載均衡vs七層負載均衡

18.1.1.1. 四層負載均衡(目標地址和端口交換)
例如,nginx,F5,lvs,haproxy

18.1.1.2. 七層負載均衡(內容交換)
例如,haproxy,nginx,apache,Mysql proxy

18.1.2. 負載均衡算法/策略

18.1.2.1. 輪循均衡(RoundRobin)
18.1.2.2. 權重輪循均衡(WeightedRoundRobin)
18.1.2.3. 隨機均衡(Random)
18.1.2.4. 權重隨機均衡(WeightedRandom)
18.1.2.5. 響應速度均衡(ResponseTime探測時間)
18.1.2.6. 最少連接數均衡(LeastConnection)
18.1.2.7. 處理能力均衡(CPU、內存等換算)
18.1.2.8. DNS響應均衡(FlashDNS)
18.1.2.9. 哈希算法
18.1.2.10. IP 地址散列(保證客戶端服務器對應關係穩定)
18.1.2.11.URL 散列

18.1.3. LVS

LVS(Linux Virtual Server) 的 IP 負載均衡技術是通過 IPVS 模塊來實現的,IPVS虛擬出一個 IP 地址 VIP,即 Virtual IP,訪問的請求首先經過 VIP 到達負載調度器,然後由負載調度器從 Real Server 列表中選取一個服務節點響應用戶的請求。

調度器轉發請求到後端Real Server的模式有以下幾種:

  • LVS NAT 模式:改寫目標IP爲RIP(Real Server IP),源IP不變
  • LVS DR 模式:改寫源MAC地址和目標MAC地址,源和目標IP不變
  • LVS TUN 模式:再封裝一層 IP 報文,源IP爲DIP(Director Server IP,IVS所在服務器),目標IP爲RIP
  • LVSFULLNAT模式:改寫目標IP爲RIP,源IP爲DIP

18.1.4. Keepalive

Keepalive加入了 vrrp(virtual router redundancy protocal, 虛擬路由器冗餘協議) 的功 能,因此,一方面具有 LVS cluster node healthcheck 功能,另一方面也具有 LVS director failover。

18.1.5. Nginx 反向代理負載均衡

LVS 實現的功能只是對請求數據包的轉發、傳遞,Real Server 看到的請求還是來自客戶端的真實用戶;反向代理服務器在接收訪問用戶請求後,會代理用戶重新發起請求代理下的 Real Server, 最後把數據返回給客戶端用戶,Real Server 看到的請求是來自代理服務器。
Nginx 採用的是多進程(單線程) & 多路IO複用模型,會有一個 master 進程,它fork出來多個相互獨立的 worker 子進程,worker 進程數,一般會設置成機器 cpu 核數。

18.1.6. HAProxy

HAProxy提供高可用性、負載均衡以及基於TCP和HTTP應用的代理,支持虛擬主機,它是免費、快速並且可靠的一種解決方案。
HAProxy實現了一種非阻塞,事件驅動, 單一進程模型,此模型支持非常大的併發連接數。支持多進程,但官方不推薦,因爲每個進程有自己的內存區域會造成一些問題。

19. 數據庫

19.1.1. 存儲引擎

數據庫存儲引擎是數據庫底層軟件組織,不同的存儲引擎提供不同的存儲機制、索引技巧、鎖定水平等功能:

  • InnoDB:底層存儲結構爲 B+樹,只有它支持外鍵約束,支持事務,對頻繁更新友好。從MySQL5.1開始是默認引擎。
  • TokuDB:底層存儲結構爲 Fractal Tree,類似B+樹,非葉子節點帶Message Buffer來緩存更新數據。支持事務,寫入速度快,主要適合歸檔數據。
  • MyIASM:不支持事務,行級鎖和外鍵,但是讀取速度且佔用內存和存儲空間少。
  • Memory:使用存在內存中的內容來創建表,同時支持散列索引(默認)和 B 樹索引。

19.1.2. 索引

  • 從業務角度選擇:
    • 爲經常需要排序、分組和聯合操作的字段建立索引。
    • 爲常作爲查詢條件的字段建立索引。
  • 從實現原理角度選擇:
    • 範圍查詢,函數都不走索引。(覆蓋查詢下不等於會走索引,覆蓋索引:SQL只需要通過索引就可以返回查詢所需要的數據)
    • 聯合索引是最左前綴匹配原則:區分度大(字段不重複的比例)的字段放在前面,避免重複索引。
    • 儘量選擇區分度高的列作爲索引。
    • 如果索引字段的值很長,建立前綴索引,或添加CRC32或MD5僞列並建立索引。
    • 不使用外鍵,而是在應用程序中處理該約束邏輯。原因是外鍵會帶來性能,併發,分庫分表擴展性等多方面問題。
  • 索引維護:
    • 限制索引的數目,刪除不再使用或很少使用的索引: 越多的索引,會使更新表變得很浪費時間。
    • 儘量的擴展索引,不要新建索引。

19.1.3. 數據庫三範式

第一範式(1st NF -列都是不可再分):確保每列的原子性
第二範式(2nd NF-非主鍵列不存在對主鍵的部分依賴):每個表只描述一件事情
第三範式(3rd NF- 不存在對非主鍵列的傳遞依賴)

19.1.4. 數據庫是事務

事務(TRANSACTION)是一個不可分割的工作邏輯單元,具備ACID屬性:原子性、一致性、隔離性、永久性。

19.1.5. 存儲過程(特定功能的SQL語句集)

存儲過程經過第一次編譯後再次調用不需要再次編譯。注意點如下:

  1. 使用平均函數等來替代一些小循環。
  2. 中間結果存放於臨時表,並加索引。
  3. 少使用遊標,它是過程運算,效率低。
  4. 事務越短越好,否則會造成併發操作的阻塞,死鎖。
  5. 使用 try-catch 處理錯誤異常。

19.1.6. 觸發器(一段能自動執行的程序)

觸發器是當對某一個表進行操作時觸發,是一種特殊的存儲過程。

19.1.7. 數據庫併發策略

19.1.7.1. 樂觀鎖
樂觀鎖直到你準備提交所作的更改時纔將對象鎖住,當你讀取以及改變該對象時並不加鎖。

19.1.7.2. 悲觀鎖
悲觀鎖開始讀取以及改變該對象之前就將對象鎖住,並且直到提交了所作的更改之後才釋放鎖。
悲觀鎖所說的加“鎖”,分別是:排它鎖(寫鎖)和共享鎖(讀鎖)。

19.1.7.3. 時間戳
時間戳不使用鎖機制,它在數據庫表中單獨加一列時間戳,比如“TimeStamp”,每次讀出來的時候,把該字段也讀出來,當寫回去的時候,把該字段加 1,提交之前 ,跟數據庫的該字段比較一次,如果比數據庫的值大的話,就允許保存,否則不允許保存。

19.1.8. 數據庫鎖

又分爲:行級鎖、表級鎖、頁級鎖

19.1.9. 基於Redis分佈式鎖

19.1.10. 分區分表

19.1.11. 兩階段提交協議

兩階段提交協議是分佈式事務的一種算法,參與者將操作成敗通知協調者,再由協調者根據所有參與者的反饋情報決定各參與者是否要提交操作還是中止操作,分爲準備階段和提交階段。

19.1.12. 三階段提交協議

與兩階段提交不同的是:

  1. 引入超時機制。同時在協調者和參與者中都引入超時機制。
  2. 在第一階段和第二階段中插入一個準備階段,分CanCommit、PreCommit、DoCommit 三個階段。保證了在最後提交階段之前各參與節點的狀態是 一致的。

19.1.13. 柔性事務

柔性事務遵循BASE理論,包括 基本可用(Basically Available)、柔性狀態(Soft State)、最終一致性 (Eventual Consistency)。分爲以下幾種:

  • 兩階段型:就是分佈式事務兩階段提交。
  • 補償型:TCC 型事務(Try/Confirm/Cancel)可以歸爲補償型。
  • 異步確保型:將一系列同步的事務操作變爲基於消息執行的異步操作。
  • 最大努力通知型(多次嘗試):異步消息通知,並允許在達到最大重試次數之後正常 結束事務。

19.1.14. CAP

CAP 原則指的是在一個分佈式系統中, Consistency(一致性)、 Availability (可用性)、Partition tolerance(分區容錯性),三者不可兼得。

20. 一致性算法

在分佈式系統中,爲保證每個節點執行相同的命令序列,需要在每一條指令上執 行一個“一致性算法”以保證每個節點看到的指令一致。

20.1.1. Paxos Paxos

Paxos 算法解決的問題是一個分佈式系統如何就某個值(決議)達成一致。其過程分爲兩個階段:

  • 準 leader 確定:Proposer 向半數以上的 Acceptor 發送編號爲 N 的 Prepare 請求;當且僅當 N 大於該 Acceptor 已經響應過的所有 Prepare 請求的編號時,它纔會將它已經接受過的編號最大的提案(如果有的話)作爲響應反饋給 Proposer。
  • leader 確認:如果 Proposer 收到半數以上 Acceptor 的響應,它就會發送一個針對[N,V]提案的 Accept 請求給半數以上的 Acceptor,其中 V 是收到的響應中編號最大的提案的 value。當且僅當 N 大於或等於該 Acceptor 已經響應過的所有 Prepare 請求的編號時,它纔會接受該提案。

20.1.2. Zab

Zab( ZooKeeper Atomic Broadcast , ZooKeeper 原子消息廣播協議)協議,會經歷三個步驟達到消息廣播狀態:

  • 初始化/崩潰恢復: 主要就是 Leader 選舉過程;
  • 數據同步: Leader 服務器與其他服務器進行數據同步;
  • 消息廣播: Leader 服務器將數據發送給其他服務器。

20.1.3. Raft

Raft 和 Paxos 一樣只要保證 n/2+1 節 點正常就能夠提供服務。
Raft 的選舉由定時器來觸發,每個節點中定時器的時間都是隨機的。Safety 就是用於保證選舉出來的 Leader 一定包含先前 commited Log 的機制。當請求投票的該 Candidate 的 Term 較大 或 Term 相同 Index 更大則投票,從而,擁有最新的log的follower會成爲新的leader。

20.1.4. NWR

N: 在分佈式存儲系統中,有多少份備份數據
W: 代表一次成功的更新操作要求至少有 w 份數據寫入成功
R: 代表一次成功的讀數據操作要求至少有 R 份數據成功讀取

NWR 值的不同組合會產生不同的一致性效果。根據鴿巢原理,當且僅當 W+R>N 的時候,整個系統對於客戶端來講才能保證強一致性。

20.1.5. Gossip

Gossip 算法又被稱爲反熵(Anti-Entropy),形象地說明了其特點:在一個有界網絡中,每個節點都隨機地與其他節點通信,經過一番雜亂無章的通信,最終所有節點的狀態都會達成一致。

20.1.6. 一致性Hash

一致性哈希算法(Consistent Hashing Algorithm)是一種分佈式算法,常用於負載均衡。

具有以下特點:

  • 平衡性(Balance): 哈希的結果能夠均勻分佈到所有的緩衝中。
  • 單調性(Monotonicity): 如果有新的緩衝加入到系統中,哈希的結果能夠保證原有已分配的內容可以被映射到原本所在的緩存或者新的緩衝中去,而不會被映射到舊的緩衝集合中的其他緩衝區。例如,簡單求餘算法hash(object)%N是不滿足單調性要求的。
  • 平滑性(Smoothness): 緩存服務器數目的平滑改變和緩存對象的平滑改變是一致的。

實現原理:

  1. 建構環形 hash 空間
  2. 把需要緩存的內容(對象)映射到 hash 空間
  3. 把服務器(節點)映射到 hash 空間
  4. 把對象映射到服務節點,沿環順時針尋找

爲了解決分佈不夠均勻和多米諾宕機的問題,引入了虛擬節點:
虛擬節點( virtual node )是實際節點在 hash 空間的複製品( replica ),一個實際節點對應了若干個“虛擬節點”,“虛擬節點”在 hash 空間中以 hash 值排列。
映射關係也變爲了:對象 -> 虛擬節點 -> 實際節點。

21. JAVA 算法

21.1.1. 二分查找

21.1.2. 冒泡排序算法

21.1.3. 插入排序算法

21.1.4. 快速排序算法

21.1.1. 希爾排序算法

定義一個間隔序列來表示排序過程中進行比較的元素之間有多遠的間隔,每次將具有相同間隔的數分爲一組,進行插入排序;間隔序列裏的值單向遞減,最終到1,例如:[n/2, n/4, /n8, …, 1]。
空間複雜度<O(n^2),具體依賴於間隔序列函數。不穩定排序。

21.1.2. 歸併排序算法

21.1.3. 桶排序算法

桶排序(Bucket sort),將數組按照遞增的數值區間依次分到有限數量的桶裏。每個桶再分別排序(其他排序算法或者遞歸桶排序),最後依次把各個桶中的記錄列出來就得到有序序列。
當要被排序的數組內的數值是均勻分配的時候,桶排序使用線性時間O(n),但空間複雜度比較高。

桶排序和基數排序都是分配排序(Distributive Sort),分配排序的基本思想:排序過程無須比較關鍵字,而是通過“分配”和“收集”過程來實現排序。所以,他們不受到O(n log n)下限的影響。

21.1.4. 基數排序算法

將所有待比較數值(正整數)統一爲同樣的數位長度,數位較短的數前面補零。然後,從最低位開始,依次進行一次排序。這樣從最低位排序一直到最高位排序完成以後,數列就變成一個有序序列。
實際操作時,是將依次將個位、十位、百位的相同的數放入同一個桶中,高位不足補零。然後再從桶中收集元素。
在大多數情況下,效率爲O(n*logn),最好情況是O(n)。空間複雜度較高。

21.1.5. 剪枝算法

21.1.6. 回溯算法

21.1.7. 最短路徑算法

21.1.8. 最大子數組算法

21.1.9. 最長公共子序算法

21.1.10. 最小生成樹算法

22. 數據結構

22.1.1. 棧(stack)

22.1.2. 隊列(queue)

22.1.3. 鏈表(Link)

22.1.4. 散列表(Hash Table)

常用的構造散列函數的方法有:

  • 直接定址法: 取關鍵字或關鍵字的某個線性函數值爲散列地址
  • 平方取值法: 取關鍵字平方後的中間幾位爲散列地址。
  • 摺疊法: 將關鍵字分割成位數相同的幾部分,然後取這幾部分的疊加和作爲散列地址。
  • 除留餘數法: 取關鍵字被 n 除後所得的餘數爲散列地址。
  • 隨機數法: 選擇一個隨機函數,取關鍵字的隨機函數值爲它的散列地址。

22.1.5. 排序二叉樹

22.1.6. 紅黑樹

22.1.7. B-TREE

22.1.8. 位圖

23. 加密算法

23.1.1. AES

23.1.2. RSA

23.1.3. CRC

23.1.4. MD5

24. 分佈式緩存

24.1.1. 緩存雪崩

緩存雪崩是因爲緩存大面積地失效,從而導致所有請求都會去查數據庫,導致數據庫、CPU和內存負載過高,甚至宕機,進而造成一系列連鎖反應,整個系統崩潰。

24.1.2. 緩存穿透

緩存穿透是指用戶查詢數據,在數據庫和緩存都沒有,這就導致進行了兩次無用的查詢。

24.1.3. 緩存預熱

緩存預熱就是系統上線後,將相關的緩存數據直接加載到緩存系統。

24.1.4. 緩存更新

24.1.5. 緩存降級

當訪問量劇增、服務出現問題(如響應時間慢或不響應)或非核心服務影響到核心流程的性能時,仍然 需要保證核心服務還是可用的,即使是有損服務。

25. HADOOP

25.1.1. 概念

HADOOP 就是一個大數據解決方案。它提供了一套分佈式系統基礎架構。 核心內容包含 hdfs 和 mapreduce,hdfs 是提供數據存儲的,mapreduce 是方便數據計算的。

25.1.2. HDFS

  • 整個 Hadoop 集羣中只有一個 NameNode。 它負責管理 HDFS 的目錄樹和相關的文件元數據信息,監控各個 DataNode 的健康狀態並管理。
  • Secondary NameNode 會定期合併 fsimage(HDFS 元數據鏡像文件) 和 edits (HDFS 文件改動日誌), 並傳輸給 NameNode。
  • DataNode 負責實際的數據存儲, 並將數據信息定期 彙報給 NameNode。

25.1.3. MapReduce

  • JobTracker 主要負責資源監控和作業調度。它監控所有 TaskTracker 與作業的健康狀況,同時會跟蹤任務的執行進度、資源使用量等信息,並將這些信息告訴任務調度器。
  • TaskTracker 會週期性地通過 Heartbeat 將本節點上資源的使用情況和任務的運行進度彙報給 JobTracker, 同時接收 JobTracker 發送過來的命令並執行相應的操作(如啓動新任務、 殺死任務等)。
  • Task 分爲 Map Task 和 Reduce Task 兩種, 均由 TaskTracker 啓動。

26. SPARK

Spark 提供了一個全面、統一的框架用於管理各種有着不同性質(文本數據、圖表數據等)的數據集和數據源(批量數據或實時的流數據)的大數據處理的需求。

Spark 的核心是建立在統一的抽象彈性分佈式數據集(Resiliennt Distributed Datasets,RDD)之上的,這使得 Spark 的各個組件,如SQLContext、HiveContext、StreamingContext可以無縫地進行集成,能夠在同一個應用程序中完成大數據處理。

27. STORM

Storm 是一個免費並開源的分佈式實時計算系統。利用 Storm 可以很容易做到可靠地處理無限的 數據流,像 Hadoop 批量處理大數據一樣,Storm 可以實時處理數據。

在 Storm 中,一個實時應用的計算任務被打包作爲 Topology 發佈,計算任務 Topology 是由多個 Spouts 和 Bolts,通過數據流(Stream)連接起來的圖。spout 會從外部數據源中讀取數據,然後轉 換爲 topology 內部的源數據,接着,數據以 tuple(一組消息的單元) 的方式發送到 bolt,bolt 執行用戶想要的操作,多個 bolt 可以相互連接起來,每個bolt可以有多個輸入和輸出。

28. YARN

28.1.1. 概念

YARN 是一個資源管理、任務調度的框架,主要包含三大模塊: ResourceManager(RM)、 NodeManager(NM)、ApplicationMaster(AM)。
其中,ResourceManager 負責所有資源的監控、分配和管理; ApplicationMaster 負責每一個具體應用程序的調度和協調; NodeManager 負責每一個節點的維護。對於所有的 applications,RM 擁有絕對的控制權和對資源的分配權。而每個 AM 則會和 RM 協商資源,同時和 NodeManager 通信來執行和監控 task。

29. 機器學習

29.1.1. 決策樹

在進行逐步應答過程中,典型的決策樹分析會使用分層變量或決策節點,例如,可將一個給定用戶分類成信用可靠或不可靠。

29.1.2. 隨機森林算法

隨機森林算法通過使用多個帶有隨機選取的數據子集的樹(tree)改善了決策樹的精確性。例如,在基因表達層面上考察大量與乳腺癌復發相關的基因,並計算出復發風險。

29.1.3. 邏輯迴歸

迴歸可以勾畫出因變量與一個或多個因變量之間的狀態關係。例如,將垃圾郵件和非垃圾郵件進行區分。

29.1.4. SVM

29.1.5. 樸素貝葉斯

樸素貝葉斯分類器用於計算可能條件的分支概率。每個獨立的特徵都是「樸素」或條件獨立的,因此它們不會影響別的對象。 例如,在一個裝有共 5 個黃色和紅色小球的罐子裏,連續拿到兩個黃色小球的概率是多少?從圖中最上方分支可見,前後抓取兩個黃色小球的概率爲 1/10。樸素貝葉斯分類器可以計算多個特徵的聯合條件概率。

29.1.6. K 最近鄰算法

29.1.7. K 均值算法

29.1.8. Adaboost 算法

29.1.9. 神經網絡

在任意神經網絡中,每個神經元都通過 1 個或多個隱藏層來將很多輸入轉換成單個輸出。循環神經網絡(RNN)會將值進一步逐層傳遞,讓逐層學習成爲可能。換句話說,RNN 存在某種形式的記憶,允許先前的輸出去影響後面的輸入。

29.1.10. 馬爾可夫

顯馬爾可夫過程是完全確定性的——一個給定的狀態經常會伴隨另一個狀態。交通信號燈就是一個例子。相反,隱馬爾可夫模型通過分析可見數據來計算隱藏狀態的發生。隨後,藉助隱藏狀態分析,隱馬爾可夫模型可以估計可能的未來觀察模式。例如,高或低氣壓的概率(這是隱藏狀態)可用於預測晴天、雨天、多雲天的概率。

30. 雲計算

30.1.1. SaaS

SaaS 是 Software-as-a-Service(軟件即服務)

30.1.2. PaaS

雲計算時代把服務器平臺或者開發環境作爲服務進行提供就成爲了 PaaS(Platform as a Service,平臺即服務)。

30.1.3. IaaS

IaaS(Infrastructure as a Service),即基礎設施即服務。
提供給消費者的服務是對所有設施的利用,包括處理、存儲、網絡和其它基本的計算資源,用戶能夠部署和運行任意軟件,包括操作系統和應用程序。

30.1.4. Docker

通過 Docker 我們可以將程序運行的環境也納入到版本控制中,排除因爲環境造成開發環境和生產環境環境不同運行結果的可能。

docker 創建新進程時傳入 CLONE_NEWPID 實現的進程隔離,也就是使用 Linux 的命名空間實現 進程的隔離,Docker 容器內部的任意進程都對宿主機器的進程一無所知。
Docker 整個網絡部分的功能都是通過 Docker 拆分出來的 libnetwork 實現的,它提供了一個連接不同容器的實現,同時也能夠爲應用給出一個能夠提供一致的編程接口和網絡層抽象的容器網絡模型。
Control Groups(簡稱 CGroups)能夠隔離宿主機器上的物理資源,例如 CPU、內存、磁盤 I/O 和網 絡帶寬。

當鏡像被 docker run 命令創建時就會在鏡像的最上層添加一個可寫的層,也就是容器層。容器和鏡像的區別就在於,所有的鏡像都是隻讀的,而每一個容器其實等於鏡像加上一個可讀寫的層。

30.1.5. Openstack和Kubernetes

OpenStack作爲一個開源的雲計算平臺,利用虛擬化技術和底層存儲服務,提供了可擴展,靈活,適應性強的雲計算服務。
簡單的說,虛擬化使得在一臺物理的服務器上可以跑多臺虛擬機,虛擬機共享物理機的CPU、內存、IO硬件資源,但邏輯上虛擬機之間是相互隔離的。宿主機一般使用hypervisor程序實現硬件資源虛擬化,並提供給客戶機使用。

Kubernetes簡稱K8s,是容器管理編排引擎,那麼底層實現自然是容器技術。它支持自動化部署、大規模可伸縮、應用容器化管理。在Kubernetes中,我們可以創建多個容器,每個容器裏面運行一個應用實例,然後通過內置的負載均衡策略,實現對這一組應用實例的管理、發現、訪問,而這些細節都不需要運維人員去進行復雜的手工配置和處理。
容器是一種輕量級、可移植、自包含的軟件打包技術,打包的應用程序可以在幾乎任何地方以相同的方式運行。以容器典型代表docker爲例,docker起源於2013年3月,是基於LXC爲基礎構建的容器引擎,通過namespace和cgourp實現了資源隔離和調配,使用分層存儲來構建鏡像。它基於Google公司推出的Go語言實現。docker相比KVM虛擬化技術最明顯的特點就是啓動快,資源佔用小。虛擬化啓動虛擬機是分鐘級別的,而docker是秒級別的。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章