Jenkin使用Publish over SSH將檔案上傳到其他伺服器
1. 目錄
1. 目錄.........................................................................................................................................................1
2. JVM.......................................................................................................................................................19
2.1. 執行緒 ...................................................................................................................................................... 20
2.2. JVM 記憶體區域 ..................................................................................................................................... 21
2.2.1. 程式計數器(執行緒私有)................................................................................................................ 22
2.2.2. 虛擬機器棧(執行緒私有).................................................................................................................... 22
2.2.3. 本地方法區(執行緒私有)................................................................................................................ 23
2.2.4. 堆(Heap-執行緒共享) -執行時資料區 ...................................................................................... 23
2.2.5. 方法區/永久代(執行緒共享) ..................................................................................................... 23
2.3. JVM 執行時記憶體 ................................................................................................................................. 24
2.3.1. 新生代 .......................................................................................................................................... 24
2.3.1.1. Eden 區....................................................................................................................................................24
2.3.1.2. ServivorFrom...........................................................................................................................................24
2.3.1.3. ServivorTo ..............................................................................................................................................24
2.3.1.4. MinorGC 的過程(複製->清空->互換) .......................................................................................24
1: eden、 servicorFrom 複製到 ServicorTo,年齡+1...................................................................................25
2:清空 eden、 servicorFrom.............................................................................................................................25
3: ServicorTo 和 ServicorFrom 互換................................................................................................................25
2.3.2. 老年代 .......................................................................................................................................... 25
2.3.3. 永久代 .......................................................................................................................................... 25
2.3.3.1. JAVA8 與元資料.................................................................................................................................25
2.4. 垃圾回收與演算法 .................................................................................................................................. 26
2.4.1. 如何確定垃圾 .............................................................................................................................. 26
2.4.1.1. 引用計數法...............................................................................................................................................26
2.4.1.2. 可達性分析...............................................................................................................................................26
2.4.2. 標記清除演算法(Mark-Sweep) .............................................................................................. 27
2.4.3. 複製演算法(copying) ................................................................................................................. 27
2.4.4. 標記整理演算法(Mark-Compact).................................................................................................. 28
2.4.5. 分代收集演算法 .............................................................................................................................. 29
2.4.5.1. 新生代與複製演算法 ..............................................................................................................................29
2.4.5.2. 老年代與標記複製演算法 ......................................................................................................................29
2.5. JAVA 四中引用型別 ........................................................................................................................... 30
2.5.1. 強引用 .......................................................................................................................................... 30
2.5.2. 軟引用 .......................................................................................................................................... 30
2.5.3. 弱引用 .......................................................................................................................................... 30
2.5.4. 虛引用 .......................................................................................................................................... 30
2.6. GC 分代收集演算法 VS 分割槽收集演算法................................................................................................ 30
2.6.1. 分代收集演算法 .............................................................................................................................. 30
2.6.1.1. 在新生代-複製演算法.............................................................................................................................30
2.6.1.2. 在老年代-標記整理演算法.....................................................................................................................30
2.6.2. 分割槽收集演算法 .............................................................................................................................. 31
2.7. GC 垃圾收集器 ................................................................................................................................... 31
2.7.1. Serial 垃圾收集器(單執行緒、複製演算法) ................................................................................ 31
2.7.2. ParNew 垃圾收集器(Serial+多執行緒) ................................................................................... 31
2.7.3. Parallel Scavenge 收集器(多執行緒複製演算法、高效) .......................................................... 32
2.7.4. Serial Old 收集器(單執行緒標記整理演算法 ) ........................................................................... 32
2.7.5. Parallel Old 收集器(多執行緒標記整理演算法) ......................................................................... 33
2.7.6. CMS 收集器(多執行緒標記清除演算法) ..................................................................................... 33
2.7.6.1. 初始標記..............................................................................................................................................3313/04/2018 Page 2 of 283
2.7.6.2. 併發標記..............................................................................................................................................34
2.7.6.3. 重新標記..............................................................................................................................................34
2.7.6.4. 併發清除..............................................................................................................................................34
2.7.7. G1 收集器.................................................................................................................................... 34
2.8. JAVA IO/NIO....................................................................................................................................... 34
2.8.1. 阻塞 IO 模型 ................................................................................................................................ 34
2.8.2. 非阻塞 IO 模型 ............................................................................................................................ 35
2.8.3. 多路複用 IO 模型 ........................................................................................................................ 35
2.8.4. 訊號驅動 IO 模型 ........................................................................................................................ 36
2.8.5. 非同步 IO 模型 ................................................................................................................................ 36
2.8.1. JAVA IO 包.................................................................................................................................. 36
2.8.2. JAVA NIO .................................................................................................................................... 37
2.8.2.1. NIO 的緩衝區 .....................................................................................................................................38
2.8.2.2. NIO 的非阻塞 .....................................................................................................................................38
2.8.3. Channel ....................................................................................................................................... 40
2.8.4. Buffer............................................................................................................................................ 40
2.8.5. Selector........................................................................................................................................ 40
2.9. JVM 類載入機制 ................................................................................................................................. 41
2.9.1.1. 載入 ..........................................................................................................................................................41
2.9.1.2. 驗證 ..........................................................................................................................................................41
2.9.1.3. 準備 ..........................................................................................................................................................41
2.9.1.4. 解析 ..........................................................................................................................................................41
2.9.1.5. 符號引用..............................................................................................................................................42
2.9.1.6. 直接引用..............................................................................................................................................42
2.9.1.7. 初始化 ......................................................................................................................................................42
2.9.1.8. 類構造器<client>..............................................................................................................................42
2.9.2. 類載入器 ...................................................................................................................................... 42
2.9.2.1. 啟動類載入器(Bootstrap ClassLoader) .........................................................................................43
2.9.2.2. 擴充套件類載入器(Extension ClassLoader)..........................................................................................43
2.9.2.3. 應用程式類載入器(Application ClassLoader): ..........................................................................43
2.9.3. 雙親委派 ...................................................................................................................................... 43
2.9.4. OSGI(動態模型系統) ............................................................................................................ 44
2.9.4.1. 動態改變構造......................................................................................................................................44
2.9.4.2. 模組化程式設計與熱插拔 ..........................................................................................................................44
3. JAVA 集合............................................................................................................................................45
3.1. 介面繼承關係和實現 .......................................................................................................................... 45
3.2. LIST....................................................................................................................................................... 47
3.2.1. ArrayList(陣列) ....................................................................................................................... 47
3.2.2. Vector(陣列實現、執行緒同步) ............................................................................................... 47
3.2.3. LinkList(連結串列) ......................................................................................................................... 47
3.3. SET ....................................................................................................................................................... 48
3.3.1.1. HashSet(Hash 表) .............................................................................................................................48
3.3.1.2. TreeSet(二叉樹) ................................................................................................................................49
3.3.1.3. LinkHashSet(HashSet+LinkedHashMap) ...................................................................................49
3.4. MAP....................................................................................................................................................... 50
3.4.1. HashMap(陣列+連結串列+紅黑樹) ............................................................................................. 50
3.4.1.1. JAVA7 實現 .............................................................................................................................................50
3.4.1.2. JAVA8 實現 .............................................................................................................................................51
3.4.2. ConcurrentHashMap.................................................................................................................. 51
3.4.2.1. Segment 段..............................................................................................................................................51
3.4.2.2. 執行緒安全(Segment 繼承 ReentrantLock 加鎖) ..............................................................................51
3.4.2.3. 並行度(預設 16) .................................................................................................................................52
3.4.2.4. Java8 實現 (引入了紅黑樹) ..............................................................................................................5213/04/2018 Page 3 of 283
3.4.3. HashTable(執行緒安全) ........................................................................................................... 53
3.4.4. TreeMap(可排序) .................................................................................................................. 53
3.4.5. LinkHashMap(記錄插入順序) .............................................................................................. 53
4. JAVA 多執行緒併發.................................................................................................................................54
4.1.1. JAVA 併發知識庫 ....................................................................................................................... 54
4.1.2. JAVA 執行緒實現/建立方式 .......................................................................................................... 54
4.1.2.1. 繼承 Thread 類........................................................................................................................................54
4.1.2.2. 實現 Runnable 介面。 ............................................................................................................................54
4.1.2.3. ExecutorService、 Callable<Class>、 Future 有返回值執行緒.............................................................55
4.1.2.4. 基於執行緒池的方式...................................................................................................................................56
4.1.3. 4 種執行緒池 ................................................................................................................................... 56
4.1.3.1. newCachedThreadPool.........................................................................................................................57
4.1.3.2. newFixedThreadPool.............................................................................................................................57
4.1.3.3. newScheduledThreadPool....................................................................................................................58
4.1.3.4. newSingleThreadExecutor .................................................................................................................58
4.1.4. 執行緒生命週期(狀態).................................................................................................................... 58
4.1.4.1. 新建狀態( NEW) .................................................................................................................................58
4.1.4.2. 就緒狀態( RUNNABLE): .................................................................................................................59
4.1.4.3. 執行狀態( RUNNING): ....................................................................................................................59
4.1.4.4. 阻塞狀態( BLOCKED): ....................................................................................................................59
等待阻塞( o.wait->等待對列): ......................................................................................................................59
同步阻塞(lock->鎖池) ..........................................................................................................................................59
其他阻塞(sleep/join) ............................................................................................................................................59
4.1.4.5. 執行緒死亡( DEAD) ................................................................................................................................59
正常結束................................................................................................................................................................59
異常結束................................................................................................................................................................59
呼叫 stop...............................................................................................................................................................59
4.1.5. 終止執行緒 4 種方式 ...................................................................................................................... 60
4.1.5.1. 正常執行結束...........................................................................................................................................60
4.1.5.2. 使用退出標誌退出執行緒...........................................................................................................................60
4.1.5.3. Interrupt 方法結束執行緒...........................................................................................................................60
4.1.5.4. stop 方法終止執行緒(執行緒不安全) .......................................................................................................61
4.1.6. sleep 與 wait 區別....................................................................................................................... 61
4.1.7. start 與 run 區別 .......................................................................................................................... 62
4.1.8. JAVA 後臺執行緒 ........................................................................................................................... 62
4.1.9. JAVA 鎖 ....................................................................................................................................... 63
4.1.9.1. 樂觀鎖 ......................................................................................................................................................63
4.1.9.2. 悲觀鎖 ......................................................................................................................................................63
4.1.9.3. 自旋鎖 ......................................................................................................................................................63
自旋鎖的優缺點....................................................................................................................................................63
自旋鎖時間閾值( 1.6 引入了適應性自旋鎖) ..................................................................................................63
自旋鎖的開啟........................................................................................................................................................64
4.1.9.4. Synchronized 同步鎖..............................................................................................................................64
Synchronized 作用範圍.......................................................................................................................................64
Synchronized 核心元件.......................................................................................................................................64
Synchronized 實現...............................................................................................................................................64
4.1.9.5. ReentrantLock.........................................................................................................................................66
Lock 介面的主要方法...........................................................................................................................................66
非公平鎖................................................................................................................................................................66
公平鎖....................................................................................................................................................................67
ReentrantLock 與 synchronized ........................................................................................................................67
ReentrantLock 實現.............................................................................................................................................67
Condition 類和 Object 類鎖方法區別區別 .........................................................................................................68
tryLock 和 lock 和 lockInterruptibly 的區別........................................................................................................68
4.1.9.6. Semaphore 訊號量 .................................................................................................................................68
實現互斥鎖(計數器為 1) .................................................................................................................................68
程式碼實現................................................................................................................................................................68
Semaphore 與 ReentrantLock ...........................................................................................................................69
4.1.9.7. AtomicInteger..........................................................................................................................................6913/04/2018 Page 4 of 283
4.1.9.8. 可重入鎖(遞迴鎖) ...............................................................................................................................69
4.1.9.9. 公平鎖與非公平鎖...................................................................................................................................70
公平鎖(Fair) .....................................................................................................................................................70
非公平鎖(Nonfair) ...........................................................................................................................................70
4.1.9.10. ReadWriteLock 讀寫鎖......................................................................................................................70
讀鎖........................................................................................................................................................................70
寫鎖........................................................................................................................................................................70
4.1.9.11. 共享鎖和獨佔鎖 ..................................................................................................................................70
獨佔鎖....................................................................................................................................................................70
共享鎖....................................................................................................................................................................70
4.1.9.12. 重量級鎖(Mutex Lock) ................................................................................................................71
4.1.9.13. 輕量級鎖..............................................................................................................................................71
鎖升級....................................................................................................................................................................71
4.1.9.14. 偏向鎖..................................................................................................................................................71
4.1.9.15. 分段鎖..................................................................................................................................................71
4.1.9.16. 鎖優化..................................................................................................................................................71
減少鎖持有時間....................................................................................................................................................72
減小鎖粒度............................................................................................................................................................72
鎖分離....................................................................................................................................................................72
鎖粗化....................................................................................................................................................................72
鎖消除....................................................................................................................................................................72
4.1.10. 執行緒基本方法 .............................................................................................................................. 72
4.1.10.1. 執行緒等待(wait) ...............................................................................................................................73
4.1.10.2. 執行緒睡眠(sleep) .............................................................................................................................73
4.1.10.3. 執行緒讓步(yield) ..............................................................................................................................73
4.1.10.4. 執行緒中斷(interrupt) ........................................................................................................................73
4.1.10.5. Join 等待其他執行緒終止......................................................................................................................74
4.1.10.6. 為什麼要用 join()方法? ....................................................................................................................74
4.1.10.7. 執行緒喚醒(notify) .............................................................................................................................74
4.1.10.8. 其他方法: ..........................................................................................................................................74
4.1.11. 執行緒上下文切換 .......................................................................................................................... 75
4.1.11.1. 程序......................................................................................................................................................75
4.1.11.2. 上下文..................................................................................................................................................75
4.1.11.3. 暫存器..................................................................................................................................................75
4.1.11.4. 程式計數器..........................................................................................................................................75
4.1.11.5. PCB-“切換楨” .................................................................................................................................75
4.1.11.6. 上下文切換的活動: ..........................................................................................................................76
4.1.11.7. 引起執行緒上下文切換的原因 ..............................................................................................................76
4.1.12. 同步鎖與死鎖 .............................................................................................................................. 76
4.1.12.1. 同步鎖..................................................................................................................................................76
4.1.12.2. 死鎖......................................................................................................................................................76
4.1.13. 執行緒池原理 .................................................................................................................................. 76
4.1.13.1. 執行緒複用..............................................................................................................................................76
4.1.13.2. 執行緒池的組成......................................................................................................................................76
4.1.13.3. 拒絕策略..............................................................................................................................................78
4.1.13.4. Java 執行緒池工作過程 .........................................................................................................................78
4.1.14. JAVA 阻塞佇列原理.................................................................................................................... 79
4.1.14.1. 阻塞佇列的主要方法 ..........................................................................................................................80
插入操作: ............................................................................................................................................................80
獲取資料操作: ....................................................................................................................................................81
4.1.14.2. Java 中的阻塞佇列 .............................................................................................................................81
4.1.14.3. ArrayBlockingQueue(公平、非公平) .......................................................................................82
4.1.14.4. LinkedBlockingQueue(兩個獨立鎖提高併發) .........................................................................82
4.1.14.5. PriorityBlockingQueue(compareTo 排序實現優先) ..............................................................82
4.1.14.6. DelayQueue(快取失效、定時任務 ) ..........................................................................................82
4.1.14.7. SynchronousQueue(不儲存資料、可用於傳遞資料) ..............................................................83
4.1.14.8. LinkedTransferQueue......................................................................................................................8313/04/2018 Page 5 of 283
4.1.14.9. LinkedBlockingDeque.....................................................................................................................83
4.1.15. CyclicBarrier、 CountDownLatch、 Semaphore 的用法........................................................ 84
4.1.15.1. CountDownLatch(執行緒計數器 ) ................................................................................................84
4.1.15.2. CyclicBarrier(迴環柵欄-等待至 barrier 狀態再全部同時執行) ...............................................84
4.1.15.3. Semaphore(訊號量-控制同時訪問的執行緒個數) .......................................................................85
4.1.16. volatile 關鍵字的作用(變數可見性、禁止重排序) ............................................................. 87
變數可見性............................................................................................................................................................87
禁止重排序............................................................................................................................................................87
比 sychronized 更輕量級的同步鎖.....................................................................................................................87
適用場景................................................................................................................................................................87
4.1.17. 如何在兩個執行緒之間共享資料................................................................................................... 88
將資料抽象成一個類,並將資料的操作作為這個類的方法.............................................................................88
Runnable 物件作為一個類的內部類 ..................................................................................................................89
4.1.18. ThreadLocal 作用(執行緒本地儲存) ........................................................................................ 90
ThreadLocalMap(執行緒的一個屬性) ..............................................................................................................90
使用場景................................................................................................................................................................91
4.1.19. synchronized 和 ReentrantLock 的區別 .................................................................................. 91
4.1.19.1. 兩者的共同點: ..................................................................................................................................91
4.1.19.2. 兩者的不同點: ..................................................................................................................................92
4.1.20. ConcurrentHashMap 併發......................................................................................................... 92
4.1.20.1. 減小鎖粒度..........................................................................................................................................92
4.1.20.2. ConcurrentHashMap 分段鎖..........................................................................................................92
ConcurrentHashMap 是由 Segment 陣列結構和 HashEntry 陣列結構組成.................................................93
4.1.21. Java 中用到的執行緒排程 ............................................................................................................. 93
4.1.21.1. 搶佔式排程: ......................................................................................................................................93
4.1.21.2. 協同式排程: ......................................................................................................................................93
4.1.21.3. JVM 的執行緒排程實現(搶佔式排程) .............................................................................................94
4.1.21.4. 執行緒讓出 cpu 的情況: .....................................................................................................................94
4.1.22. 程序排程演算法 .............................................................................................................................. 94
4.1.22.1. 優先排程演算法 ......................................................................................................................................94
4.1.22.2. 高優先權優先排程演算法 ......................................................................................................................95
4.1.22.3. 基於時間片的輪轉排程演算法 ..............................................................................................................96
4.1.23. 什麼是 CAS(比較並交換-樂觀鎖機制-鎖自旋) .................................................................. 96
4.1.23.1. 概念及特性..........................................................................................................................................96
4.1.23.2. 原子包 java.util.concurrent.atomic(鎖自旋) ...........................................................................97
4.1.23.3. ABA 問題.............................................................................................................................................98
4.1.24. 什麼是 AQS(抽象的佇列同步器) ......................................................................................... 98
Exclusive 獨佔資源-ReentrantLock ...................................................................................................................99
Share 共享資源-Semaphore/CountDownLatch ...............................................................................................99
同步器的實現是 ABS 核心(state 資源狀態計數) .......................................................................................100
ReentrantReadWriteLock 實現獨佔和共享兩種方式.....................................................................................100
5. JAVA 基礎..........................................................................................................................................101
5.1.1. JAVA 異常分類及處理..............................................................................................................101
5.1.1.1. 概念....................................................................................................................................................101
5.1.1.2. 異常分類............................................................................................................................................101
Error.....................................................................................................................................................................101
Exception(RuntimeException、 CheckedException) ...........................................................................101
5.1.1.3. 異常的處理方式 ................................................................................................................................102
遇到問題不進行具體處理,而是繼續拋給呼叫者 (throw,throws) .........................................................102
try catch 捕獲異常針對性處理方式..................................................................................................................102
5.1.1.4. Throw 和 throws 的區別: ............................................................................................................10213/04/2018 Page 6 of 283
位置不同..............................................................................................................................................................102
功能不同: ..........................................................................................................................................................102
5.1.2. JAVA 反射 .................................................................................................................................103
5.1.2.1. 動態語言............................................................................................................................................103
5.1.2.2. 反射機制概念 (執行狀態中知道類所有的屬性和方法) ............................................................103
5.1.2.3. 反射的應用場合 ................................................................................................................................103
編譯時型別和執行時型別..................................................................................................................................103
的編譯時型別無法獲取具體方法......................................................................................................................104
5.1.2.4. Java 反射 API....................................................................................................................................104
反射 API 用來生成 JVM 中的類、介面或則物件的資訊。 ............................................................................104
5.1.2.5. 反射使用步驟(獲取 Class 物件、呼叫物件方法) .....................................................................104
5.1.2.6. 獲取 Class 物件的 3 種方法 ............................................................................................................104
呼叫某個物件的 getClass()方法.......................................................................................................................104
呼叫某個類的 class 屬性來獲取該類對應的 Class 物件................................................................................104
使用 Class 類中的 forName()靜態方法(最安全/效能最好)............................................................................104
5.1.2.7. 建立物件的兩種方法 ........................................................................................................................105
Class 物件的 newInstance().............................................................................................................................105
呼叫 Constructor 物件的 newInstance()..........................................................................................................105
5.1.3. JAVA 註解 .................................................................................................................................106
5.1.3.1. 概念 ........................................................................................................................................................106
5.1.3.2. 4 種標準元註解......................................................................................................................................106
@Target 修飾的物件範圍 .................................................................................................................................106
@Retention 定義 被保留的時間長短...............................................................................................................106
@Documented 描述-javadoc................................................................................................................................106
@Inherited 闡述了某個被標註的型別是被繼承的 ..............................................................................................106
5.1.3.3. 註解處理器.............................................................................................................................................107
5.1.4. JAVA 內部類 .............................................................................................................................109
5.1.4.1. 靜態內部類.............................................................................................................................................109
5.1.4.2. 成員內部類.............................................................................................................................................110
5.1.4.3. 區域性內部類(定義在方法中的類) .....................................................................................................110
5.1.4.4. 匿名內部類(要繼承一個父類或者實現一個介面、直接使用 new 來生成一個物件的引用) .....111
5.1.5. JAVA 泛型 .................................................................................................................................112
5.1.5.1. 泛型方法(<E>) .............................................................................................................................112
5.1.5.2. 泛型類<T> .............................................................................................................................................112
5.1.5.3. 型別萬用字元? ..........................................................................................................................................113
5.1.5.4. 型別擦除 ................................................................................................................................................113
5.1.6. JAVA 序列化(建立可複用的 Java 物件).................................................................................113
儲存(持久化)物件及其狀態到記憶體或者磁碟....................................................................................................113
序列化物件以位元組陣列保持-靜態成員不儲存.................................................................................................113
序列化使用者遠端物件傳輸..................................................................................................................................113
Serializable 實現序列化 ....................................................................................................................................113
ObjectOutputStream 和 ObjectInputStream 對物件進行序列化及反序列化...............................................113
writeObject 和 readObject 自定義序列化策略................................................................................................113
序列化 ID.............................................................................................................................................................113
序列化並不儲存靜態變數..................................................................................................................................114
序列化子父類說明..............................................................................................................................................114
Transient 關鍵字阻止該變數被序列化到檔案中............................................................................................114
5.1.7. JAVA 複製 .................................................................................................................................114
5.1.7.1. 直接賦值複製.........................................................................................................................................114
5.1.7.2. 淺複製(複製引用但不復制引用的物件) .....................................................................................114
5.1.7.3. 深複製(複製物件和其應用物件) .................................................................................................115
5.1.7.4. 序列化(深 clone 一中實現) ........................................................................................................115
6. SPRING 原理 .....................................................................................................................................116
6.1.1. Spring 特點................................................................................................................................116
6.1.1.1. 輕量級................................................................................................................................................11613/04/2018 Page 7 of 283
6.1.1.2. 控制反轉............................................................................................................................................116
6.1.1.3. 面向切面............................................................................................................................................116
6.1.1.4. 容器....................................................................................................................................................116
6.1.1.5. 框架集合............................................................................................................................................116
6.1.2. Spring 核心元件........................................................................................................................117
6.1.3. Spring 常用模組........................................................................................................................117
6.1.4. Spring 主要包............................................................................................................................118
6.1.5. Spring 常用註解........................................................................................................................118
6.1.6. Spring 第三方結合....................................................................................................................119
6.1.7. Spring IOC 原理........................................................................................................................120
6.1.7.1. 概念 ........................................................................................................................................................120
6.1.7.2. Spring 容器高層檢視 ............................................................................................................................120
6.1.7.3. IOC 容器實現.........................................................................................................................................120
BeanFactory-框架基礎設施..............................................................................................................................120
1.1..1.1.1 BeanDefinitionRegistry 登錄檔.................................................................................................121
1.1..1.1.2 BeanFactory 頂層介面..............................................................................................................121
1.1..1.1.3 ListableBeanFactory .................................................................................................................121
1.1..1.1.4 HierarchicalBeanFactory 父子級聯..........................................................................................121
1.1..1.1.5 ConfigurableBeanFactory.........................................................................................................121
1.1..1.1.6 AutowireCapableBeanFactory 自動裝配 ................................................................................122
1.1..1.1.7 SingletonBeanRegistry 執行期間註冊單例 Bean...................................................................122
1.1..1.1.8 依賴日誌框框.............................................................................................................................122
ApplicationContext 面向開發應用....................................................................................................................122
WebApplication 體系架構 .................................................................................................................................123
6.1.7.4. Spring Bean 作用域..............................................................................................................................123
singleton:單例模式(多執行緒下不安全) ......................................................................................................123
prototype:原型模式每次使用時建立 ................................................................................................................124
Request:一次 request 一個例項....................................................................................................................124
session................................................................................................................................................................124
global Session....................................................................................................................................................124
6.1.7.5. Spring Bean 生命週期..........................................................................................................................124
例項化..................................................................................................................................................................124
IOC 依賴注入......................................................................................................................................................124
setBeanName 實現............................................................................................................................................124
BeanFactoryAware 實現...................................................................................................................................124
ApplicationContextAware 實現.........................................................................................................................125
postProcessBeforeInitialization 介面實現-初始化預處理..........................................................................125
init-method..........................................................................................................................................................125
postProcessAfterInitialization...........................................................................................................................125
Destroy 過期自動清理階段 ...............................................................................................................................125
destroy-method 自配置清理 .............................................................................................................................125
6.1.7.6. Spring 依賴注入四種方式....................................................................................................................126
構造器注入..........................................................................................................................................................126
setter 方法注入...................................................................................................................................................127
靜態工廠注入......................................................................................................................................................127
例項工廠..............................................................................................................................................................127
6.1.7.7. 5 種不同方式的自動裝配......................................................................................................................128
6.1.8. Spring APO 原理 ......................................................................................................................129
6.1.8.1. 概念 ........................................................................................................................................................129
6.1.8.2. AOP 核心概念 .......................................................................................................................................129
6.1.8.1. AOP 兩種代理方式 ...............................................................................................................................130
JDK 動態介面代理 .............................................................................................................................................130
CGLib 動態代理..................................................................................................................................................131
6.1.8.2. 實現原理 ................................................................................................................................................131
6.1.9. Spring MVC 原理......................................................................................................................132
6.1.9.1. MVC 流程...............................................................................................................................................132
Http 請求到 DispatcherServlet .......................................................................................................................133
HandlerMapping 尋找處理器............................................................................................................................133
呼叫處理器 Controller........................................................................................................................................13313/04/2018 Page 8 of 283
Controller 呼叫業務邏輯處理後,返回 ModelAndView.................................................................................133
DispatcherServlet 查詢 ModelAndView ..........................................................................................................133
ModelAndView 反饋瀏覽器 HTTP ...................................................................................................................133
6.1.9.1. MVC 常用註解.......................................................................................................................................133
6.1.10. Spring Boot 原理.......................................................................................................................134
1. 建立獨立的 Spring 應用程式.............................................................................................................................134
2. 嵌入的 Tomcat,無需部署 WAR 檔案.............................................................................................................134
3. 簡化 Maven 配置................................................................................................................................................134
4. 自動配置 Spring .................................................................................................................................................134
5. 提供生產就緒型功能,如指標,健康檢查和外部配置...................................................................................134
6. 絕對沒有程式碼生成和對 XML 沒有要求配置 [1]...............................................................................................134
6.1.11. JPA 原理....................................................................................................................................134
6.1.11.1. 事務....................................................................................................................................................134
6.1.11.2. 本地事務............................................................................................................................................134
6.1.11.1. 分散式事務........................................................................................................................................135
6.1.11.1. 兩階段提交........................................................................................................................................136
1 準備階段...........................................................................................................................................................136
2 提交階段: .......................................................................................................................................................136
6.1.12. Mybatis 快取..............................................................................................................................137
6.1.12.1. Mybatis 的一級快取原理(sqlsession 級別) ..............................................................................138
6.1.12.2. 二級快取原理(mapper 基本) .....................................................................................................138
具體使用需要配置: ..........................................................................................................................................139
6.1.13. Tomcat 架構..............................................................................................................................139
7. 微服務 .................................................................................................................................................140
7.1.1. 服務註冊發現 ............................................................................................................................140
7.1.1.1. 客戶端註冊(zookeeper) ..................................................................................................................140
7.1.1.2. 第三方註冊(獨立的服務 Registrar) ...............................................................................................140
7.1.1.3. 客戶端發現.............................................................................................................................................141
7.1.1.4. 服務端發現.............................................................................................................................................142
7.1.1.5. Consul....................................................................................................................................................142
7.1.1.6. Eureka....................................................................................................................................................142
7.1.1.7. SmartStack............................................................................................................................................142
7.1.1.8. Etcd ........................................................................................................................................................142
7.1.2. API 閘道器.....................................................................................................................................142
7.1.2.1. 請求轉發 ................................................................................................................................................143
7.1.2.2. 響應合併 ................................................................................................................................................143
7.1.2.3. 協議轉換 ................................................................................................................................................143
7.1.2.4. 資料轉換 ................................................................................................................................................143
7.1.2.5. 安全認證 ................................................................................................................................................144
7.1.3. 配置中心 ....................................................................................................................................144
7.1.3.1. zookeeper 配置中心.............................................................................................................................144
7.1.3.2. 配置中心資料分類.................................................................................................................................144
7.1.4. 事件排程(kafka) ...................................................................................................................144
7.1.5. 服務跟蹤(starter-sleuth) ...................................................................................................144
7.1.6. 服務熔斷(Hystrix) ................................................................................................................145
7.1.6.1. Hystrix 斷路器機制................................................................................................................................146
7.1.7. API 管理.....................................................................................................................................146
8. NETTY 與 RPC ..................................................................................................................................147
8.1.1. Netty 原理..................................................................................................................................147
8.1.2. Netty 高效能..............................................................................................................................147
8.1.2.1. 多路複用通訊方式 ............................................................................................................................147
8.1.2.1. 非同步通訊 NIO....................................................................................................................................148
8.1.2.2. 零拷貝(DIRECT BUFFERS 使用堆外直接記憶體) ..........................................................................149
8.1.2.3. 記憶體池(基於記憶體池的緩衝區重用機制) .........................................................................................149
8.1.2.4. 高效的 Reactor 執行緒模型.....................................................................................................................149
Reactor 單執行緒模型...........................................................................................................................................149
Reactor 多執行緒模型...........................................................................................................................................15013/04/2018 Page 9 of 283
主從 Reactor 多執行緒模型..................................................................................................................................150
8.1.2.5. 無鎖設計、執行緒繫結.............................................................................................................................151
8.1.2.6. 高效能的序列化框架.............................................................................................................................151
小包封大包,防止網路阻塞..............................................................................................................................152
軟中斷 Hash 值和 CPU 繫結.............................................................................................................................152
8.1.3. Netty RPC 實現.........................................................................................................................152
8.1.3.1. 概念 ........................................................................................................................................................152
8.1.3.2. 關鍵技術 ................................................................................................................................................152
8.1.3.3. 核心流程 ................................................................................................................................................152
8.1.3.1. 訊息編解碼.............................................................................................................................................153
息資料結構(介面名稱+方法名+引數型別和引數值+超時時間+ requestID) ...........................................153
序列化..................................................................................................................................................................154
8.1.3.1. 通訊過程 ................................................................................................................................................154
核心問題(執行緒暫停、訊息亂序) .......................................................................................................................154
通訊流程..............................................................................................................................................................154
requestID 生成-AtomicLong .............................................................................................................................154
存放回調物件 callback 到全域性 ConcurrentHashMap ....................................................................................154
synchronized 獲取回撥物件 callback 的鎖並自旋 wait..................................................................................154
監聽訊息的執行緒收到訊息,找到 callback 上的鎖並喚醒 ..............................................................................155
8.1.4. RMI 實現方式............................................................................................................................155
8.1.4.1. 實現步驟 ................................................................................................................................................155
8.1.5. Protoclol Buffer .........................................................................................................................156
8.1.5.1. 特點 ........................................................................................................................................................157
8.1.6. Thrift ...........................................................................................................................................157
9. 網路.....................................................................................................................................................159
9.1.1. 網路 7 層架構 ............................................................................................................................159
9.1.2. TCP/IP 原理...............................................................................................................................160
9.1.2.1. 網路訪問層(Network Access Layer)...................................................................................................160
9.1.2.2. 網路層(Internet Layer) .........................................................................................................................160
9.1.2.3. 傳輸層(Tramsport Layer-TCP/UDP) ..................................................................................................160
9.1.2.4. 應用層(Application Layer)....................................................................................................................160
9.1.3. TCP 三次握手/四次揮手 ..........................................................................................................161
9.1.3.1. 資料包說明.............................................................................................................................................161
9.1.3.2. 三次握手 ................................................................................................................................................162
9.1.3.3. 四次揮手 ................................................................................................................................................163
9.1.4. HTTP 原理.................................................................................................................................164
9.1.4.1. 傳輸流程 ................................................................................................................................................164
1:地址解析 .......................................................................................................................................................164
2:封裝 HTTP 請求資料包 ...............................................................................................................................165
3:封裝成 TCP 包並建立連線..........................................................................................................................165
4:客戶機發送請求命........................................................................................................................................165
5:伺服器響應....................................................................................................................................................165
6:伺服器關閉 TCP 連線..................................................................................................................................165
9.1.4.2. HTTP 狀態 .............................................................................................................................................165
9.1.4.3. HTTPS ...................................................................................................................................................166
建立連接獲取證書..............................................................................................................................................167
證書驗證..............................................................................................................................................................167
資料加密和傳輸..................................................................................................................................................167
9.1.5. CDN 原理...................................................................................................................................167
9.1.5.1. 分發服務系統.........................................................................................................................................167
9.1.5.2. 負載均衡系統: .....................................................................................................................................168
9.1.5.3. 管理系統: .............................................................................................................................................168
10. 日誌 .................................................................................................................................................169
10.1.1. Slf4j ............................................................................................................................................169
10.1.2. Log4j ..........................................................................................................................................169
10.1.3. LogBack.....................................................................................................................................169
10.1.3.1. Logback 優點...................................................................................................................................169
10.1.4. ELK.............................................................................................................................................17013/04/2018 Page 10 of 283
11. ZOOKEEPER .................................................................................................................................171
11.1.1. Zookeeper 概念 ........................................................................................................................171
11.1.1. Zookeeper 角色 ........................................................................................................................171
11.1.1.1. Leader ...............................................................................................................................................171
11.1.1.2. Follower............................................................................................................................................171
11.1.1.3. Observer...........................................................................................................................................171
11.1.1.1. ZAB 協議 ...........................................................................................................................................172
事務編號 Zxid(事務請求計數器+ epoch) ...................................................................................................172
epoch...................................................................................................................................................................172
Zab 協議有兩種模式-恢復模式(選主)、廣播模式(同步) ......................................................................172
ZAB 協議 4 階段.................................................................................................................................................172
Leader election(選舉階段-選出準 Leader) ................................................................................................172
Discovery(發現階段-接受提議、生成 epoch、接受 epoch) ....................................................................173
Synchronization(同步階段-同步 follower 副本) .........................................................................................173
Broadcast(廣播階段-leader 訊息廣播) .......................................................................................................173
ZAB 協議 JAVA 實現(FLE-發現階段和同步合併為 Recovery Phase(恢復階段) ) ............................173
11.1.1.2. 投票機制............................................................................................................................................173
11.1.2. Zookeeper 工作原理(原子廣播) .........................................................................................174
11.1.3. Znode 有四種形式的目錄節點 ................................................................................................174
12. KAFKA............................................................................................................................................175
12.1.1. Kafka 概念 .................................................................................................................................175
12.1.2. Kafka 資料儲存設計 .................................................................................................................175
12.1.2.1. partition 的資料檔案(offset, MessageSize, data) ............................................................175
12.1.2.2. 資料檔案分段 segment(順序讀寫、分段命令、二分查詢) ....................................................176
12.1.2.3. 資料檔案索引(分段索引、稀疏儲存) .........................................................................................176
12.1.3. 生產者設計 ................................................................................................................................176
12.1.3.1. 負載均衡(partition 會均衡分佈到不同 broker 上) .................................................................176
12.1.3.2. 批量傳送............................................................................................................................................177
12.1.3.3. 壓縮(GZIP 或 Snappy) ...............................................................................................................177
12.1.1. 消費者設計 ................................................................................................................................177
12.1.1.1. Consumer Group ...........................................................................................................................178
13. RABBITMQ ....................................................................................................................................179
13.1.1. 概念 ............................................................................................................................................179
13.1.2. RabbitMQ 架構 .........................................................................................................................179
13.1.2.1. Message ...........................................................................................................................................180
13.1.2.2. Publisher ..........................................................................................................................................180
13.1.2.3. Exchange(將訊息路由給佇列 ) .................................................................................................180
13.1.2.4. Binding(訊息佇列和交換器之間的關聯) ..................................................................................180
13.1.2.5. Queue ...............................................................................................................................................180
13.1.2.6. Connection ......................................................................................................................................180
13.1.2.7. Channel ............................................................................................................................................180
13.1.2.8. Consumer.........................................................................................................................................180
13.1.2.9. Virtual Host .....................................................................................................................................180
13.1.2.10. Broker...............................................................................................................................................181
13.1.3. Exchange 型別 .........................................................................................................................181
13.1.3.1. Direct 鍵(routing key)分佈: ..................................................................................................181
13.1.3.2. Fanout(廣播分發) .......................................................................................................................181
13.1.3.3. topic 交換器(模式匹配) ...................................................................................................18213/04/2018 Page 11 of 283
14. HBASE............................................................................................................................................183
14.1.1. 概念 ............................................................................................................................................183
14.1.2. 列式儲存 ....................................................................................................................................183
14.1.3. Hbase 核心概念........................................................................................................................184
14.1.3.1. Column Family 列族.......................................................................................................................184
14.1.3.2. Rowkey(Rowkey 查詢, Rowkey 範圍掃描,全表掃描) .......................................................184
14.1.3.3. Region 分割槽......................................................................................................................................184
14.1.3.4. TimeStamp 多版本..........................................................................................................................184
14.1.4. Hbase 核心架構........................................................................................................................184
14.1.4.1. Client: .............................................................................................................................................185
14.1.4.2. Zookeeper: ....................................................................................................................................185
14.1.4.3. Hmaster............................................................................................................................................185
14.1.4.4. HregionServer.................................................................................................................................185
14.1.4.5. Region 定址方式(通過 zookeeper .META) ............................................................................186
14.1.4.6. HDFS .................................................................................................................................................186
14.1.5. Hbase 的寫邏輯........................................................................................................................187
14.1.5.1. Hbase 的寫入流程 ...........................................................................................................................187
獲取 RegionServer ............................................................................................................................................187
請求寫 Hlog ........................................................................................................................................................187
請求寫 MemStore ..............................................................................................................................................187
14.1.5.2. MemStore 刷盤...............................................................................................................................187
全域性記憶體控制......................................................................................................................................................188
MemStore 達到上限...........................................................................................................................................188
RegionServer 的 Hlog 數量達到上限...............................................................................................................188
手工觸發..............................................................................................................................................................188
關閉 RegionServer 觸發....................................................................................................................................188
Region 使用 HLOG 恢復完資料後觸發............................................................................................................188
14.1.6. HBase vs Cassandra...............................................................................................................188
15. MONGODB.....................................................................................................................................190
15.1.1. 概念 ............................................................................................................................................190
15.1.2. 特點 ............................................................................................................................................190
16. CASSANDRA.................................................................................................................................192
16.1.1. 概念 ............................................................................................................................................192
16.1.2. 資料模型 ....................................................................................................................................192
Key Space(對應 SQL 資料庫中的 database) ................................................................................................192
Key(對應 SQL 資料庫中的主鍵) ......................................................................................................................192
column(對應 SQL 資料庫中的列) ....................................................................................................................192
super column(SQL 資料庫不支援) ..................................................................................................................192
Standard Column Family(相對應 SQL 資料庫中的 table) ............................................................................192
Super Column Family(SQL 資料庫不支援) ...................................................................................................192
16.1.3. Cassandra 一致 Hash 和虛擬節點 .........................................................................................192
一致性 Hash(多米諾 down 機) .........................................................................................................................192
虛擬節點(down 機多節點託管) ........................................................................................................................193
16.1.4. Gossip 協議...............................................................................................................................193
Gossip 節點的通訊方式及收斂性 .........................................................................................................................194
Gossip 兩個節點(A、 B)之間存在三種通訊方式(push、 pull、 push&pull) ........................................194
gossip 的協議和 seed list(防止叢集分列) ..................................................................................................194
16.1.5. 資料複製 ....................................................................................................................................194
Partitioners(計算 primary key token 的 hash 函式) .......................................................................................194
兩種可用的複製策略: ..........................................................................................................................................194
SimpleStrategy:僅用於單資料中心, ...........................................................................................................194
將第一個 replica 放在由 partitioner 確定的節點中,其餘的 replicas 放在上述節點順時針方向的後續節
點中。 ..................................................................................................................................................................19413/04/2018 Page 12 of 283
NetworkTopologyStrategy:可用於較複雜的多資料中心。 .........................................................................194
可以指定在每個資料中心分別儲存多少份 replicas。 ...................................................................................194
16.1.6. 資料寫請求和協調者 ................................................................................................................195
協調者(coordinator)................................................................................................................................................195
16.1.7. 資料讀請求和後臺修復 ............................................................................................................195
16.1.8. 資料儲存(CommitLog、 MemTable、 SSTable) ...........................................................196
SSTable 檔案構成( BloomFilter、 index、 data、 static) ................................................................................196
16.1.9. 二級索引(對要索引的 value 摘要,生成 RowKey) ..........................................................196
16.1.10. 資料讀寫 ................................................................................................................................197
資料寫入和更新(資料追加) ..............................................................................................................................197
資料的寫和刪除效率極高..................................................................................................................................197
錯誤恢復簡單......................................................................................................................................................197
讀的複雜度高......................................................................................................................................................197
資料刪除( column 的墓碑) ................................................................................................................................197
墓碑......................................................................................................................................................................198
垃圾回收 compaction ........................................................................................................................................198
資料讀取(memtable+SStables) ................................................................................................................198
行快取和鍵快取請求流程圖 ..................................................................................................................................199
Row Cache( SSTables 中頻繁被訪問的資料) ............................................................................................199
Bloom Filter(查詢資料可能對應的 SSTable) .............................................................................................200
Partition Key Cache(查詢資料可能對應的 Partition key) ........................................................................200
Partition Summary(記憶體中儲存一些 partition index 的樣本) ...................................................................200
Partition Index(磁碟中) ................................................................................................................................200
Compression offset map(磁碟中) ...............................................................................................................200
17. 設計模式..........................................................................................................................................201
17.1.1. 設計原則 ....................................................................................................................................201
17.1.2. 工廠方法模式 ............................................................................................................................201
17.1.3. 抽象工廠模式 ............................................................................................................................201
17.1.4. 單例模式 ....................................................................................................................................201
17.1.5. 建造者模式 ................................................................................................................................201
17.1.6. 原型模式 ....................................................................................................................................201
17.1.7. 介面卡模式 ................................................................................................................................201
17.1.8. 裝飾器模式 ................................................................................................................................201
17.1.9. 代理模式 ....................................................................................................................................201
17.1.10. 外觀模式 ................................................................................................................................201
17.1.11. 橋接模式 ................................................................................................................................201
17.1.12. 組合模式 ................................................................................................................................201
17.1.13. 享元模式 ................................................................................................................................201
17.1.14. 策略模式 ................................................................................................................................201
17.1.15. 模板方法模式 ........................................................................................................................201
17.1.16. 觀察者模式 ............................................................................................................................201
17.1.17. 迭代子模式 ............................................................................................................................201
17.1.18. 責任鏈模式 ............................................................................................................................201
17.1.19. 命令模式 ................................................................................................................................201
17.1.20. 備忘錄模式 ............................................................................................................................201
17.1.21. 狀態模式 ................................................................................................................................202
17.1.22. 訪問者模式 ............................................................................................................................202
17.1.23. 中介者模式 ............................................................................................................................202
17.1.24. 直譯器模式 ............................................................................................................................202
18. 負載均衡..........................................................................................................................................203
18.1.1. 四層負載均衡 vs 七層負載均衡 ..............................................................................................203
18.1.1.1. 四層負載均衡(目標地址和埠交換) .........................................................................................203
F5:硬體負載均衡器,功能很好,但是成本很高。 ......................................................................................203
lvs:重量級的四層負載軟體。 .........................................................................................................................203
nginx:輕量級的四層負載軟體,帶快取功能,正則表示式較靈活。 .........................................................20313/04/2018 Page 13 of 283
haproxy:模擬四層轉發,較靈活。 ................................................................................................................203
18.1.1.2. 七層負載均衡(內容交換) ............................................................................................................203
haproxy:天生負載均衡技能,全面支援七層代理,會話保持,標記,路徑轉移; .................................204
nginx:只在 http 協議和 mail 協議上功能比較好,效能與 haproxy 差不多; ............................................204
apache:功能較差.............................................................................................................................................204
Mysql proxy:功能尚可。 .................................................................................................................................204
18.1.2. 負載均衡演算法/策略 ...................................................................................................................204
18.1.2.1. 輪循均衡(Round Robin) ...........................................................................................................204
18.1.2.2. 權重輪循均衡(Weighted Round Robin) ................................................................................204
18.1.2.3. 隨機均衡(Random) ....................................................................................................................204
18.1.2.4. 權重隨機均衡(Weighted Random) .........................................................................................204
18.1.2.5. 響應速度均衡(Response Time 探測時間) ...............................................................................204
18.1.2.6. 最少連線數均衡(Least Connection) ........................................................................................205
18.1.2.7. 處理能力均衡(CPU、記憶體) ........................................................................................................205
18.1.2.8. DNS 響應均衡(Flash DNS) .......................................................................................................205
18.1.2.9. 雜湊演算法............................................................................................................................................205
18.1.2.10. IP 地址雜湊(保證客戶端伺服器對應關係穩定) ........................................................................205
18.1.2.11. URL 雜湊...........................................................................................................................................205
18.1.3. LVS.............................................................................................................................................206
18.1.3.1. LVS 原理............................................................................................................................................206
IPVS ....................................................................................................................................................................206
18.1.3.1. LVS NAT 模式 ..................................................................................................................................207
18.1.3.2. LVS DR 模式(區域網改寫 mac 地址) ........................................................................................208
18.1.3.3. LVS TUN 模式( IP 封裝、跨網段) ..............................................................................................209
18.1.3.4. LVS FULLNAT 模式.........................................................................................................................210
18.1.4. Keepalive...................................................................................................................................211
18.1.5. Nginx 反向代理負載均衡 .........................................................................................................211
18.1.5.1. upstream_module 和健康檢測........................................................................................................212
18.1.5.1. proxy_pass 請求轉發.......................................................................................................................212
18.1.6. HAProxy ....................................................................................................................................213
19. 資料庫 .............................................................................................................................................214
19.1.1. 儲存引擎 ....................................................................................................................................214
19.1.1.1. 概念....................................................................................................................................................214
19.1.1.2. InnoDB(B+樹) .............................................................................................................................214
19.1.1.3. TokuDB(Fractal Tree-節點帶資料) ..........................................................................................215
19.1.1.4. MyIASM.............................................................................................................................................215
19.1.1.5. Memory..............................................................................................................................................215
19.1.2. 索引 ............................................................................................................................................215
19.1.2.1. 常見索引原則有 ................................................................................................................................216
1.選擇唯一性索引 ..............................................................................................................................................216
2.為經常需要排序、分組和聯合操作的欄位建立索引: ...............................................................................216
3.為常作為查詢條件的欄位建立索引。 ........................................................................................................216
4.限制索引的數目: ........................................................................................................................................216
儘量使用資料量少的索引..................................................................................................................................216
儘量使用字首來索引..........................................................................................................................................216
7.刪除不再使用或者很少使用的索引 ............................................................................................................216
8 . 最左字首匹配原則,非常重要的原則。 .....................................................................................................216
10 . 儘量選擇區分度高的列作為索引 ..............................................................................................................216
11 .索引列不能參與計算,保持列“乾淨”:帶函式的查詢不參與索引。 ................................................216
12 .儘量的擴充套件索引,不要新建索引。 ............................................................................................................216
19.1.3. 資料庫三正規化 ............................................................................................................................216
19.1.3.1. 第一正規化(1st NF -列都是不可再分).............................................................................................216
19.1.3.2. 第二正規化(2nd NF-每個表只描述一件事情).................................................................................216
19.1.3.3. 第三正規化(3rd NF- 不存在對非主鍵列的傳遞依賴).....................................................................217
19.1.4. 資料庫是事務 ............................................................................................................................21713/04/2018 Page 14 of 283
原子性(Atomicity) ..........................................................................................................................................217
一致性(Consistency) ....................................................................................................................................217
隔離性(Isolation) ...........................................................................................................................................218
永久性(Durability) .........................................................................................................................................218
19.1.5. 儲存過程(特定功能的 SQL 語句集)........................................................................................218
儲存過程優化思路: ..............................................................................................................................................218
19.1.6. 觸發器(一段能自動執行的程式)..............................................................................................218
19.1.7. 資料庫併發策略 ........................................................................................................................218
19.1.7.1. 樂觀鎖................................................................................................................................................218
19.1.7.2. 悲觀鎖................................................................................................................................................219
19.1.7.3. 時間戳................................................................................................................................................219
19.1.8. 資料庫鎖 ....................................................................................................................................219
19.1.8.1. 行級鎖................................................................................................................................................219
19.1.8.2. 表級鎖................................................................................................................................................219
19.1.8.1. 頁級鎖................................................................................................................................................219
19.1.9. 基於 Redis 分散式鎖 ................................................................................................................219
19.1.10. 分割槽分表 ................................................................................................................................220
垂直切分(按照功能模組) ...................................................................................................................................220
水平切分(按照規則劃分儲存) ...........................................................................................................................220
19.1.11. 兩階段提交協議 ....................................................................................................................220
19.1.11.1. 準備階段............................................................................................................................................221
19.1.11.2. 提交階段............................................................................................................................................221
19.1.11.3. 缺點....................................................................................................................................................221
同步阻塞問題......................................................................................................................................................221
單點故障..............................................................................................................................................................221
資料不一致(腦裂問題) ..................................................................................................................................221
二階段無法解決的問題(資料狀態不確定) ..................................................................................................221
19.1.12. 三階段提交協議 ....................................................................................................................222
19.1.12.1. CanCommit 階段 ..............................................................................................................................222
19.1.12.2. PreCommit 階段 ...............................................................................................................................222
19.1.12.3. doCommit 階段.................................................................................................................................222
19.1.13. 柔性事務 ................................................................................................................................222
19.1.13.1. 柔性事務............................................................................................................................................222
兩階段型..............................................................................................................................................................222
補償型..................................................................................................................................................................222
非同步確保型..........................................................................................................................................................223
最大努力通知型(多次嘗試) ..........................................................................................................................223
19.1.14. CAP ........................................................................................................................................224
一致性(C): .......................................................................................................................................................224
可用性(A): .......................................................................................................................................................224
分割槽容忍性(P) : ................................................................................................................................................224
20. 一致性演算法......................................................................................................................................225
20.1.1. Paxos .........................................................................................................................................225
Paxos 三種角色: Proposer, Acceptor, Learners ...........................................................................................225
Proposer: .........................................................................................................................................................225
Acceptor: ..........................................................................................................................................................225
Learner: ............................................................................................................................................................225
Paxos 演算法分為兩個階段。具體如下: ...............................................................................................................225
階段一(準 leader 確定 ): ............................................................................................................................225
階段二(leader 確認): ..................................................................................................................................225
20.1.2. Zab .............................................................................................................................................225
1.崩潰恢復:主要就是 Leader 選舉過程.........................................................................................................226
2.資料同步: Leader 伺服器與其他伺服器進行資料同步..............................................................................226
3.訊息廣播: Leader 伺服器將資料傳送給其他伺服器..................................................................................226
20.1.3. Raft.............................................................................................................................................226
20.1.3.1. 角色....................................................................................................................................................226
Leader(領導者-日誌管理) ............................................................................................................................226
Follower(追隨者-日誌同步) ..........................................................................................................................226
Candidate(候選者-負責選票) .......................................................................................................................22613/04/2018 Page 15 of 283
20.1.3.2. Term(任期) ...................................................................................................................................226
20.1.3.3. 選舉( Election) ..............................................................................................................................227
選舉定時器..........................................................................................................................................................227
20.1.3.4. 安全性( Safety) .............................................................................................................................227
20.1.3.5. raft 協議和 zab 協議區別 .................................................................................................................227
20.1.4. NWR...........................................................................................................................................228
N:在分散式儲存系統中,有多少份備份資料................................................................................................228
W:代表一次成功的更新操作要求至少有 w 份資料寫入成功 ......................................................................228
R: 代表一次成功的讀資料操作要求至少有 R 份資料成功讀取..................................................................228
20.1.5. Gossip........................................................................................................................................228
20.1.6. 一致性 Hash..............................................................................................................................229
20.1.6.1. 一致性 Hash 特性.............................................................................................................................229
20.1.6.2. 一致性 Hash 原理.............................................................................................................................229
1.建構環形 hash 空間: ....................................................................................................................................229
2.把需要快取的內容(物件)對映到 hash 空間..................................................................................................229
3.把伺服器(節點)對映到 hash 空間 .................................................................................................................229
4.把物件對映到服務節點...................................................................................................................................229
考察 cache 的變動.............................................................................................................................................230
虛擬節點..............................................................................................................................................................230
21. JAVA 演算法 ......................................................................................................................................232
21.1.1. 二分查詢 ....................................................................................................................................232
21.1.2. 氣泡排序演算法 ............................................................................................................................232
21.1.3. 插入排序演算法 ............................................................................................................................233
21.1.4. 快速排序演算法 ............................................................................................................................234
21.1.1. 希爾排序演算法 ............................................................................................................................236
21.1.2. 歸併排序演算法 ............................................................................................................................237
21.1.3. 桶排序演算法 ................................................................................................................................240
21.1.4. 基數排序演算法 ............................................................................................................................241
21.1.5. 剪枝演算法 ....................................................................................................................................243
21.1.6. 回溯演算法 ....................................................................................................................................243
21.1.7. 最短路徑演算法 ............................................................................................................................243
21.1.8. 最大子陣列演算法 ........................................................................................................................243
21.1.9. 最長公共子序演算法 ....................................................................................................................243
21.1.10. 最小生成樹演算法 ....................................................................................................................243
22. 資料結構..........................................................................................................................................245
22.1.1. 棧( stack) ...............................................................................................................................245
22.1.2. 佇列( queue) .........................................................................................................................245
22.1.3. 連結串列( Link) .............................................................................................................................245
22.1.4. 散列表( Hash Table) ............................................................................................................246
22.1.5. 排序二叉樹 ................................................................................................................................246
22.1.5.1. 插入操作............................................................................................................................................246
22.1.5.2. 刪除操作............................................................................................................................................247
22.1.5.3. 查詢操作............................................................................................................................................248
22.1.6. 紅黑樹 ........................................................................................................................................248
22.1.6.1. 紅黑樹的特性 ....................................................................................................................................248
22.1.6.1. 左旋....................................................................................................................................................248
22.1.6.1. 右旋....................................................................................................................................................249
22.1.6.1. 新增....................................................................................................................................................250
22.1.6.2. 刪除....................................................................................................................................................251
22.1.7. B-TREE......................................................................................................................................252
22.1.8. 點陣圖 ............................................................................................................................................254
23. 加密演算法..........................................................................................................................................255
23.1.1. AES ............................................................................................................................................255
23.1.2. RSA............................................................................................................................................255
23.1.3. CRC............................................................................................................................................256
23.1.4. MD5............................................................................................................................................25613/04/2018 Page 16 of 283
24. 分散式快取......................................................................................................................................257
24.1.1. 快取雪崩 ....................................................................................................................................257
24.1.2. 快取穿透 ....................................................................................................................................257
24.1.3. 快取預熱 ....................................................................................................................................257
24.1.4. 快取更新 ....................................................................................................................................257
24.1.5. 快取降級 ....................................................................................................................................257
25. HADOOP ........................................................................................................................................259
25.1.1. 概念 ............................................................................................................................................259
25.1.2. HDFS .........................................................................................................................................259
25.1.2.1. Client..................................................................................................................................................259
25.1.2.2. NameNode........................................................................................................................................259
25.1.2.3. Secondary NameNode .................................................................................................................259
25.1.2.4. DataNode.........................................................................................................................................259
25.1.3. MapReduce...............................................................................................................................260
25.1.3.1. Client.................................................................................................................................................260
25.1.3.2. JobTracker .......................................................................................................................................260
25.1.3.3. TaskTracker......................................................................................................................................261
25.1.3.4. Task ...................................................................................................................................................261
25.1.3.5. Reduce Task 執行過程 ...................................................................................................................261
25.1.4. Hadoop MapReduce 作業的生命週期...................................................................................262
1.作業提交與初始化...........................................................................................................................................262
2.任務排程與監控。 ...........................................................................................................................................262
3.任務執行環境準備...........................................................................................................................................262
4.任務執行 ..........................................................................................................................................................262
5.作業完成。 ......................................................................................................................................................262
26. SPARK............................................................................................................................................263
26.1.1. 概念 ............................................................................................................................................263
26.1.2. 核心架構 ....................................................................................................................................263
Spark Core .........................................................................................................................................................263
Spark SQL ..........................................................................................................................................................263
Spark Streaming................................................................................................................................................263
Mllib .....................................................................................................................................................................263
GraphX................................................................................................................................................................263
26.1.3. 核心元件 ....................................................................................................................................264
Cluster Manager-制整個叢集,監控 worker .................................................................................................264
Worker 節點-負責控制計算節點.......................................................................................................................264
Driver: 執行 Application 的 main()函式.........................................................................................................264
Executor:執行器,是為某個 Application 執行在 worker node 上的一個程序..........................................264
26.1.4. SPARK 程式設計模型......................................................................................................................264
26.1.5. SPARK 計算模型......................................................................................................................265
26.1.6. SPARK 執行流程......................................................................................................................266
1. 構建 Spark Application 的執行環境,啟動 SparkContext....................................................................267
2. SparkContext 向資源管理器(可以是 Standalone, Mesos, Yarn)申請執行 Executor 資源,並啟
動 StandaloneExecutorbackend, ..................................................................................................................267
3. Executor 向 SparkContext 申請 Task.....................................................................................................267
4. SparkContext 將應用程式分發給 Executor............................................................................................267
5. SparkContext 構建成 DAG 圖,將 DAG 圖分解成 Stage、將 Taskset 傳送給 Task Scheduler,最
後由 Task Scheduler 將 Task 傳送給 Executor 執行.....................................................................................267
6. Task 在 Executor 上執行,執行完釋放所有資源...................................................................................267
26.1.7. SPARK RDD 流程 ....................................................................................................................267
26.1.8. SPARK RDD.............................................................................................................................267
(1) RDD 的建立方式...........................................................................................................................................267
(2) RDD 的兩種操作運算元(轉換(Transformation)與行動(Action) ) ..............................................268
27. STORM ...........................................................................................................................................26913/04/2018 Page 17 of 283
27.1.1. 概念 ............................................................................................................................................269
27.1.1. 叢集架構 ....................................................................................................................................269
27.1.1.1. Nimbus(master-程式碼分發給 Supervisor) ................................................................................269
27.1.1.2. Supervisor(slave-管理 Worker 程序的啟動和終止) ...............................................................269
27.1.1.3. Worker(具體處理元件邏輯的程序) ............................................................................................269
27.1.1.4. Task ...................................................................................................................................................270
27.1.1.5. ZooKeeper ........................................................................................................................................270
27.1.2. 程式設計模型(spout->tuple->bolt) .......................................................................................270
27.1.2.1. Topology............................................................................................................................................270
27.1.2.2. Spout..................................................................................................................................................270
27.1.2.3. Bolt.....................................................................................................................................................270
27.1.2.4. Tuple..................................................................................................................................................270
27.1.2.5. Stream ...............................................................................................................................................271
27.1.3. Topology 執行...........................................................................................................................271
(1). Worker(程序) (2). Executor(執行緒) (3). Task..................................................................................271
27.1.3.1. Worker(1 個 worker 程序執行的是 1 個 topology 的子集) .......................................................271
27.1.3.2. Executor(executor 是 1 個被 worker 程序啟動的單獨執行緒)......................................................271
27.1.3.3. Task(最終執行 spout 或 bolt 中程式碼的單元)...............................................................................272
27.1.4. Storm Streaming Grouping.....................................................................................................272
27.1.4.1. huffle Grouping.................................................................................................................................273
27.1.4.2. Fields Grouping................................................................................................................................273
27.1.4.3. All grouping :廣播..........................................................................................................................273
27.1.4.4. Global grouping................................................................................................................................274
27.1.4.5. None grouping :不分組.................................................................................................................274
27.1.4.6. Direct grouping :直接分組 指定分組 ...........................................................................................274
28. YARN ..............................................................................................................................................275
28.1.1. 概念 ............................................................................................................................................275
28.1.2. ResourceManager ...................................................................................................................275
28.1.3. NodeManager ...........................................................................................................................275
28.1.4. ApplicationMaster .................................................................................................................276
28.1.5. YARN 執行流程 ....................................................................................................................277
29. 機器學習..........................................................................................................................................278
29.1.1. 決策樹 ........................................................................................................................................278
29.1.2. 隨機森林演算法 ............................................................................................................................278
29.1.3. 邏輯迴歸 ....................................................................................................................................278
29.1.4. SVM............................................................................................................................................278
29.1.5. 樸素貝葉斯 ................................................................................................................................278
29.1.6. K 最近鄰演算法.............................................................................................................................278
29.1.7. K 均值演算法.................................................................................................................................278
29.1.8. Adaboost 演算法 ..........................................................................................................................278
29.1.9. 神經網路 ....................................................................................................................................278
29.1.10. 馬爾可夫 ................................................................................................................................278
30. 雲端計算 .............................................................................................................................................279
30.1.1. SaaS ..........................................................................................................................................279
30.1.2. PaaS ..........................................................................................................................................279
30.1.3. IaaS............................................................................................................................................279
30.1.4. Docker........................................................................................................................................279
30.1.4.1. 概念....................................................................................................................................................279
30.1.4.2. Namespaces.....................................................................................................................................280
30.1.4.3. 程序(CLONE_NEWPID 實現的程序隔離)......................................................................................281
30.1.4.4. Libnetwork 與網路隔離....................................................................................................................281
30.1.4.5. 資源隔離與 CGroups .......................................................................................................................282
30.1.4.6. 映象與 UnionFS................................................................................................................................282
30.1.4.7. 儲存驅動............................................................................................................................................28213/04/2018 Page 18 of 283
30.1.5. Openstack .................................................................................................................................28313/04/2018 Page 19 of 283
2. JVM
(1) 基本概念:
JVM 是可執行 Java 程式碼的假想計算機 ,包括一套位元組碼指令集、一組暫存器、一個棧、
一個垃圾回收,堆 和 一個儲存方法域。 JVM 是執行在作業系統之上的,它與硬體沒有直接
的互動。
(2) 執行過程:13/04/2018 Page 20 of 283
我們都知道 Java 原始檔,通過編譯器,能夠生產相應的.Class 檔案,也就是位元組碼檔案,
而位元組碼檔案又通過 Java 虛擬機器中的直譯器,編譯成特定機器上的機器碼 。
也就是如下:
① Java 原始檔—->編譯器—->位元組碼檔案
② 位元組碼檔案—->JVM—->機器碼
每一種平臺的直譯器是不同的,但是實現的虛擬機器是相同的,這也就是 Java 為什麼能夠
跨平臺的原因了 ,當一個程式從開始執行,這時虛擬機器就開始例項化了,多個程式啟動就會
存在多個虛擬機器例項。程式退出或者關閉,則虛擬機器例項消亡,多個虛擬機器例項之間資料不
能共享。
2.1.執行緒
這裡所說的執行緒指程式執行過程中的一個執行緒實體。 JVM 允許一個應用併發執行多個執行緒。
Hotspot JVM 中的 Java 執行緒與原生作業系統執行緒有直接的對映關係。 當執行緒本地儲存、緩
衝區分配、同步物件、棧、程式計數器等準備好以後,就會建立一個作業系統原生執行緒。
Java 執行緒結束,原生執行緒隨之被回收。作業系統負責排程所有執行緒,並把它們分配到任何可
用的 CPU 上。當原生執行緒初始化完畢,就會呼叫 Java 執行緒的 run() 方法。當執行緒結束時,13/04/2018 Page 21 of 283
會釋放原生執行緒和 Java 執行緒的所有資源。
Hotspot JVM 後臺執行的系統執行緒主要有下面幾個:
虛擬機器執行緒
(VM thread)
這個執行緒等待 JVM 到達安全點操作出現。這些操作必須要在獨立的執行緒裡執行,因為當
堆修改無法進行時,執行緒都需要 JVM 位於安全點。這些操作的型別有: stop-theworld 垃圾回收、執行緒棧 dump、執行緒暫停、執行緒偏向鎖(biased locking)解除。
週期性任務執行緒 這執行緒負責定時器事件(也就是中斷),用來排程週期性操作的執行。
GC 執行緒 這些執行緒支援 JVM 中不同的垃圾回收活動。
編譯器執行緒 這些執行緒在執行時將位元組碼動態編譯成本地平臺相關的機器碼。
訊號分發執行緒 這個執行緒接收發送到 JVM 的訊號並呼叫適當的 JVM 方法處理。
2.2.JVM 記憶體區域
JVM 記憶體區域主要分為執行緒私有區域【程式計數器、虛擬機器棧、本地方法區】、執行緒共享區
域【JAVA 堆、方法區】、直接記憶體。
執行緒私有資料區域生命週期與執行緒相同, 依賴使用者執行緒的啟動/結束 而 建立/銷燬(在 Hotspot
VM 內, 每個執行緒都與作業系統的本地執行緒直接對映, 因此這部分記憶體區域的存/否跟隨本地執行緒的
生/死對應)。13/04/2018 Page 22 of 283
執行緒共享區域隨虛擬機器的啟動/關閉而建立/銷燬。
直接記憶體並不是 JVM 執行時資料區的一部分, 但也會被頻繁的使用: 在 JDK 1.4 引入的 NIO 提
供了基於 Channel 與 Buffer 的 IO 方式, 它可以使用 Native 函式庫直接分配堆外記憶體, 然後使用
DirectByteBuffer 物件作為這塊記憶體的引用進行操作(詳見: Java I/O 擴充套件), 這樣就避免了在 Java
堆和 Native 堆中來回複製資料, 因此在一些場景中可以顯著提高效能。
2.2.1. 程式計數器(執行緒私有)
一塊較小的記憶體空間, 是當前執行緒所執行的位元組碼的行號指示器,每條執行緒都要有一個獨立的
程式計數器,這類記憶體也稱為“執行緒私有” 的記憶體。
正在執行 java 方法的話,計數器記錄的是虛擬機器位元組碼指令的地址(當前指令的地址) 。如
果還是 Native 方法,則為空。
這個記憶體區域是唯一一個在虛擬機器中沒有規定任何 OutOfMemoryError 情況的區域。
2.2.2. 虛擬機器棧(執行緒私有)
是描述java方法執行的記憶體模型,每個方法在執行的同時都會建立一個棧幀(Stack Frame)
用於儲存區域性變量表、運算元棧、動態連結、方法出口等資訊。 每一個方法從呼叫直至執行完成
的過程,就對應著一個棧幀在虛擬機器棧中入棧到出棧的過程。
棧幀( Frame)是用來儲存資料和部分過程結果的資料結構,同時也被用來處理動態連結
(Dynamic Linking)、 方法返回值和異常分派( Dispatch Exception)。 棧幀隨著方法呼叫而創13/04/2018 Page 23 of 283
建,隨著方法結束而銷燬——無論方法是正常完成還是異常完成(丟擲了在方法內未被捕獲的異
常)都算作方法結束。
2.2.3. 本地方法區(執行緒私有)
本地方法區和 Java Stack 作用類似, 區別是虛擬機器棧為執行 Java 方法服務, 而本地方法棧則為
Native 方法服務, 如果一個 VM 實現使用 C-linkage 模型來支援 Native 呼叫, 那麼該棧將會是一個
C 棧,但 HotSpot VM 直接就把本地方法棧和虛擬機器棧合二為一。
2.2.4. 堆(Heap-執行緒共享) -執行時資料區
是被執行緒共享的一塊記憶體區域, 建立的物件和陣列都儲存在 Java 堆記憶體中,也是垃圾收集器進行
垃圾收集的最重要的記憶體區域。 由於現代 VM 採用分代收集演算法, 因此 Java 堆從 GC 的角度還可以
細分為: 新生代(Eden 區、 From Survivor 區和 To Survivor 區)和老年代。
2.2.5. 方法區/永久代(執行緒共享)
即我們常說的永久代(Permanent Generation), 用於儲存被 JVM 載入的類資訊、 常量、 靜
態變數、 即時編譯器編譯後的程式碼等資料. HotSpot VM把GC分代收集擴充套件至方法區, 即使用Java
堆的永久代來實現方法區, 這樣 HotSpot 的垃圾收集器就可以像管理 Java 堆一樣管理這部分記憶體,
而不必為方法區開發專門的記憶體管理器(永久帶的記憶體回收的主要目標是針對常量池的回收和型別
的解除安裝, 因此收益一般很小)。
執行時常量池(Runtime Constant Pool)是方法區的一部分。 Class 檔案中除了有類的版
本、欄位、方法、介面等描述等資訊外,還有一項資訊是常量池13/04/2018 Page 24 of 283
(Constant Pool Table),用於存放編譯期生成的各種字面量和符號引用,這部分內容將在類加
載後存放到方法區的執行時常量池中。 Java 虛擬機器對 Class 檔案的每一部分(自然也包括常量
池)的格式都有嚴格的規定,每一個位元組用於儲存哪種資料都必須符合規範上的要求,這樣才會
被虛擬機器認可、裝載和執行。
2.3.JVM 執行時記憶體
Java 堆從 GC 的角度還可以細分為: 新生代(Eden 區、 From Survivor 區和 To Survivor 區)和老年
代。
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 採用複製演算法。13/04/2018 Page 25 of 283
1: eden、 servicorFrom 複製到 ServicorTo,年齡+1
首先,把 Eden 和 ServivorFrom 區域中存活的物件複製到 ServicorTo 區域(如果有物件的年
齡以及達到了老年的標準,則賦值到老年代區),同時把這些物件的年齡+1(如果 ServicorTo 不
夠位置了就放到老年區);
2: 清空 eden、 servicorFrom
然後,清空 Eden 和 ServicorFrom 中的物件;
3: ServicorTo 和 ServicorFrom 互換
最後, ServicorTo 和 ServicorFrom 互換,原 ServicorTo 成為下一次 GC 時的 ServicorFrom
區。
2.3.2. 老年代
主要存放應用程式中生命週期長的記憶體物件。
老年代的物件比較穩定,所以 MajorGC 不會頻繁執行。在進行 MajorGC 前一般都先進行
了一次 MinorGC,使得有新生代的物件晉身入老年代,導致空間不夠用時才觸發。當無法找到足
夠大的連續空間分配給新建立的較大物件時也會提前觸發一次 MajorGC 進行垃圾回收騰出空間。
MajorGC 採用標記清除演算法:首先掃描一次所有老年代,標記出存活的物件,然後回收沒
有標記的物件。 MajorGC 的耗時比較長,因為要掃描再回收。 MajorGC 會產生記憶體碎片,為了減
少記憶體損耗,我們一般需要進行合併或者標記出來方便下次直接分配。當老年代也滿了裝不下的
時候,就會丟擲 OOM(Out of Memory)異常。
2.3.3. 永久代
指記憶體的永久儲存區域,主要存放 Class 和 Meta(元資料)的資訊,Class 在被載入的時候被
放入永久區域, 它和和存放例項的區域不同,GC 不會在主程式執行期對永久區域進行清理。所以這
也導致了永久代的區域會隨著載入的 Class 的增多而脹滿,最終丟擲 OOM 異常。
2.3.3.1. JAVA8 與元資料
在 Java8 中, 永久代已經被移除,被一個稱為“元資料區”(元空間)的區域所取代。元空間
的本質和永久代類似,元空間與永久代之間最大的區別在於: 元空間並不在虛擬機器中,而是使用
本地記憶體。因此,預設情況下,元空間的大小僅受本地記憶體限制。 類的元資料放入 native
memory, 字串池和類的靜態變數放入 java 堆中, 這樣可以載入多少類的元資料就不再由
MaxPermSize 控制, 而由系統的實際可用空間來控制。13/04/2018 Page 26 of 283
2.4.垃圾回收與演算法
2.4.1. 如何確定垃圾
2.4.1.1. 引用計數法
在 Java 中,引用和物件是有關聯的。如果要操作物件則必須用引用進行。因此,很顯然一個簡單
的辦法是通過引用計數來判斷一個物件是否可以回收。簡單說,即一個物件如果沒有任何與之關
聯的引用, 即他們的引用計數都不為 0, 則說明物件不太可能再被用到,那麼這個物件就是可回收
物件。
2.4.1.2. 可達性分析
為了解決引用計數法的迴圈引用問題, Java 使用了可達性分析的方法。通過一系列的“GC roots”
物件作為起點搜尋。如果在“GC roots”和一個物件之間沒有可達路徑,則稱該物件是不可達的。13/04/2018 Page 27 of 283
要注意的是,不可達物件不等價於可回收物件, 不可達物件變為可回收物件至少要經過兩次標記
過程。兩次標記後仍然是可回收物件,則將面臨回收。
2.4.2. 標記清除演算法(Mark-Sweep)
最基礎的垃圾回收演算法,分為兩個階段,標註和清除。標記階段標記出所有需要回收的物件,清
除階段回收被標記的物件所佔用的空間。如圖
從圖中我們就可以發現,該演算法最大的問題是記憶體碎片化嚴重,後續可能發生大物件不能找到可
利用空間的問題。
2.4.3. 複製演算法(copying)
為了解決 Mark-Sweep 演算法記憶體碎片化的缺陷而被提出的演算法。按記憶體容量將記憶體劃分為等大小
的兩塊。每次只使用其中一塊,當這一塊記憶體滿後將尚存活的物件複製到另一塊上去,把已使用
的記憶體清掉,如圖:13/04/2018 Page 28 of 283
這種演算法雖然實現簡單,記憶體效率高,不易產生碎片,但是最大的問題是可用記憶體被壓縮到了原
本的一半。且存活物件增多的話, Copying 演算法的效率會大大降低。
2.4.4. 標記整理演算法(Mark-Compact)
結合了以上兩個演算法,為了避免缺陷而提出。標記階段和 Mark-Sweep 演算法相同, 標記後不是清
理物件,而是將存活物件移向記憶體的一端。然後清除端邊界外的物件。如圖:13/04/2018 Page 29 of 283
2.4.5. 分代收集演算法
分代收集法是目前大部分 JVM 所採用的方法,其核心思想是根據物件存活的不同生命週期將記憶體
劃分為不同的域,一般情況下將 GC 堆劃分為老生代(Tenured/Old Generation)和新生代(Young
Generation)。老生代的特點是每次垃圾回收時只有少量物件需要被回收,新生代的特點是每次垃
圾回收時都有大量垃圾需要被回收,因此可以根據不同區域選擇不同的演算法。
2.4.5.1. 新生代與複製演算法
目前大部分 JVM 的 GC 對於新生代都採取 Copying 演算法,因為新生代中每次垃圾回收都要
回收大部分物件,即要複製的操作比較少,但通常並不是按照 1: 1 來劃分新生代。一般將新生代
劃分為一塊較大的 Eden 空間和兩個較小的 Survivor 空間(From Space, To Space),每次使用
Eden 空間和其中的一塊 Survivor 空間,當進行回收時,將該兩塊空間中還存活的物件複製到另
一塊 Survivor 空間中。
2.4.5.2. 老年代與標記複製演算法
而老年代因為每次只回收少量物件,因而採用 Mark-Compact 演算法。
1. JAVA 虛擬機器提到過的處於方法區的永生代(Permanet Generation), 它用來儲存 class 類,
常量,方法描述等。對永生代的回收主要包括廢棄常量和無用的類。
2. 物件的記憶體分配主要在新生代的 Eden Space 和 Survivor Space 的 From Space(Survivor 目
前存放物件的那一塊),少數情況會直接分配到老生代。
3. 當新生代的 Eden Space 和 From Space 空間不足時就會發生一次 GC,進行 GC 後, Eden
Space 和 From Space 區的存活物件會被挪到 To Space,然後將 Eden Space 和 From
Space 進行清理。
4. 如果 To Space 無法足夠儲存某個物件,則將這個物件儲存到老生代。
5. 在進行 GC 後,使用的便是 Eden Space 和 To Space 了,如此反覆迴圈。
6. 當物件在 Survivor 區躲過一次 GC 後,其年齡就會+1。 預設情況下年齡到達 15 的物件會被
移到老生代中。13/04/2018 Page 30 of 283
2.5.JAVA 四中引用型別
2.5.1. 強引用
在 Java 中最常見的就是強引用, 把一個物件賦給一個引用變數,這個引用變數就是一個強引
用。當一個物件被強引用變數引用時,它處於可達狀態,它是不可能被垃圾回收機制回收的,即
使該物件以後永遠都不會被用到 JVM 也不會回收。因此強引用是造成 Java 記憶體洩漏的主要原因之
一。
2.5.2. 軟引用
軟引用需要用 SoftReference 類來實現,對於只有軟引用的物件來說,當系統記憶體足夠時它
不會被回收,當系統記憶體空間不足時它會被回收。軟引用通常用在對記憶體敏感的程式中。
2.5.3. 弱引用
弱引用需要用 WeakReference 類來實現,它比軟引用的生存期更短,對於只有弱引用的物件
來說,只要垃圾回收機制一執行,不管 JVM 的記憶體空間是否足夠,總會回收該物件佔用的記憶體。
2.5.4. 虛引用
虛引用需要 PhantomReference 類來實現,它不能單獨使用,必須和引用佇列聯合使用。 虛
引用的主要作用是跟蹤物件被垃圾回收的狀態。
2.6.GC 分代收集演算法 VS 分割槽收集演算法
2.6.1. 分代收集演算法
當前主流 VM 垃圾收集都採用”分代收集” (Generational Collection)演算法, 這種演算法會根據
物件存活週期的不同將記憶體劃分為幾塊, 如 JVM 中的 新生代、老年代、永久代, 這樣就可以根據
各年代特點分別採用最適當的 GC 演算法
2.6.1.1. 在新生代-複製演算法
每次垃圾收集都能發現大批物件已死, 只有少量存活. 因此選用複製演算法, 只需要付出少量
存活物件的複製成本就可以完成收集.
2.6.1.2. 在老年代-標記整理演算法
因為物件存活率高、沒有額外空間對它進行分配擔保, 就必須採用“標記—清理”或“標
記—整理” 演算法來進行回收, 不必進行記憶體複製, 且直接騰出空閒記憶體.13/04/2018 Page 31 of 283
2.6.2. 分割槽收集演算法
分割槽演算法則將整個堆空間劃分為連續的不同小區間, 每個小區間獨立使用, 獨立回收. 這樣做的
好處是可以控制一次回收多少個小區間 , 根據目標停頓時間, 每次合理地回收若干個小區間(而不是
整個堆), 從而減少一次 GC 所產生的停頓。
2.7.GC 垃圾收集器
Java 堆記憶體被劃分為新生代和年老代兩部分,新生代主要使用複製和標記-清除垃圾回收演算法;
年老代主要使用標記-整理垃圾回收演算法,因此 java 虛擬中針對新生代和年老代分別提供了多種不
同的垃圾收集器, JDK1.6 中 Sun HotSpot 虛擬機器的垃圾收集器如下:
2.7.1. Serial 垃圾收集器(單執行緒、 複製演算法)
Serial(英文連續) 是最基本垃圾收集器,使用複製演算法,曾經是JDK1.3.1 之前新生代唯一的垃圾
收集器。 Serial 是一個單執行緒的收集器,它不但只會使用一個 CPU 或一條執行緒去完成垃圾收集工
作,並且在進行垃圾收集的同時,必須暫停其他所有的工作執行緒,直到垃圾收集結束。
Serial 垃圾收集器雖然在收集垃圾過程中需要暫停所有其他的工作執行緒,但是它簡單高效,對於限
定單個 CPU 環境來說,沒有執行緒互動的開銷,可以獲得最高的單執行緒垃圾收集效率,因此 Serial
垃圾收集器依然是 java 虛擬機器執行在 Client 模式下預設的新生代垃圾收集器。
2.7.2. ParNew 垃圾收集器(Serial+多執行緒)
ParNew 垃圾收集器其實是 Serial 收集器的多執行緒版本,也使用複製演算法,除了使用多執行緒進行垃
圾收集之外,其餘的行為和 Serial 收集器完全一樣, ParNew 垃圾收集器在垃圾收集過程中同樣也
要暫停所有其他的工作執行緒。13/04/2018 Page 32 of 283
ParNew 收集器預設開啟和 CPU 數目相同的執行緒數,可以通過-XX:ParallelGCThreads 引數來限
制垃圾收集器的執行緒數。 【Parallel:平行的】
ParNew 雖然是除了多執行緒外和Serial 收集器幾乎完全一樣,但是ParNew垃圾收集器是很多 java
虛擬機器執行在 Server 模式下新生代的預設垃圾收集器。
2.7.3. Parallel Scavenge 收集器(多執行緒複製演算法、高效)
Parallel Scavenge 收集器也是一個新生代垃圾收集器,同樣使用複製演算法,也是一個多執行緒的垃
圾收集器, 它重點關注的是程式達到一個可控制的吞吐量(Thoughput, CPU 用於執行使用者程式碼
的時間/CPU 總消耗時間,即吞吐量=執行使用者程式碼時間/(執行使用者程式碼時間+垃圾收集時間)),
高吞吐量可以最高效率地利用 CPU 時間,儘快地完成程式的運算任務,主要適用於在後臺運算而
不需要太多互動的任務。 自適應調節策略也是 ParallelScavenge 收集器與 ParNew 收集器的一個
重要區別。
2.7.4. Serial Old 收集器(單執行緒標記整理演算法 )
Serial Old 是 Serial 垃圾收集器年老代版本,它同樣是個單執行緒的收集器,使用標記-整理演算法,
這個收集器也主要是執行在 Client 預設的 java 虛擬機器預設的年老代垃圾收集器。
在 Server 模式下,主要有兩個用途:
1. 在 JDK1.5 之前版本中與新生代的 Parallel Scavenge 收集器搭配使用。
2. 作為年老代中使用 CMS 收集器的後備垃圾收集方案。
新生代 Serial 與年老代 Serial Old 搭配垃圾收集過程圖:
新生代 Parallel Scavenge 收集器與 ParNew 收集器工作原理類似,都是多執行緒的收集器,都使
用的是複製演算法,在垃圾收集過程中都需要暫停所有的工作執行緒。新生代 Parallel
Scavenge/ParNew 與年老代 Serial Old 搭配垃圾收集過程圖:13/04/2018 Page 33 of 283
2.7.5. Parallel Old 收集器(多執行緒標記整理演算法)
Parallel Old 收集器是Parallel Scavenge的年老代版本,使用多執行緒的標記-整理演算法,在 JDK1.6
才開始提供。
在 JDK1.6 之前,新生代使用 ParallelScavenge 收集器只能搭配年老代的 Serial Old 收集器,只
能保證新生代的吞吐量優先,無法保證整體的吞吐量, Parallel Old 正是為了在年老代同樣提供吞
吐量優先的垃圾收集器, 如果系統對吞吐量要求比較高,可以優先考慮新生代 Parallel Scavenge
和年老代 Parallel Old 收集器的搭配策略。
新生代 Parallel Scavenge 和年老代 Parallel Old 收集器搭配執行過程圖:
2.7.6. CMS 收集器(多執行緒標記清除演算法)
Concurrent mark sweep(CMS)收集器是一種年老代垃圾收集器,其最主要目標是獲取最短垃圾
回收停頓時間, 和其他年老代使用標記-整理演算法不同,它使用多執行緒的標記-清除演算法。
最短的垃圾收集停頓時間可以為互動比較高的程式提高使用者體驗。
CMS 工作機制相比其他的垃圾收集器來說更復雜,整個過程分為以下 4 個階段:
2.7.6.1. 初始標記
只是標記一下 GC Roots 能直接關聯的物件,速度很快,仍然需要暫停所有的工作執行緒。13/04/2018 Page 34 of 283
2.7.6.2. 併發標記
進行 GC Roots 跟蹤的過程,和使用者執行緒一起工作,不需要暫停工作執行緒。
2.7.6.3. 重新標記
為了修正在併發標記期間,因使用者程式繼續執行而導致標記產生變動的那一部分物件的標記
記錄,仍然需要暫停所有的工作執行緒。
2.7.6.4. 併發清除
清除 GC Roots 不可達物件,和使用者執行緒一起工作,不需要暫停工作執行緒。由於耗時最長的並
發標記和併發清除過程中,垃圾收集執行緒可以和使用者現在一起併發工作, 所以總體上來看
CMS 收集器的記憶體回收和使用者執行緒是一起併發地執行。
CMS 收集器工作過程:
2.7.7. G1 收集器
Garbage first 垃圾收集器是目前垃圾收集器理論發展的最前沿成果,相比與 CMS 收集器, G1 收
集器兩個最突出的改進是:
1. 基於標記-整理演算法,不產生記憶體碎片。
2. 可以非常精確控制停頓時間,在不犧牲吞吐量前提下,實現低停頓垃圾回收。
G1 收集器避免全區域垃圾收集,它把堆記憶體劃分為大小固定的幾個獨立區域,並且跟蹤這些區域
的垃圾收集進度,同時在後臺維護一個優先順序列表,每次根據所允許的收集時間, 優先回收垃圾
最多的區域。區域劃分和優先順序區域回收機制,確保 G1 收集器可以在有限時間獲得最高的垃圾收
集效率。
2.8. JAVA IO/NIO
2.8.1. 阻塞 IO 模型
最傳統的一種 IO 模型,即在讀寫資料過程中會發生阻塞現象。當用戶執行緒發出 IO 請求之後,內
核會去檢視資料是否就緒,如果沒有就緒就會等待資料就緒,而使用者執行緒就會處於阻塞狀態,用
戶執行緒交出 CPU。當資料就緒之後,核心會將資料拷貝到使用者執行緒,並返回結果給使用者執行緒,用13/04/2018 Page 35 of 283
戶執行緒才解除 block 狀態。典型的阻塞 IO 模型的例子為: data = socket.read();如果資料沒有就
緒,就會一直阻塞在 read 方法。
2.8.2. 非阻塞 IO 模型
當用戶執行緒發起一個 read 操作後,並不需要等待,而是馬上就得到了一個結果。 如果結果是一個
error 時,它就知道資料還沒有準備好,於是它可以再次傳送 read 操作。一旦核心中的資料準備
好了,並且又再次收到了使用者執行緒的請求,那麼它馬上就將資料拷貝到了使用者執行緒,然後返回。
所以事實上,在非阻塞 IO 模型中,使用者執行緒需要不斷地詢問核心資料是否就緒,也就說非阻塞 IO
不會交出 CPU,而會一直佔用 CPU。 典型的非阻塞 IO 模型一般如下:
while(true){
data = socket.read();
if(data!= error){
處理資料
break;
}
}
但是對於非阻塞 IO 就有一個非常嚴重的問題, 在 while 迴圈中需要不斷地去詢問核心資料是否就
緒,這樣會導致 CPU 佔用率非常高,因此一般情況下很少使用 while 迴圈這種方式來讀取資料。
2.8.3. 多路複用 IO 模型
多路複用 IO 模型是目前使用得比較多的模型。 Java NIO 實際上就是多路複用 IO。在多路複用 IO
模型中,會有一個執行緒不斷去輪詢多個 socket 的狀態,只有當 socket 真正有讀寫事件時,才真
正呼叫實際的 IO 讀寫操作。因為在多路複用 IO 模型中,只需要使用一個執行緒就可以管理多個
socket,系統不需要建立新的程序或者執行緒,也不必維護這些執行緒和程序,並且只有在真正有
socket 讀寫事件進行時,才會使用 IO 資源,所以它大大減少了資源佔用。在 Java NIO 中,是通
過 selector.select()去查詢每個通道是否有到達事件,如果沒有事件,則一直阻塞在那裡,因此這
種方式會導致使用者執行緒的阻塞。多路複用 IO 模式,通過一個執行緒就可以管理多個 socket,只有當
socket 真正有讀寫事件發生才會佔用資源來進行實際的讀寫操作。因此,多路複用 IO 比較適合連
接數比較多的情況。
另外多路複用 IO 為何比非阻塞 IO 模型的效率高是因為在非阻塞 IO 中,不斷地詢問 socket 狀態
時通過使用者執行緒去進行的,而在多路複用 IO 中,輪詢每個 socket 狀態是核心在進行的,這個效
率要比使用者執行緒要高的多。
不過要注意的是,多路複用 IO 模型是通過輪詢的方式來檢測是否有事件到達,並且對到達的事件
逐一進行響應。因此對於多路複用 IO 模型來說, 一旦事件響應體很大,那麼就會導致後續的事件
遲遲得不到處理,並且會影響新的事件輪詢。13/04/2018 Page 36 of 283
2.8.4. 訊號驅動 IO 模型
在訊號驅動 IO 模型中,當用戶執行緒發起一個 IO 請求操作,會給對應的 socket 註冊一個訊號函
數,然後使用者執行緒會繼續執行,當核心資料就緒時會發送一個訊號給使用者執行緒,使用者執行緒接收到
訊號之後,便在訊號函式中呼叫 IO 讀寫操作來進行實際的 IO 請求操作。
2.8.5. 非同步 IO 模型
非同步 IO 模型才是最理想的 IO 模型,在非同步 IO 模型中,當用戶執行緒發起 read 操作之後,立刻就
可以開始去做其它的事。而另一方面,從核心的角度,當它受到一個 asynchronous read 之後,
它會立刻返回,說明 read 請求已經成功發起了,因此不會對使用者執行緒產生任何 block。然後,內
核會等待資料準備完成,然後將資料拷貝到使用者執行緒,當這一切都完成之後,核心會給使用者執行緒
傳送一個訊號,告訴它 read 操作完成了。也就說使用者執行緒完全不需要實際的整個 IO 操作是如何
進行的, 只需要先發起一個請求,當接收核心返回的成功訊號時表示 IO 操作已經完成,可以直接
去使用資料了。
也就說在非同步 IO 模型中, IO 操作的兩個階段都不會阻塞使用者執行緒,這兩個階段都是由核心自動完
成,然後傳送一個訊號告知使用者執行緒操作已完成。使用者執行緒中不需要再次呼叫 IO 函式進行具體的
讀寫。這點是和訊號驅動模型有所不同的,在訊號驅動模型中,當用戶執行緒接收到訊號表示資料
已經就緒,然後需要使用者執行緒呼叫 IO 函式進行實際的讀寫操作;而在非同步 IO 模型中,收到訊號
表示 IO 操作已經完成,不需要再在使用者執行緒中呼叫 IO 函式進行實際的讀寫操作。
注意,非同步 IO 是需要作業系統的底層支援,在 Java 7 中,提供了 Asynchronous IO。
更多參考: http://www.importnew.com/19816.html
2.8.1. JAVA IO 包13/04/2018 Page 37 of 283
2.8.2. JAVA NIO
NIO 主要有三大核心部分: Channel(通道), Buffer(緩衝區), Selector。傳統 IO 基於位元組流和字
符流進行操作, 而 NIO 基於 Channel 和 Buffer(緩衝區)進行操作,資料總是從通道讀取到緩衝區
中,或者從緩衝區寫入到通道中。 Selector(選擇區)用於監聽多個通道的事件(比如:連線開啟,
資料到達)。因此,單個執行緒可以監聽多個數據通道。13/04/2018 Page 38 of 283
NIO 和傳統 IO 之間第一個最大的區別是, IO 是面向流的, NIO 是面向緩衝區的。
2.8.2.1. NIO 的緩衝區
Java IO 面向流意味著每次從流中讀一個或多個位元組,直至讀取所有位元組,它們沒有被快取在任何
地方。此外,它不能前後移動流中的資料。如果需要前後移動從流中讀取的資料, 需要先將它緩
存到一個緩衝區。 NIO 的緩衝導向方法不同。資料讀取到一個它稍後處理的緩衝區,需要時可在
緩衝區中前後移動。這就增加了處理過程中的靈活性。但是,還需要檢查是否該緩衝區中包含所
有您需要處理的資料。而且,需確保當更多的資料讀入緩衝區時,不要覆蓋緩衝區裡尚未處理的
資料。
2.8.2.2. NIO 的非阻塞
IO 的各種流是阻塞的。這意味著,當一個執行緒呼叫 read() 或 write()時,該執行緒被阻塞,直到有
一些資料被讀取,或資料完全寫入。該執行緒在此期間不能再幹任何事情了。 NIO 的非阻塞模式,
使一個執行緒從某通道傳送請求讀取資料,但是它僅能得到目前可用的資料,如果目前沒有資料可
用時,就什麼都不會獲取。而不是保持執行緒阻塞,所以直至資料變的可以讀取之前,該執行緒可以
繼續做其他的事情。 非阻塞寫也是如此。一個執行緒請求寫入一些資料到某通道,但不需要等待它
完全寫入,這個執行緒同時可以去做別的事情。 執行緒通常將非阻塞 IO 的空閒時間用於在其它通道上
執行 IO 操作,所以一個單獨的執行緒現在可以管理多個輸入和輸出通道(channel)。13/04/2018 Page 39 of 28313/04/2018 Page 40 of 283
2.8.3. Channel
首先說一下 Channel,國內大多翻譯成“通道”。 Channel 和 IO 中的 Stream(流)是差不多一個
等級的。 只不過 Stream 是單向的,譬如: InputStream, OutputStream, 而 Channel 是雙向
的,既可以用來進行讀操作,又可以用來進行寫操作。
NIO 中的 Channel 的主要實現有:
1. FileChannel
2. DatagramChannel
3. SocketChannel
4. ServerSocketChannel
這裡看名字就可以猜出個所以然來:分別可以對應檔案 IO、 UDP 和 TCP(Server 和 Client)。
下面演示的案例基本上就是圍繞這 4 個型別的 Channel 進行陳述的。
2.8.4. Buffer
Buffer,故名思意, 緩衝區,實際上是一個容器,是一個連續陣列。 Channel 提供從檔案、
網路讀取資料的渠道,但是讀取或寫入的資料都必須經由 Buffer。
上面的圖描述了從一個客戶端向服務端傳送資料,然後服務端接收資料的過程。客戶端傳送
資料時,必須先將資料存入 Buffer 中,然後將 Buffer 中的內容寫入通道。服務端這邊接收資料必
須通過 Channel 將資料讀入到 Buffer 中,然後再從 Buffer 中取出資料來處理。
在 NIO 中, Buffer 是一個頂層父類,它是一個抽象類,常用的 Buffer 的子類有:
ByteBuffer、 IntBuffer、 CharBuffer、 LongBuffer、 DoubleBuffer、 FloatBuffer、
ShortBuffer
2.8.5. Selector
Selector 類是 NIO 的核心類, Selector 能夠檢測多個註冊的通道上是否有事件發生,如果有事
件發生,便獲取事件然後針對每個事件進行相應的響應處理。這樣一來,只是用一個單執行緒就可
以管理多個通道,也就是管理多個連線。這樣使得只有在連線真正有讀寫事件發生時,才會呼叫
函式來進行讀寫,就大大地減少了系統開銷,並且不必為每個連線都建立一個執行緒,不用去維護
多個執行緒,並且避免了多執行緒之間的上下文切換導致的開銷。13/04/2018 Page 41 of 283
2.9.JVM 類載入機制
JVM 類載入機制分為五個部分:載入,驗證,準備,解析,初始化,下面我們就分別來看一下這
五個過程。
2.9.1.1. 載入
載入是類載入過程中的一個階段, 這個階段會在記憶體中生成一個代表這個類的 java.lang.Class 對
象, 作為方法區這個類的各種資料的入口。注意這裡不一定非得要從一個 Class 檔案獲取,這裡既
可以從 ZIP 包中讀取(比如從 jar 包和 war 包中讀取),也可以在執行時計算生成(動態代理),
也可以由其它檔案生成(比如將 JSP 檔案轉換成對應的 Class 類)。
2.9.1.2. 驗證
這一階段的主要目的是為了確保 Class 檔案的位元組流中包含的資訊是否符合當前虛擬機器的要求,並
且不會危害虛擬機器自身的安全。
2.9.1.3. 準備
準備階段是正式為類變數分配記憶體並設定類變數的初始值階段,即在方法區中分配這些變數所使
用的記憶體空間。注意這裡所說的初始值概念,比如一個類變數定義為:
public static int v = 8080;
實際上變數 v 在準備階段過後的初始值為 0 而不是 8080, 將 v 賦值為 8080 的 put static 指令是
程式被編譯後, 存放於類構造器<client>方法之中。
但是注意如果宣告為:
public static final int v = 8080;
在編譯階段會為 v 生成 ConstantValue 屬性,在準備階段虛擬機器會根據 ConstantValue 屬性將 v
賦值為 8080。
2.9.1.4. 解析
解析階段是指虛擬機器將常量池中的符號引用替換為直接引用的過程。符號引用就是 class 檔案中
的:13/04/2018 Page 42 of 283
1. CONSTANT_Class_info
2. CONSTANT_Field_info
3. CONSTANT_Method_info
等型別的常量。
2.9.1.5. 符號引用
符號引用與虛擬機器實現的佈局無關, 引用的目標並不一定要已經載入到記憶體中。 各種虛擬
機實現的記憶體佈局可以各不相同,但是它們能接受的符號引用必須是一致的,因為符號引
用的字面量形式明確定義在 Java 虛擬機器規範的 Class 檔案格式中。
2.9.1.6. 直接引用
直接引用可以是指向目標的指標,相對偏移量或是一個能間接定位到目標的控制代碼。如果有
了直接引用,那引用的目標必定已經在記憶體中存在。
2.9.1.7. 初始化
初始化階段是類載入最後一個階段,前面的類載入階段之後,除了在載入階段可以自定義類載入
器以外,其它操作都由 JVM 主導。到了初始階段,才開始真正執行類中定義的 Java 程式程式碼。
2.9.1.8. 類構造器<client>
初始化階段是執行類構造器<client>方法的過程。 <client>方法是由編譯器自動收集類中的類變
量的賦值操作和靜態語句塊中的語句合併而成的。虛擬機器會保證子<client>方法執行之前,父類
的<client>方法已經執行完畢, 如果一個類中沒有對靜態變數賦值也沒有靜態語句塊,那麼編譯
器可以不為這個類生成<client>()方法。
注意以下幾種情況不會執行類初始化:
1. 通過子類引用父類的靜態欄位,只會觸發父類的初始化,而不會觸發子類的初始化。
2. 定義物件陣列,不會觸發該類的初始化。
3. 常量在編譯期間會存入呼叫類的常量池中,本質上並沒有直接引用定義常量的類,不會觸
發定義常量所在的類。
4. 通過類名獲取 Class 物件,不會觸發類的初始化。
5. 通過 Class.forName 載入指定類時,如果指定引數 initialize 為 false 時,也不會觸發類初
始化,其實這個引數是告訴虛擬機器,是否要對類進行初始化。
6. 通過 ClassLoader 預設的 loadClass 方法,也不會觸發初始化動作。
2.9.2. 類載入器
虛擬機器設計團隊把載入動作放到 JVM 外部實現,以便讓應用程式決定如何獲取所需的類, JVM 提
供了 3 種類載入器:13/04/2018 Page 43 of 283
2.9.2.1. 啟動類載入器(Bootstrap ClassLoader)
1. 負責載入 JAVA_HOME\lib 目錄中的, 或通過-Xbootclasspath 引數指定路徑中的, 且被
虛擬機器認可(按檔名識別, 如 rt.jar) 的類。
2.9.2.2. 擴充套件類載入器(Extension ClassLoader)
2. 負責載入 JAVA_HOME\lib\ext 目錄中的,或通過 java.ext.dirs 系統變數指定路徑中的類
庫。
2.9.2.3. 應用程式類載入器(Application ClassLoader):
3. 負責載入使用者路徑(classpath)上的類庫。
JVM 通過雙親委派模型進行類的載入, 當然我們也可以通過繼承 java.lang.ClassLoader
實現自定義的類載入器。
2.9.3. 雙親委派
當一個類收到了類載入請求,他首先不會嘗試自己去載入這個類,而是把這個請求委派給父
類去完成,每一個層次類載入器都是如此,因此所有的載入請求都應該傳送到啟動類載入其中,
只有當父類載入器反饋自己無法完成這個請求的時候(在它的載入路徑下沒有找到所需載入的
Class), 子類載入器才會嘗試自己去載入。
採用雙親委派的一個好處是比如載入位於 rt.jar 包中的類 java.lang.Object,不管是哪個載入
器載入這個類,最終都是委託給頂層的啟動類載入器進行載入,這樣就保證了使用不同的類載入
器最終得到的都是同樣一個 Object 物件。13/04/2018 Page 44 of 283
2.9.4. OSGI( 動態模型系統)
OSGi(Open Service Gateway Initiative),是面向 Java 的動態模型系統,是 Java 動態化模組化系
統的一系列規範。
2.9.4.1. 動態改變構造
OSGi 服務平臺提供在多種網路裝置上無需重啟的動態改變構造的功能。為了最小化耦合度和促使
這些耦合度可管理, OSGi 技術提供一種面向服務的架構,它能使這些元件動態地發現對方。
2.9.4.2. 模組化程式設計與熱插拔
OSGi 旨在為實現 Java 程式的模組化程式設計提供基礎條件,基於 OSGi 的程式很可能可以實現模組級
的熱插拔功能,當程序升級更新時,可以只停用、重新安裝然後啟動程式的其中一部分,這對企
業級程式開發來說是非常具有誘惑力的特性。
OSGi 描繪了一個很美好的模組化開發目標,而且定義了實現這個目標的所需要服務與架構,同時
也有成熟的框架進行實現支援。但並非所有的應用都適合採用 OSGi 作為基礎架構,它在提供強大
功能同時,也引入了額外的複雜度,因為它不遵守了類載入的雙親委託模型。13/04/2018 Page 45 of 283
3. JAVA 集合
3.1.介面繼承關係和實現
集合類存放於 Java.util 包中, 主要有 3 種: set(集)、 list(列表包含 Queue)和 map(對映)。
1. Collection: Collection 是集合 List、 Set、 Queue 的最基本的介面。
2. Iterator:迭代器,可以通過迭代器遍歷集合中的資料
3. Map:是對映表的基礎介面13/04/2018 Page 46 of 28313/04/2018 Page 47 of 283
3.2.List
Java 的 List 是非常常用的資料型別。 List 是有序的 Collection。 Java List 一共三個實現類:
分別是 ArrayList、 Vector 和 LinkedList。
3.2.1. ArrayList(陣列)
ArrayList 是最常用的 List 實現類,內部是通過陣列實現的,它允許對元素進行快速隨機訪問。數
組的缺點是每個元素之間不能有間隔, 當陣列大小不滿足時需要增加儲存能力,就要將已經有數
組的資料複製到新的儲存空間中。 當從 ArrayList 的中間位置插入或者刪除元素時,需要對陣列進
行復制、移動、代價比較高。因此,它適合隨機查詢和遍歷,不適合插入和刪除。
3.2.2. Vector( 陣列實現、 執行緒同步)
Vector 與 ArrayList 一樣,也是通過陣列實現的,不同的是它支援執行緒的同步,即某一時刻只有一
個執行緒能夠寫 Vector,避免多執行緒同時寫而引起的不一致性,但實現同步需要很高的花費,因此,
訪問它比訪問 ArrayList 慢。
3.2.3. LinkList(連結串列)
LinkedList 是用連結串列結構儲存資料的,很適合資料的動態插入和刪除,隨機訪問和遍歷速度比較
慢。另外,他還提供了 List 介面中沒有定義的方法,專門用於操作表頭和表尾元素,可以當作堆
棧、佇列和雙向佇列使用。13/04/2018 Page 48 of 283
3.3.Set
Set 注重獨一無二的性質,該體系集合用於儲存無序(存入和取出的順序不一定相同)元素, 值不能重
復。物件的相等性本質是物件 hashCode 值(java 是依據物件的記憶體地址計算出的此序號) 判斷
的, 如果想要讓兩個不同的物件視為相等的,就必須覆蓋 Object 的 hashCode 方法和 equals 方
法。
3.3.1.1. HashSet( Hash 表)
雜湊表邊存放的是雜湊值。 HashSet 儲存元素的順序並不是按照存入時的順序(和 List 顯然不
同) 而是按照雜湊值來存的所以取資料也是按照雜湊值取得。元素的雜湊值是通過元素的
hashcode 方法來獲取的, HashSet 首先判斷兩個元素的雜湊值,如果雜湊值一樣,接著會比較
equals 方法 如果 equls 結果為 true , HashSet 就視為同一個元素。如果 equals 為 false 就不是
同一個元素。
雜湊值相同 equals 為 false 的元素是怎麼儲存呢,就是在同樣的雜湊值下順延(可以認為雜湊值相
同的元素放在一個雜湊桶中)。也就是雜湊一樣的存一列。 如圖 1 表示 hashCode 值不相同的情
況; 圖 2 表示 hashCode 值相同,但 equals 不相同的情況。13/04/2018 Page 49 of 283
HashSet 通過 hashCode 值來確定元素在記憶體中的位置。 一個 hashCode 位置上可以存放多個元
素。
3.3.1.2. TreeSet(二叉樹)
1. TreeSet()是使用二叉樹的原理對新 add()的物件按照指定的順序排序(升序、降序),每增
加一個物件都會進行排序,將物件插入的二叉樹指定的位置。
2. Integer 和 String 物件都可以進行預設的 TreeSet 排序,而自定義類的物件是不可以的, 自
己定義的類必須實現 Comparable 介面,並且覆寫相應的 compareTo()函式,才可以正常使
用。
3. 在覆寫 compare()函式時,要返回相應的值才能使 TreeSet 按照一定的規則來排序
4. 比較此物件與指定物件的順序。如果該物件小於、等於或大於指定物件,則分別返回負整
數、零或正整數。
3.3.1.3. LinkHashSet( HashSet+LinkedHashMap)
對於 LinkedHashSet 而言,它繼承與 HashSet、又基於 LinkedHashMap 來實現的。
LinkedHashSet 底層使用 LinkedHashMap 來儲存所有元素,它繼承與 HashSet,其所有的方法
操作上又與 HashSet 相同,因此 LinkedHashSet 的實現上非常簡單,只提供了四個構造方法,並
通過傳遞一個標識引數,呼叫父類的構造器,底層構造一個 LinkedHashMap 來實現,在相關操
作上與父類 HashSet 的操作相同,直接呼叫父類 HashSet 的方法即可。13/04/2018 Page 50 of 283
3.4.Map
3.4.1. HashMap(陣列+連結串列+紅黑樹)
HashMap 根據鍵的 hashCode 值儲存資料,大多數情況下可以直接定位到它的值,因而具有很快
的訪問速度,但遍歷順序卻是不確定的。 HashMap 最多隻允許一條記錄的鍵為 null,允許多條記
錄的值為 null。 HashMap 非執行緒安全,即任一時刻可以有多個執行緒同時寫 HashMap,可能會導
致資料的不一致。如果需要滿足執行緒安全,可以用 Collections 的 synchronizedMap 方法使
HashMap 具有執行緒安全的能力,或者使用 ConcurrentHashMap。 我們用下面這張圖來介紹
HashMap 的結構。
3.4.1.1. JAVA7 實現
大方向上, HashMap 裡面是一個數組,然後陣列中每個元素是一個單向連結串列。上圖中,每個綠色
的實體是巢狀類 Entry 的例項, Entry 包含四個屬性: key, value, hash 值和用於單向連結串列的 next。
1. capacity:當前陣列容量,始終保持 2^n,可以擴容,擴容後陣列大小為當前的 2 倍。
2. loadFactor:負載因子,預設為 0.75。13/04/2018 Page 51 of 283
3. threshold:擴容的閾值,等於 capacity * loadFactor
3.4.1.2. JAVA8 實現
Java8 對 HashMap 進行了一些修改, 最大的不同就是利用了紅黑樹,所以其由 陣列+連結串列+紅黑
樹 組成。
根據 Java7 HashMap 的介紹,我們知道,查詢的時候,根據 hash 值我們能夠快速定位到陣列的
具體下標,但是之後的話, 需要順著連結串列一個個比較下去才能找到我們需要的,時間複雜度取決
於連結串列的長度,為 O(n)。為了降低這部分的開銷,在 Java8 中, 當連結串列中的元素超過了 8 個以後,
會將連結串列轉換為紅黑樹,在這些位置進行查詢的時候可以降低時間複雜度為 O(logN)。
3.4.2. ConcurrentHashMap
3.4.2.1. Segment 段
ConcurrentHashMap 和 HashMap 思路是差不多的,但是因為它支援併發操作,所以要複雜一
些。整個 ConcurrentHashMap 由一個個 Segment 組成, Segment 代表”部分“或”一段“的
意思,所以很多地方都會將其描述為分段鎖。注意,行文中,我很多地方用了“槽”來代表一個
segment。
3.4.2.2. 執行緒安全(Segment 繼承 ReentrantLock 加鎖)
簡單理解就是, ConcurrentHashMap 是一個 Segment 陣列, Segment 通過繼承
ReentrantLock 來進行加鎖,所以每次需要加鎖的操作鎖住的是一個 segment,這樣只要保證每
個 Segment 是執行緒安全的,也就實現了全域性的執行緒安全。13/04/2018 Page 52 of 283
3.4.2.3. 並行度(預設 16)
concurrencyLevel:並行級別、併發數、 Segment 數,怎麼翻譯不重要,理解它。預設是 16,
也就是說 ConcurrentHashMap 有 16 個 Segments,所以理論上, 這個時候,最多可以同時支
持 16 個執行緒併發寫,只要它們的操作分別分佈在不同的 Segment 上。這個值可以在初始化的時
候設定為其他值,但是一旦初始化以後,它是不可以擴容的。再具體到每個 Segment 內部,其實
每個 Segment 很像之前介紹的 HashMap,不過它要保證執行緒安全,所以處理起來要麻煩些。
3.4.2.4. Java8 實現 (引入了紅黑樹)
Java8 對 ConcurrentHashMap 進行了比較大的改動,Java8 也引入了紅黑樹。13/04/2018 Page 53 of 283
3.4.3. HashTable(執行緒安全)
Hashtable 是遺留類,很多對映的常用功能與 HashMap 類似,不同的是它承自 Dictionary 類,
並且是執行緒安全的,任一時間只有一個執行緒能寫 Hashtable,併發性不如 ConcurrentHashMap,
因為 ConcurrentHashMap 引入了分段鎖。 Hashtable 不建議在新程式碼中使用,不需要執行緒安全
的場合可以用 HashMap 替換,需要執行緒安全的場合可以用 ConcurrentHashMap 替換。
3.4.4. TreeMap(可排序)
TreeMap 實現 SortedMap 介面,能夠把它儲存的記錄根據鍵排序,預設是按鍵值的升序排序,
也可以指定排序的比較器,當用 Iterator 遍歷 TreeMap 時,得到的記錄是排過序的。
如果使用排序的對映,建議使用 TreeMap。
在使用 TreeMap 時, key 必須實現 Comparable 介面或者在構造 TreeMap 傳入自定義的
Comparator,否則會在執行時丟擲 java.lang.ClassCastException 型別的異常。
參考: https://www.ibm.com/developerworks/cn/java/j-lo-tree/index.html
3.4.5. LinkHashMap(記錄插入順序)
LinkedHashMap 是 HashMap 的一個子類,儲存了記錄的插入順序,在用 Iterator 遍歷
LinkedHashMap 時,先得到的記錄肯定是先插入的,也可以在構造時帶引數,按照訪問次序排序。
參考 1: http://www.importnew.com/28263.html
參考 2: http://www.importnew.com/20386.html#comment-64812313/04/2018 Page 54 of 283
4. JAVA 多執行緒併發
4.1.1. JAVA 併發知識庫
4.1.2. JAVA 執行緒實現/建立方式
4.1.2.1. 繼承 Thread 類
Thread 類本質上是實現了 Runnable 介面的一個例項,代表一個執行緒的例項。 啟動執行緒的唯一方
法就是通過 Thread 類的 start()例項方法。 start()方法是一個 native 方法,它將啟動一個新線
程,並執行 run()方法。
public class MyThread extends Thread {
public void run() {
System.out.println("MyThread.run()");
}
}
MyThread myThread1 = new MyThread();
myThread1.start();
4.1.2.2. 實現 Runnable 介面。
如果自己的類已經 extends 另一個類,就無法直接 extends Thread,此時,可以實現一個
Runnable 介面。
public class MyThread extends OtherClass implements Runnable {
public void run() {
System.out.println("MyThread.run()");
}
}13/04/2018 Page 55 of 283
//啟動 MyThread,需要首先例項化一個 Thread,並傳入自己的 MyThread 例項:
MyThread myThread = new MyThread();
Thread thread = new Thread(myThread);
thread.start();
//事實上,當傳入一個 Runnable target 引數給 Thread 後, Thread 的 run()方法就會呼叫
target.run()
public void run() {
if (target != null) {
target.run();
}
}
4.1.2.3. ExecutorService、 Callable<Class>、 Future 有返回值執行緒
有返回值的任務必須實現 Callable 介面,類似的,無返回值的任務必須 Runnable 介面。執行
Callable 任務後,可以獲取一個 Future 的物件,在該物件上呼叫 get 就可以獲取到 Callable 任務
返回的 Object 了,再結合線程池介面 ExecutorService 就可以實現傳說中有返回結果的多執行緒
了。
//建立一個執行緒池
ExecutorService pool = Executors.newFixedThreadPool(taskSize);
// 建立多個有返回值的任務
List<Future> list = new ArrayList<Future>();
for (int i = 0; i < taskSize; i++) {
Callable c = new MyCallable(i + " ");
// 執行任務並獲取 Future 物件
Future f = pool.submit(c);
list.add(f);
}
// 關閉執行緒池
pool.shutdown();
// 獲取所有併發任務的執行結果
for (Future f : list) {
// 從 Future 物件上獲取任務的返回值,並輸出到控制檯
System.out.println("res: " + f.get().toString());
}13/04/2018 Page 56 of 283
4.1.2.4. 基於執行緒池的方式
執行緒和資料庫連線這些資源都是非常寶貴的資源。那麼每次需要的時候建立,不需要的時候銷
毀,是非常浪費資源的。那麼我們就可以使用快取的策略,也就是使用執行緒池。
// 建立執行緒池
ExecutorService threadPool = Executors.newFixedThreadPool(10);
while(true) {
threadPool.execute(new Runnable() { // 提交多個執行緒任務,並執行
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + " is running ..");
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
}
}
4.1.3. 4 種執行緒池
Java 裡面執行緒池的頂級介面是 Executor,但是嚴格意義上講 Executor 並不是一個執行緒池,而
只是一個執行執行緒的工具。真正的執行緒池介面是 ExecutorService。13/04/2018 Page 57 of 283
4.1.3.1. newCachedThreadPool
建立一個可根據需要建立新執行緒的執行緒池,但是在以前構造的執行緒可用時將重用它們。對於執行
很多短期非同步任務的程式而言,這些執行緒池通常可提高程式效能。 呼叫 execute 將重用以前構造
的執行緒(如果執行緒可用)。如果現有執行緒沒有可用的,則建立一個新執行緒並新增到池中。終止並
從快取中移除那些已有 60 秒鐘未被使用的執行緒。 因此,長時間保持空閒的執行緒池不會使用任何資
源。
4.1.3.2. newFixedThreadPool
建立一個可重用固定執行緒數的執行緒池,以共享的無界佇列方式來執行這些執行緒。在任意點,在大
多數 nThreads 執行緒會處於處理任務的活動狀態。如果在所有執行緒處於活動狀態時提交附加任務,
則在有可用執行緒之前,附加任務將在佇列中等待。如果在關閉前的執行期間由於失敗而導致任何
執行緒終止,那麼一個新執行緒將代替它執行後續的任務(如果需要)。在某個執行緒被顯式地關閉之
前,池中的執行緒將一直存在。13/04/2018 Page 58 of 283
4.1.3.3. newScheduledThreadPool
建立一個執行緒池,它可安排在給定延遲後執行命令或者定期地執行。
ScheduledExecutorService scheduledThreadPool= Executors.newScheduledThreadPool(3);
scheduledThreadPool.schedule(newRunnable(){
@Override
public void run() {
System.out.println("延遲三秒");
}
}, 3, TimeUnit.SECONDS);
scheduledThreadPool.scheduleAtFixedRate(newRunnable(){
@Override
public void run() {
System.out.println("延遲 1 秒後每三秒執行一次");
}
},1,3,TimeUnit.SECONDS);
4.1.3.4. newSingleThreadExecutor
Executors.newSingleThreadExecutor()返回一個執行緒池(這個執行緒池只有一個執行緒) ,這個執行緒
池可以線上程死後(或發生異常時)重新啟動一個執行緒來替代原來的執行緒繼續執行下去!
4.1.4. 執行緒生命週期(狀態)
當執行緒被建立並啟動以後,它既不是一啟動就進入了執行狀態,也不是一直處於執行狀態。
線上程的生命週期中,它要經過新建(New)、就緒(Runnable)、執行(Running)、阻塞
(Blocked)和死亡(Dead)5 種狀態。尤其是當執行緒啟動以後,它不可能一直"霸佔"著 CPU 獨自
執行,所以 CPU 需要在多條執行緒之間切換,於是執行緒狀態也會多次在執行、阻塞之間切換
4.1.4.1. 新建狀態(NEW)
當程式使用 new 關鍵字建立了一個執行緒之後,該執行緒就處於新建狀態,此時僅由 JVM 為其分配
記憶體,並初始化其成員變數的值13/04/2018 Page 59 of 283
4.1.4.2. 就緒狀態(RUNNABLE):
當執行緒物件呼叫了 start()方法之後,該執行緒處於就緒狀態。 Java 虛擬機器會為其建立方法呼叫棧和
程式計數器,等待排程執行。
4.1.4.3. 執行狀態(RUNNING):
如果處於就緒狀態的執行緒獲得了 CPU,開始執行 run()方法的執行緒執行體,則該執行緒處於執行狀
態。
4.1.4.4. 阻塞狀態(BLOCKED):
阻塞狀態是指執行緒因為某種原因放棄了 cpu 使用權,也即讓出了 cpu timeslice,暫時停止執行。
直到執行緒進入可執行(runnable)狀態,才有機會再次獲得 cpu timeslice 轉到執行(running)狀
態。阻塞的情況分三種:
等待阻塞(o.wait->等待對列) :
執行(running)的執行緒執行 o.wait()方法, JVM 會把該執行緒放入等待佇列(waitting queue)
中。
同步阻塞(lock->鎖池)
執行(running)的執行緒在獲取物件的同步鎖時,若該同步鎖被別的執行緒佔用,則 JVM 會把該線
程放入鎖池(lock pool)中。
其他阻塞(sleep/join)
執行(running)的執行緒執行 Thread.sleep(long ms)或 t.join()方法,或者發出了 I/O 請求時,
JVM 會把該執行緒置為阻塞狀態。當 sleep()狀態超時、 join()等待執行緒終止或者超時、或者 I/O
處理完畢時,執行緒重新轉入可執行(runnable)狀態。
4.1.4.5. 執行緒死亡(DEAD)
執行緒會以下面三種方式結束,結束後就是死亡狀態。
正常結束
1. run()或 call()方法執行完成,執行緒正常結束。
異常結束
2. 執行緒丟擲一個未捕獲的 Exception 或 Error。
呼叫 stop
3. 直接呼叫該執行緒的 stop()方法來結束該執行緒—該方法通常容易導致死鎖,不推薦使用。13/04/2018 Page 60 of 283
4.1.5. 終止執行緒 4 種方式
4.1.5.1. 正常執行結束
程式執行結束,執行緒自動結束。
4.1.5.2. 使用退出標誌退出執行緒
一般 run()方法執行完,執行緒就會正常結束,然而,常常有些執行緒是伺服執行緒。它們需要長時間的
執行,只有在外部某些條件滿足的情況下,才能關閉這些執行緒。使用一個變數來控制迴圈,例如:
最直接的方法就是設一個 boolean 型別的標誌,並通過設定這個標誌為 true 或 false 來控制 while
迴圈是否退出,程式碼示例:
public class ThreadSafe extends Thread {
public volatile boolean exit = false;
public void run() {
while (!exit){
//do something
}
}
}
定義了一個退出標誌 exit,當 exit 為 true 時, while 迴圈退出, exit 的預設值為 false.在定義 exit
時,使用了一個 Java 關鍵字 volatile,這個關鍵字的目的是使 exit 同步,也就是說在同一時刻只
能由一個執行緒來修改 exit 的值。
4.1.5.3. Interrupt 方法結束執行緒
使用 interrupt()方法來中斷執行緒有兩種情況:13/04/2018 Page 61 of 283
1. 執行緒處於阻塞狀態: 如使用了 sleep,同步鎖的 wait,socket 中的 receiver,accept 等方法時,
會使執行緒處於阻塞狀態。當呼叫執行緒的 interrupt()方法時,會丟擲 InterruptException 異常。
阻塞中的那個方法丟擲這個異常,通過程式碼捕獲該異常,然後 break 跳出迴圈狀態,從而讓
我們有機會結束這個執行緒的執行。 通常很多人認為只要呼叫 interrupt 方法執行緒就會結束,實
際上是錯的, 一定要先捕獲 InterruptedException 異常之後通過 break 來跳出迴圈,才能正
常結束 run 方法。
2. 執行緒未處於阻塞狀態: 使用 isInterrupted()判斷執行緒的中斷標誌來退出迴圈。當使用
interrupt()方法時,中斷標誌就會置 true,和使用自定義的標誌來控制迴圈是一樣的道理。
public class ThreadSafe extends Thread {
public void run() {
while (!isInterrupted()){ //非阻塞過程中通過判斷中斷標誌來退出
try{
Thread.sleep(5*1000);//阻塞過程捕獲中斷異常來退出
}catch(InterruptedException e){
e.printStackTrace();
break;//捕獲到異常之後,執行 break 跳出迴圈
}
}
}
}
4.1.5.4. stop 方法終止執行緒(執行緒不安全)
程式中可以直接使用 thread.stop()來強行終止執行緒,但是 stop 方法是很危險的,就象突然關
閉計算機電源,而不是按正常程式關機一樣,可能會產生不可預料的結果,不安全主要是:
thread.stop()呼叫之後,建立子執行緒的執行緒就會丟擲 ThreadDeatherror 的錯誤,並且會釋放子
執行緒所持有的所有鎖。一般任何進行加鎖的程式碼塊,都是為了保護資料的一致性,如果在呼叫
thread.stop()後導致了該執行緒所持有的所有鎖的突然釋放(不可控制),那麼被保護資料就有可能呈
現不一致性,其他執行緒在使用這些被破壞的資料時,有可能導致一些很奇怪的應用程式錯誤。因
此,並不推薦使用 stop 方法來終止執行緒。
4.1.6. sleep 與 wait 區別
1. 對於 sleep()方法,我們首先要知道該方法是屬於 Thread 類中的。而 wait()方法,則是屬於
Object 類中的。13/04/2018 Page 62 of 283
2. sleep()方法導致了程式暫停執行指定的時間,讓出 cpu 該其他執行緒,但是他的監控狀態依然
保持者,當指定的時間到了又會自動恢復執行狀態。
3. 在呼叫 sleep()方法的過程中, 執行緒不會釋放物件鎖。
4. 而當呼叫 wait()方法的時候,執行緒會放棄物件鎖,進入等待此物件的等待鎖定池,只有針對此
物件呼叫 notify()方法後本執行緒才進入物件鎖定池準備獲取物件鎖進入執行狀態。
4.1.7. start 與 run 區別
1. start() 方法來啟動執行緒,真正實現了多執行緒執行。這時無需等待 run 方法體程式碼執行完畢,
可以直接繼續執行下面的程式碼。
2. 通過呼叫 Thread 類的 start()方法來啟動一個執行緒, 這時此執行緒是處於就緒狀態, 並沒有運
行。
3. 方法 run()稱為執行緒體,它包含了要執行的這個執行緒的內容,執行緒就進入了執行狀態,開始運
行 run 函式當中的程式碼。 Run 方法執行結束, 此執行緒終止。然後 CPU 再排程其它執行緒。
4.1.8. JAVA 後臺執行緒
1. 定義:守護執行緒--也稱“服務執行緒”, 他是後臺執行緒, 它有一個特性,即為使用者執行緒 提供 公
共服務, 在沒有使用者執行緒可服務時會自動離開。
2. 優先順序:守護執行緒的優先順序比較低,用於為系統中的其它物件和執行緒提供服務。
3. 設定:通過 setDaemon(true)來設定執行緒為“守護執行緒”;將一個使用者執行緒設定為守護執行緒
的方式是在 執行緒物件建立 之前 用執行緒物件的 setDaemon 方法。
4. 在 Daemon 執行緒中產生的新執行緒也是 Daemon 的。
5. 執行緒則是 JVM 級別的,以 Tomcat 為例,如果你在 Web 應用中啟動一個執行緒,這個執行緒的
生命週期並不會和 Web 應用程式保持同步。也就是說,即使你停止了 Web 應用,這個執行緒
依舊是活躍的。
6. example: 垃圾回收執行緒就是一個經典的守護執行緒,當我們的程式中不再有任何執行的Thread,
程式就不會再產生垃圾,垃圾回收器也就無事可做, 所以當垃圾回收執行緒是 JVM 上僅剩的線
程時,垃圾回收執行緒會自動離開。它始終在低級別的狀態中執行,用於實時監控和管理系統
中的可回收資源。
7. 生命週期:守護程序(Daemon)是執行在後臺的一種特殊程序。它獨立於控制終端並且周
期性地執行某種任務或等待處理某些發生的事件。也就是說守護執行緒不依賴於終端,但是依
賴於系統,與系統“同生共死”。當 JVM 中所有的執行緒都是守護執行緒的時候, JVM 就可以退
出了;如果還有一個或以上的非守護執行緒則 JVM 不會退出。13/04/2018 Page 63 of 283
4.1.9. JAVA 鎖
4.1.9.1. 樂觀鎖
樂觀鎖是一種樂觀思想,即認為讀多寫少,遇到併發寫的可能性低,每次去拿資料的時候都認為
別人不會修改,所以不會上鎖,但是在更新的時候會判斷一下在此期間別人有沒有去更新這個數
據,採取在寫時先讀出當前版本號,然後加鎖操作(比較跟上一次的版本號,如果一樣則更新),
如果失敗則要重複讀-比較-寫的操作。
java 中的樂觀鎖基本都是通過 CAS 操作實現的, CAS 是一種更新的原子操作, 比較當前值跟傳入
值是否一樣,一樣則更新,否則失敗。
4.1.9.2. 悲觀鎖
悲觀鎖是就是悲觀思想,即認為寫多,遇到併發寫的可能性高,每次去拿資料的時候都認為別人
會修改,所以每次在讀寫資料的時候都會上鎖,這樣別人想讀寫這個資料就會 block 直到拿到鎖。
java中的悲觀鎖就是Synchronized,AQS框架下的鎖則是先嚐試cas樂觀鎖去獲取鎖,獲取不到,
才會轉換為悲觀鎖,如 RetreenLock。
4.1.9.3. 自旋鎖
自旋鎖原理非常簡單, 如果持有鎖的執行緒能在很短時間內釋放鎖資源,那麼那些等待競爭鎖
的執行緒就不需要做核心態和使用者態之間的切換進入阻塞掛起狀態,它們只需要等一等(自旋),
等持有鎖的執行緒釋放鎖後即可立即獲取鎖,這樣就避免使用者執行緒和核心的切換的消耗。
執行緒自旋是需要消耗 cup 的,說白了就是讓 cup 在做無用功,如果一直獲取不到鎖,那執行緒
也不能一直佔用 cup 自旋做無用功,所以需要設定一個自旋等待的最大時間。
如果持有鎖的執行緒執行的時間超過自旋等待的最大時間扔沒有釋放鎖,就會導致其它爭用鎖
的執行緒在最大等待時間內還是獲取不到鎖,這時爭用執行緒會停止自旋進入阻塞狀態。
自旋鎖的優缺點
自旋鎖儘可能的減少執行緒的阻塞,這對於鎖的競爭不激烈,且佔用鎖時間非常短的程式碼塊來
說效能能大幅度的提升,因為自旋的消耗會小於執行緒阻塞掛起再喚醒的操作的消耗,這些操作會
導致執行緒發生兩次上下文切換!
但是如果鎖的競爭激烈,或者持有鎖的執行緒需要長時間佔用鎖執行同步塊,這時候就不適合
使用自旋鎖了,因為自旋鎖在獲取鎖前一直都是佔用 cpu 做無用功,佔著 XX 不 XX,同時有大量
執行緒在競爭一個鎖,會導致獲取鎖的時間很長,執行緒自旋的消耗大於執行緒阻塞掛起操作的消耗,
其它需要 cup 的執行緒又不能獲取到 cpu,造成 cpu 的浪費。所以這種情況下我們要關閉自旋鎖;
自旋鎖時間閾值(1.6 引入了適應性自旋鎖)
自旋鎖的目的是為了佔著 CPU 的資源不釋放,等到獲取到鎖立即進行處理。但是如何去選擇
自旋的執行時間呢?如果自旋執行時間太長,會有大量的執行緒處於自旋狀態佔用 CPU 資源,進而
會影響整體系統的效能。因此自旋的週期選的額外重要!13/04/2018 Page 64 of 283
JVM 對於自旋週期的選擇, jdk1.5 這個限度是一定的寫死的, 在 1.6 引入了適應性自旋鎖,適應
性自旋鎖意味著自旋的時間不在是固定的了,而是由前一次在同一個鎖上的自旋時間以及鎖的擁
有者的狀態來決定,基本認為一個執行緒上下文切換的時間是最佳的一個時間,同時 JVM 還針對當
前 CPU 的負荷情況做了較多的優化, 如果平均負載小於 CPUs 則一直自旋, 如果有超過(CPUs/2)
個執行緒正在自旋,則後來執行緒直接阻塞, 如果正在自旋的執行緒發現 Owner 發生了變化則延遲自旋
時間(自旋計數)或進入阻塞, 如果 CPU 處於節電模式則停止自旋, 自旋時間的最壞情況是 CPU
的儲存延遲(CPU A 儲存了一個數據,到 CPU B 得知這個資料直接的時間差) , 自旋時會適當放
棄執行緒優先順序之間的差異。
自旋鎖的開啟
JDK1.6 中-XX:+UseSpinning 開啟;
-XX:PreBlockSpin=10 為自旋次數;
JDK1.7 後,去掉此引數,由 jvm 控制;
4.1.9.4. Synchronized 同步鎖
synchronized 它可以把任意一個非 NULL 的物件當作鎖。 他屬於獨佔式的悲觀鎖,同時屬於可重
入鎖。
Synchronized 作用範圍
1. 作用於方法時,鎖住的是物件的例項(this);
2. 當作用於靜態方法時,鎖住的是Class例項,又因為Class的相關資料儲存在永久帶PermGen
(jdk1.8 則是 metaspace),永久帶是全域性共享的,因此靜態方法鎖相當於類的一個全域性鎖,
會鎖所有呼叫該方法的執行緒;
3. synchronized 作用於一個物件例項時,鎖住的是所有以該物件為鎖的程式碼塊。 它有多個佇列,
當多個執行緒一起訪問某個物件監視器的時候,物件監視器會將這些執行緒儲存在不同的容器中。
Synchronized 核心元件
1) Wait Set:哪些呼叫 wait 方法被阻塞的執行緒被放置在這裡;
2) Contention List: 競爭佇列,所有請求鎖的執行緒首先被放在這個競爭佇列中;
3) Entry List: Contention List 中那些有資格成為候選資源的執行緒被移動到 Entry List 中;
4) OnDeck:任意時刻, 最多隻有一個執行緒正在競爭鎖資源,該執行緒被成為 OnDeck;
5) Owner:當前已經獲取到所資源的執行緒被稱為 Owner;
6) !Owner:當前釋放鎖的執行緒。
Synchronized 實現13/04/2018 Page 65 of 283
1. JVM 每次從佇列的尾部取出一個數據用於鎖競爭候選者(OnDeck),但是併發情況下,
ContentionList 會被大量的併發執行緒進行 CAS 訪問,為了降低對尾部元素的競爭, JVM 會將
一部分執行緒移動到 EntryList 中作為候選競爭執行緒。
2. Owner 執行緒會在 unlock 時,將 ContentionList 中的部分執行緒遷移到 EntryList 中,並指定
EntryList 中的某個執行緒為 OnDeck 執行緒(一般是最先進去的那個執行緒)。
3. Owner 執行緒並不直接把鎖傳遞給 OnDeck 執行緒,而是把鎖競爭的權利交給 OnDeck,
OnDeck 需要重新競爭鎖。這樣雖然犧牲了一些公平性,但是能極大的提升系統的吞吐量,在
JVM 中,也把這種選擇行為稱之為“競爭切換”。
4. OnDeck 執行緒獲取到鎖資源後會變為 Owner 執行緒,而沒有得到鎖資源的仍然停留在 EntryList
中。如果 Owner 執行緒被 wait 方法阻塞,則轉移到 WaitSet 佇列中,直到某個時刻通過 notify
或者 notifyAll 喚醒,會重新進去 EntryList 中。
5. 處於 ContentionList、 EntryList、 WaitSet 中的執行緒都處於阻塞狀態,該阻塞是由作業系統
來完成的(Linux 核心下采用 pthread_mutex_lock 核心函式實現的)。
6. Synchronized 是非公平鎖。 Synchronized 線上程進入 ContentionList 時, 等待的執行緒會先
嘗試自旋獲取鎖,如果獲取不到就進入 ContentionList,這明顯對於已經進入佇列的執行緒是
不公平的,還有一個不公平的事情就是自旋獲取鎖的執行緒還可能直接搶佔 OnDeck 執行緒的鎖
資源。
參考: https://blog.csdn.net/zqz_zqz/article/details/70233767
7. 每個物件都有個 monitor 物件, 加鎖就是在競爭 monitor 物件,程式碼塊加鎖是在前後分別加
上 monitorenter 和 monitorexit 指令來實現的,方法加鎖是通過一個標記位來判斷的
8. synchronized 是一個重量級操作,需要呼叫作業系統相關介面,效能是低效的,有可能給線
程加鎖消耗的時間比有用操作消耗的時間更多。
9. Java1.6, synchronized 進行了很多的優化, 有適應自旋、鎖消除、鎖粗化、輕量級鎖及偏向
鎖等,效率有了本質上的提高。在之後推出的 Java1.7 與 1.8 中,均對該關鍵字的實現機理做
了優化。引入了偏向鎖和輕量級鎖。都是在物件頭中有標記位,不需要經過作業系統加鎖。
10. 鎖可以從偏向鎖升級到輕量級鎖,再升級到重量級鎖。這種升級過程叫做鎖膨脹;
11. JDK 1.6 中預設是開啟偏向鎖和輕量級鎖,可以通過-XX:-UseBiasedLocking 來禁用偏向鎖。13/04/2018 Page 66 of 283
4.1.9.5. ReentrantLock
ReentantLock 繼承介面 Lock 並實現了介面中定義的方法, 他是一種可重入鎖, 除了能完
成 synchronized 所能完成的所有工作外,還提供了諸如可響應中斷鎖、可輪詢鎖請求、定時鎖等
避免多執行緒死鎖的方法。
Lock 介面的主要方法
1. void lock(): 執行此方法時, 如果鎖處於空閒狀態, 當前執行緒將獲取到鎖. 相反, 如果鎖已經
被其他執行緒持有, 將禁用當前執行緒, 直到當前執行緒獲取到鎖.
2. boolean tryLock(): 如果鎖可用, 則獲取鎖, 並立即返回 true, 否則返回 false. 該方法和
lock()的區別在於, tryLock()只是"試圖"獲取鎖, 如果鎖不可用, 不會導致當前執行緒被禁用,
當前執行緒仍然繼續往下執行程式碼. 而 lock()方法則是一定要獲取到鎖, 如果鎖不可用, 就一
直等待, 在未獲得鎖之前,當前執行緒並不繼續向下執行.
3. void unlock():執行此方法時, 當前執行緒將釋放持有的鎖. 鎖只能由持有者釋放, 如果執行緒
並不持有鎖, 卻執行該方法, 可能導致異常的發生.
4. Condition newCondition(): 條件物件,獲取等待通知元件。該元件和當前的鎖繫結,
當前執行緒只有獲取了鎖,才能呼叫該元件的 await()方法,而呼叫後,當前執行緒將縮放鎖。
5. getHoldCount() : 查詢當前執行緒保持此鎖的次數,也就是執行此執行緒執行 lock 方法的次
數。
6. getQueueLength() : 返回正等待獲取此鎖的執行緒估計數,比如啟動 10 個執行緒, 1 個
執行緒獲得鎖,此時返回的是 9
7. getWaitQueueLength: (Condition condition)返回等待與此鎖相關的給定條件的線
程估計數。比如 10 個執行緒,用同一個 condition 物件,並且此時這 10 個執行緒都執行了
condition 物件的 await 方法,那麼此時執行此方法返回 10
8. hasWaiters(Condition condition): 查詢是否有執行緒等待與此鎖有關的給定條件
(condition),對於指定 contidion 物件,有多少執行緒執行了 condition.await 方法
9. hasQueuedThread(Thread thread): 查詢給定執行緒是否等待獲取此鎖
10. hasQueuedThreads(): 是否有執行緒等待此鎖
11. isFair(): 該鎖是否公平鎖
12. isHeldByCurrentThread(): 當前執行緒是否保持鎖鎖定,執行緒的執行 lock 方法的前後分
別是 false 和 true
13. isLock(): 此鎖是否有任意執行緒佔用
14. lockInterruptibly() : 如果當前執行緒未被中斷,獲取鎖
15. tryLock() : 嘗試獲得鎖,僅在呼叫時鎖未被執行緒佔用,獲得鎖
16. tryLock(long timeout TimeUnit unit): 如果鎖在給定等待時間內沒有被另一個執行緒保持,
則獲取該鎖。
非公平鎖
JVM 按隨機、就近原則分配鎖的機制則稱為不公平鎖, ReentrantLock 在建構函式中提供了
是否公平鎖的初始化方式,預設為非公平鎖。 非公平鎖實際執行的效率要遠遠超出公平鎖,除非
程式有特殊需要,否則最常用非公平鎖的分配機制。13/04/2018 Page 67 of 283
公平鎖
公平鎖指的是鎖的分配機制是公平的,通常先對鎖提出獲取請求的執行緒會先被分配到鎖,
ReentrantLock 在建構函式中提供了是否公平鎖的初始化方式來定義公平鎖。
ReentrantLock 與 synchronized
1. ReentrantLock 通過方法 lock()與 unlock()來進行加鎖與解鎖操作,與 synchronized 會
被 JVM 自動解鎖機制不同, ReentrantLock 加鎖後需要手動進行解鎖。為了避免程式出
現異常而無法正常解鎖的情況,使用 ReentrantLock 必須在 finally 控制塊中進行解鎖操
作。
2. ReentrantLock 相比 synchronized 的優勢是可中斷、公平鎖、多個鎖。這種情況下需要
使用 ReentrantLock。
ReentrantLock 實現
public class MyService {
private Lock lock = new ReentrantLock();
//Lock lock=new ReentrantLock(true);//公平鎖
//Lock lock=new ReentrantLock(false);//非公平鎖
private Condition condition=lock.newCondition();//建立 Condition
public void testMethod() {
try {
lock.lock();//lock 加鎖
//1: wait 方法等待:
//System.out.println("開始 wait");
condition.await();
//通過建立 Condition 物件來使執行緒 wait,必須先執行 lock.lock 方法獲得鎖
//:2: signal 方法喚醒
condition.signal();//condition 物件的 signal 方法可以喚醒 wait 執行緒
for (int i = 0; i < 5; i++) {
System.out.println("ThreadName=" + Thread.currentThread().getName()+ (" " + (i + 1)));
}
} catch (InterruptedException e) {
e.printStackTrace();
}
finally13/04/2018 Page 68 of 283
{
lock.unlock();
}
}
}
Condition 類和 Object 類鎖方法區別區別
1. Condition 類的 awiat 方法和 Object 類的 wait 方法等效
2. Condition 類的 signal 方法和 Object 類的 notify 方法等效
3. Condition 類的 signalAll 方法和 Object 類的 notifyAll 方法等效
4. ReentrantLock 類可以喚醒指定條件的執行緒,而 object 的喚醒是隨機的
tryLock 和 lock 和 lockInterruptibly 的區別
1. tryLock 能獲得鎖就返回 true,不能就立即返回 false, tryLock(long timeout,TimeUnit
unit),可以增加時間限制,如果超過該時間段還沒獲得鎖,返回 false
2. lock 能獲得鎖就返回 true,不能的話一直等待獲得鎖
3. lock 和 lockInterruptibly,如果兩個執行緒分別執行這兩個方法,但此時中斷這兩個執行緒,
lock 不會丟擲異常,而 lockInterruptibly 會丟擲異常。
4.1.9.6. Semaphore 訊號量
Semaphore 是一種基於計數的訊號量。它可以設定一個閾值,基於此,多個執行緒競爭獲取許可信
號,做完自己的申請後歸還,超過閾值後,執行緒申請許可訊號將會被阻塞。 Semaphore 可以用來
構建一些物件池,資源池之類的, 比如資料庫連線池
實現互斥鎖(計數器為 1)
我們也可以建立計數為 1 的 Semaphore,將其作為一種類似互斥鎖的機制,這也叫二元訊號量,
表示兩種互斥狀態。
程式碼實現
它的用法如下:
// 建立一個計數閾值為 5 的訊號量物件
// 只能 5 個執行緒同時訪問
Semaphore semp = new Semaphore(5);
try { // 申請許可
semp.acquire();
try {
// 業務邏輯13/04/2018 Page 69 of 283
} catch (Exception e) {
} finally {
// 釋放許可
semp.release();
}
} catch (InterruptedException e) {
}
Semaphore 與 ReentrantLock
Semaphore 基本能完成 ReentrantLock 的所有工作,使用方法也與之類似,通過 acquire()與
release()方法來獲得和釋放臨界資源。經實測, Semaphone.acquire()方法預設為可響應中斷鎖,
與 ReentrantLock.lockInterruptibly()作用效果一致,也就是說在等待臨界資源的過程中可以被
Thread.interrupt()方法中斷。
此外, Semaphore 也實現了可輪詢的鎖請求與定時鎖的功能,除了方法名 tryAcquire 與 tryLock
不同,其使用方法與 ReentrantLock 幾乎一致。 Semaphore 也提供了公平與非公平鎖的機制,也
可在建構函式中進行設定。
Semaphore 的鎖釋放操作也由手動進行,因此與 ReentrantLock 一樣,為避免執行緒因丟擲異常而
無法正常釋放鎖的情況發生,釋放鎖的操作也必須在 finally 程式碼塊中完成。
4.1.9.7. AtomicInteger
首先說明,此處 AtomicInteger ,一個提供原子操作的 Integer 的類,常見的還有
AtomicBoolean、 AtomicInteger、 AtomicLong、 AtomicReference 等,他們的實現原理相同,
區別在與運算物件型別的不同。令人興奮地,還可以通過 AtomicReference<V>將一個物件的所
有操作轉化成原子操作。
我們知道, 在多執行緒程式中,諸如++i 或 i++等運算不具有原子性,是不安全的執行緒操作之一。
通常我們會使用 synchronized 將該操作變成一個原子操作,但 JVM 為此類操作特意提供了一些
同步類,使得使用更方便,且使程式執行效率變得更高。通過相關資料顯示,通常AtomicInteger
的效能是 ReentantLock 的好幾倍。
4.1.9.8. 可重入鎖(遞迴鎖)
本文裡面講的是廣義上的可重入鎖,而不是單指 JAVA 下的 ReentrantLock。 可重入鎖,也叫
做遞迴鎖,指的是同一執行緒 外層函式獲得鎖之後 ,內層遞迴函式仍然有獲取該鎖的程式碼,但不受
影響。在 JAVA 環境下 ReentrantLock 和 synchronized 都是 可重入鎖。13/04/2018 Page 70 of 283
4.1.9.9. 公平鎖與非公平鎖
公平鎖(Fair)
加鎖前檢查是否有排隊等待的執行緒,優先排隊等待的執行緒,先來先得
非公平鎖(Nonfair)
加鎖時不考慮排隊等待問題,直接嘗試獲取鎖,獲取不到自動到隊尾等待
1. 非公平鎖效能比公平鎖高 5~10 倍,因為公平鎖需要在多核的情況下維護一個佇列
2. Java 中的 synchronized 是非公平鎖, ReentrantLock 預設的 lock()方法採用的是非公平鎖。
4.1.9.10. ReadWriteLock 讀寫鎖
為了提高效能, Java 提供了讀寫鎖,在讀的地方使用讀鎖,在寫的地方使用寫鎖,靈活控制,如
果沒有寫鎖的情況下,讀是無阻塞的,在一定程度上提高了程式的執行效率。 讀寫鎖分為讀鎖和寫
鎖,多個讀鎖不互斥,讀鎖與寫鎖互斥,這是由 jvm 自己控制的,你只要上好相應的鎖即可。
讀鎖
如果你的程式碼只讀資料,可以很多人同時讀,但不能同時寫,那就上讀鎖
寫鎖
如果你的程式碼修改資料,只能有一個人在寫,且不能同時讀取,那就上寫鎖。總之,讀的時候上
讀鎖,寫的時候上寫鎖!
Java 中 讀 寫 鎖 有 個 接 口 java.util.concurrent.locks.ReadWriteLock , 也 有 具 體 的 實 現
ReentrantReadWriteLock。
4.1.9.11. 共享鎖和獨佔鎖
java 併發包提供的加鎖模式分為獨佔鎖和共享鎖。
獨佔鎖
獨佔鎖模式下,每次只能有一個執行緒能持有鎖, ReentrantLock 就是以獨佔方式實現的互斥鎖。
獨佔鎖是一種悲觀保守的加鎖策略,它避免了讀/讀衝突,如果某個只讀執行緒獲取鎖,則其他讀線
程都只能等待,這種情況下就限制了不必要的併發性,因為讀操作並不會影響資料的一致性。
共享鎖
共享鎖則允許多個執行緒同時獲取鎖,併發訪問 共享資源,如: ReadWriteLock。 共享鎖則是一種
樂觀鎖,它放寬了加鎖策略,允許多個執行讀操作的執行緒同時訪問共享資源。
1. AQS 的內部類 Node 定義了兩個常量 SHARED 和 EXCLUSIVE,他們分別標識 AQS 佇列中等
待執行緒的鎖獲取模式。
2. java 的併發包中提供了 ReadWriteLock,讀-寫鎖。它允許一個資源可以被多個讀操作訪問,
或者被一個 寫操作訪問,但兩者不能同時進行。13/04/2018 Page 71 of 283
4.1.9.12. 重量級鎖(Mutex Lock)
Synchronized 是通過物件內部的一個叫做監視器鎖(monitor)來實現的。但是監視器鎖本質又
是依賴於底層的作業系統的 Mutex Lock 來實現的。而作業系統實現執行緒之間的切換這就需要從用
戶態轉換到核心態,這個成本非常高,狀態之間的轉換需要相對比較長的時間,這就是為什麼
Synchronized 效率低的原因。因此, 這種依賴於作業系統 Mutex Lock 所實現的鎖我們稱之為
“重量級鎖” 。 JDK 中對 Synchronized 做的種種優化,其核心都是為了減少這種重量級鎖的使用。
JDK1.6 以後,為了減少獲得鎖和釋放鎖所帶來的效能消耗,提高效能,引入了“輕量級鎖”和
“偏向鎖”。
4.1.9.13. 輕量級鎖
鎖的狀態總共有四種:無鎖狀態、偏向鎖、輕量級鎖和重量級鎖。
鎖升級
隨著鎖的競爭,鎖可以從偏向鎖升級到輕量級鎖,再升級的重量級鎖(但是鎖的升級是單向的,
也就是說只能從低到高升級,不會出現鎖的降級)。
“輕量級” 是相對於使用作業系統互斥量來實現的傳統鎖而言的。但是,首先需要強調一點的是,
輕量級鎖並不是用來代替重量級鎖的,它的本意是在沒有多執行緒競爭的前提下,減少傳統的重量
級鎖使用產生的效能消耗。在解釋輕量級鎖的執行過程之前, 先明白一點,輕量級鎖所適應的場
景是執行緒交替執行同步塊的情況,如果存在同一時間訪問同一鎖的情況,就會導致輕量級鎖膨脹
為重量級鎖。
4.1.9.14. 偏向鎖
Hotspot 的作者經過以往的研究發現大多數情況下鎖不僅不存在多執行緒競爭,而且總是由同一線
程多次獲得。 偏向鎖的目的是在某個執行緒獲得鎖之後,消除這個執行緒鎖重入(CAS)的開銷,看起
來讓這個執行緒得到了偏護。 引入偏向鎖是為了在無多執行緒競爭的情況下儘量減少不必要的輕量級
鎖執行路徑,因為輕量級鎖的獲取及釋放依賴多次 CAS 原子指令, 而偏向鎖只需要在置換
ThreadID 的時候依賴一次 CAS 原子指令(由於一旦出現多執行緒競爭的情況就必須撤銷偏向鎖,所
以偏向鎖的撤銷操作的效能損耗必須小於節省下來的 CAS 原子指令的效能消耗)。上面說過, 輕
量級鎖是為了線上程交替執行同步塊時提高效能, 而偏向鎖則是在只有一個執行緒執行同步塊時進
一步提高效能。
4.1.9.15. 分段鎖
分段鎖也並非一種實際的鎖,而是一種思想 ConcurrentHashMap 是學習分段鎖的最好實踐
4.1.9.16. 鎖優化13/04/2018 Page 72 of 283
減少鎖持有時間
只用在有執行緒安全要求的程式上加鎖
減小鎖粒度
將大物件(這個物件可能會被很多執行緒訪問),拆成小物件,大大增加並行度,降低鎖競爭。
降低了鎖的競爭,偏向鎖,輕量級鎖成功率才會提高。最最典型的減小鎖粒度的案例就是
ConcurrentHashMap。
鎖分離
最常見的鎖分離就是讀寫鎖 ReadWriteLock,根據功能進行分離成讀鎖和寫鎖,這樣讀讀不互
斥,讀寫互斥,寫寫互斥,即保證了執行緒安全,又提高了效能,具體也請檢視[高併發 Java 五]
JDK 併發包 1。讀寫分離思想可以延伸,只要操作互不影響,鎖就可以分離。比如
LinkedBlockingQueue 從頭部取出,從尾部放資料
鎖粗化
通常情況下,為了保證多執行緒間的有效併發,會要求每個執行緒持有鎖的時間儘量短,即在使用完
公共資源後,應該立即釋放鎖。但是,凡事都有一個度, 如果對同一個鎖不停的進行請求、同步
和釋放,其本身也會消耗系統寶貴的資源,反而不利於效能的優化 。
鎖消除
鎖消除是在編譯器級別的事情。 在即時編譯器時,如果發現不可能被共享的物件,則可以消除這
些物件的鎖操作,多數是因為程式設計師編碼不規範引起。
參考: https://www.jianshu.com/p/39628e1180a9
4.1.10. 執行緒基本方法
執行緒相關的基本方法有 wait, notify, notifyAll, sleep, join, yield 等。13/04/2018 Page 73 of 283
4.1.10.1. 執行緒等待(wait)
呼叫該方法的執行緒進入 WAITING 狀態,只有等待另外執行緒的通知或被中斷才會返回,需要注意的
是呼叫 wait()方法後, 會釋放物件的鎖。因此, wait 方法一般用在同步方法或同步程式碼塊中。
4.1.10.2. 執行緒睡眠(sleep)
sleep 導致當前執行緒休眠,與 wait 方法不同的是 sleep 不會釋放當前佔有的鎖,sleep(long)會導致
執行緒進入 TIMED-WATING 狀態,而 wait()方法會導致當前執行緒進入 WATING 狀態
4.1.10.3. 執行緒讓步(yield)
yield 會使當前執行緒讓出 CPU 執行時間片,與其他執行緒一起重新競爭 CPU 時間片。一般情況下,
優先順序高的執行緒有更大的可能性成功競爭得到 CPU 時間片, 但這又不是絕對的,有的作業系統對
執行緒優先順序並不敏感。
4.1.10.4. 執行緒中斷(interrupt)
中斷一個執行緒,其本意是給這個執行緒一個通知訊號,會影響這個執行緒內部的一箇中斷標識位。 這
個執行緒本身並不會因此而改變狀態(如阻塞,終止等)。
1. 呼叫 interrupt()方法並不會中斷一個正在執行的執行緒。也就是說處於 Running 狀態的線
程並不會因為被中斷而被終止,僅僅改變了內部維護的中斷標識位而已。
2. 若呼叫 sleep()而使執行緒處於 TIMED-WATING 狀態,這時呼叫 interrupt()方法,會丟擲
InterruptedException,從而使執行緒提前結束 TIMED-WATING 狀態。13/04/2018 Page 74 of 283
3. 許多宣告丟擲 InterruptedException 的方法(如 Thread.sleep(long mills 方法)),丟擲異
常前,都會清除中斷標識位,所以丟擲異常後,呼叫 isInterrupted()方法將會返回 false。
4. 中斷狀態是執行緒固有的一個標識位,可以通過此標識位安全的終止執行緒。比如,你想終止
一個執行緒 thread 的時候,可以呼叫 thread.interrupt()方法,線上程的 run 方法內部可以
根據 thread.isInterrupted()的值來優雅的終止執行緒。
4.1.10.5. Join 等待其他執行緒終止
join() 方法,等待其他執行緒終止,在當前執行緒中呼叫一個執行緒的 join() 方法,則當前執行緒轉為阻塞
狀態,回到另一個執行緒結束,當前執行緒再由阻塞狀態變為就緒狀態,等待 cpu 的寵幸。
4.1.10.6. 為什麼要用 join()方法?
很多情況下,主執行緒生成並啟動了子執行緒,需要用到子執行緒返回的結果,也就是需要主執行緒需要
在子執行緒結束後再結束,這時候就要用到 join() 方法。
System.out.println(Thread.currentThread().getName() + "執行緒執行開始!");
Thread6 thread1 = new Thread6();
thread1.setName("執行緒 B");
thread1.join();
System.out.println("這時 thread1 執行完畢之後才能執行主執行緒");
4.1.10.7. 執行緒喚醒(notify)
Object 類中的 notify() 方法, 喚醒在此物件監視器上等待的單個執行緒,如果所有執行緒都在此物件
上等待,則會選擇喚醒其中一個執行緒,選擇是任意的,並在對實現做出決定時發生,執行緒通過調
用其中一個 wait() 方法,在物件的監視器上等待, 直到當前的執行緒放棄此物件上的鎖定,才能繼
續執行被喚醒的執行緒,被喚醒的執行緒將以常規方式與在該物件上主動同步的其他所有執行緒進行競
爭。類似的方法還有 notifyAll() ,喚醒再次監視器上等待的所有執行緒。
4.1.10.8. 其他方法:
1. sleep():強迫一個執行緒睡眠N毫秒。
2. isAlive(): 判斷一個執行緒是否存活。
3. join(): 等待執行緒終止。
4. activeCount(): 程式中活躍的執行緒數。
5. enumerate(): 列舉程式中的執行緒。
6. currentThread(): 得到當前執行緒。
7. isDaemon(): 一個執行緒是否為守護執行緒。
8. setDaemon(): 設定一個執行緒為守護執行緒。 (使用者執行緒和守護執行緒的區別在於,是否等待主線
程依賴於主執行緒結束而結束)
9. setName(): 為執行緒設定一個名稱。
10. wait(): 強迫一個執行緒等待。13/04/2018 Page 75 of 283
11. notify(): 通知一個執行緒繼續執行。
12. setPriority(): 設定一個執行緒的優先順序。
13. getPriority()::獲得一個執行緒的優先順序。
4.1.11. 執行緒上下文切換
巧妙地利用了時間片輪轉的方式, CPU 給每個任務都服務一定的時間,然後把當前任務的狀態儲存
下來,在載入下一任務的狀態後,繼續服務下一任務, 任務的狀態儲存及再載入, 這段過程就叫做
上下文切換。時間片輪轉的方式使多個任務在同一顆 CPU 上執行變成了可能。
4.1.11.1. 程序
(有時候也稱做任務)是指一個程式執行的例項。在 Linux 系統中,執行緒就是能並行執行並且
與他們的父程序(建立他們的程序)共享同一地址空間(一段記憶體區域)和其他資源的輕量
級的程序。
4.1.11.2. 上下文
是指某一時間點 CPU 暫存器和程式計數器的內容。
4.1.11.3. 暫存器
是 CPU 內部的數量較少但是速度很快的記憶體(與之對應的是 CPU 外部相對較慢的 RAM 主內
存)。暫存器通過對常用值(通常是運算的中間值)的快速訪問來提高計算機程式執行的速
度。
4.1.11.4. 程式計數器
是一個專用的暫存器, 用於表明指令序列中 CPU 正在執行的位置,存的值為正在執行的指令
的位置或者下一個將要被執行的指令的位置,具體依賴於特定的系統。
4.1.11.5. PCB-“切換楨”
上下文切換可以認為是核心(作業系統的核心)在 CPU 上對於程序(包括執行緒)進行切換,上下
文切換過程中的資訊是儲存在程序控制塊(PCB, process control block)中的。 PCB 還經常被稱
作“切換楨”(switchframe)。 資訊會一直儲存到 CPU 的記憶體中,直到他們被再次使用。13/04/2018 Page 76 of 283
4.1.11.6. 上下文切換的活動:
1. 掛起一個程序,將這個程序在 CPU 中的狀態(上下文)儲存於記憶體中的某處。
2. 在記憶體中檢索下一個程序的上下文並將其在 CPU 的暫存器中恢復。
3. 跳轉到程式計數器所指向的位置(即跳轉到程序被中斷時的程式碼行),以恢復該程序在程式
中。
4.1.11.7. 引起執行緒上下文切換的原因
1. 當前執行任務的時間片用完之後,系統 CPU 正常排程下一個任務;
2. 當前執行任務碰到 IO 阻塞,排程器將此任務掛起,繼續下一任務;
3. 多個任務搶佔鎖資源,當前任務沒有搶到鎖資源,被排程器掛起,繼續下一任務;
4. 使用者程式碼掛起當前任務,讓出 CPU 時間;
5. 硬體中斷;
4.1.12. 同步鎖與死鎖
4.1.12.1. 同步鎖
當多個執行緒同時訪問同一個資料時,很容易出現問題。為了避免這種情況出現,我們要保證執行緒
同步互斥,就是指併發執行的多個執行緒,在同一時間內只允許一個執行緒訪問共享資料。 Java 中可
以使用 synchronized 關鍵字來取得一個物件的同步鎖。
4.1.12.2. 死鎖
何為死鎖,就是多個執行緒同時被阻塞,它們中的一個或者全部都在等待某個資源被釋放。
4.1.13. 執行緒池原理
執行緒池做的工作主要是控制執行的執行緒的數量,處理過程中將任務放入佇列,然後線上程建立後
啟動這些任務,如果執行緒數量超過了最大數量超出數量的執行緒排隊等候,等其它執行緒執行完畢,
再從佇列中取出任務來執行。 他的主要特點為: 執行緒複用; 控制最大併發數; 管理執行緒。
4.1.13.1. 執行緒複用
每一個 Thread 的類都有一個 start 方法。 當呼叫 start 啟動執行緒時 Java 虛擬機器會呼叫該類的 run
方法。 那麼該類的 run() 方法中就是呼叫了 Runnable 物件的 run() 方法。 我們可以繼承重寫
Thread 類,在其 start 方法中新增不斷迴圈呼叫傳遞過來的 Runnable 物件。 這就是執行緒池的實
現原理。 迴圈方法中不斷獲取 Runnable 是用 Queue 實現的,在獲取下一個 Runnable 之前可以
是阻塞的。
4.1.13.2. 執行緒池的組成
一般的執行緒池主要分為以下 4 個組成部分:13/04/2018 Page 77 of 283
1. 執行緒池管理器:用於建立並管理執行緒池
2. 工作執行緒:執行緒池中的執行緒
3. 任務介面:每個任務必須實現的介面,用於工作執行緒排程其執行
4. 任務佇列:用於存放待處理的任務,提供一種緩衝機制
Java 中的執行緒池是通過 Executor 框架實現的,該框架中用到了 Executor, Executors,
ExecutorService, ThreadPoolExecutor , Callable 和 Future、 FutureTask 這幾個類。
ThreadPoolExecutor 的構造方法如下:
public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize, long keepAliveTime,
TimeUnit unit, BlockingQueue<Runnable> workQueue) {
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
Executors.defaultThreadFactory(), defaultHandler);
}
1. corePoolSize:指定了執行緒池中的執行緒數量。
2. maximumPoolSize:指定了執行緒池中的最大執行緒數量。
3. keepAliveTime:當前執行緒池數量超過 corePoolSize 時,多餘的空閒執行緒的存活時間,即多
次時間內會被銷燬。
4. unit: keepAliveTime 的單位。
5. workQueue:任務佇列,被提交但尚未被執行的任務。
6. threadFactory:執行緒工廠,用於建立執行緒,一般用預設的即可。
7. handler:拒絕策略,當任務太多來不及處理,如何拒絕任務。13/04/2018 Page 78 of 283
4.1.13.3. 拒絕策略
執行緒池中的執行緒已經用完了,無法繼續為新任務服務,同時,等待佇列也已經排滿了,再也
塞不下新任務了。這時候我們就需要拒絕策略機制合理的處理這個問題。
JDK 內建的拒絕策略如下:
1. AbortPolicy : 直接丟擲異常,阻止系統正常執行。
2. CallerRunsPolicy : 只要執行緒池未關閉,該策略直接在呼叫者執行緒中,運行當前被丟棄的
任務。顯然這樣做不會真的丟棄任務,但是,任務提交執行緒的效能極有可能會急劇下降。
3. DiscardOldestPolicy : 丟棄最老的一個請求,也就是即將被執行的一個任務,並嘗試再
次提交當前任務。
4. DiscardPolicy : 該策略默默地丟棄無法處理的任務,不予任何處理。如果允許任務丟
失,這是最好的一種方案。
以上內建拒絕策略均實現了 RejectedExecutionHandler 介面,若以上策略仍無法滿足實際
需要,完全可以自己擴充套件 RejectedExecutionHandler 介面。
4.1.13.4. Java 執行緒池工作過程
1. 執行緒池剛建立時,裡面沒有一個執行緒。任務佇列是作為引數傳進來的。不過,就算佇列裡面
有任務,執行緒池也不會馬上執行它們。
2. 當呼叫 execute() 方法新增一個任務時,執行緒池會做如下判斷:
a) 如果正在執行的執行緒數量小於 corePoolSize,那麼馬上建立執行緒執行這個任務;
b) 如果正在執行的執行緒數量大於或等於 corePoolSize,那麼將這個任務放入佇列;
c) 如果這時候佇列滿了,而且正在執行的執行緒數量小於 maximumPoolSize,那麼還是要
建立非核心執行緒立刻執行這個任務;
d) 如果佇列滿了,而且正在執行的執行緒數量大於或等於 maximumPoolSize,那麼執行緒池
會丟擲異常 RejectExecutionException。
3. 當一個執行緒完成任務時,它會從佇列中取下一個任務來執行。
4. 當一個執行緒無事可做,超過一定的時間(keepAliveTime)時,執行緒池會判斷,如果當前運
行的執行緒數大於 corePoolSize,那麼這個執行緒就被停掉。所以執行緒池的所有任務完成後,它
最終會收縮到 corePoolSize 的大小。13/04/2018 Page 79 of 283
4.1.14. JAVA 阻塞佇列原理
阻塞佇列,關鍵字是阻塞,先理解阻塞的含義,在阻塞佇列中,執行緒阻塞有這樣的兩種情況:
1. 當佇列中沒有資料的情況下,消費者端的所有執行緒都會被自動阻塞(掛起),直到有資料放
入佇列。
2. 當佇列中填滿資料的情況下,生產者端的所有執行緒都會被自動阻塞(掛起),直到佇列中有
空的位置,執行緒被自動喚醒。13/04/2018 Page 80 of 283
4.1.14.1. 阻塞佇列的主要方法
丟擲異常:丟擲一個異常;
特殊值:返回一個特殊值(null 或 false,視情況而定)
則塞:在成功操作之前,一直阻塞執行緒
超時:放棄前只在最大的時間內阻塞
插入操作:
1: public abstract boolean add(E paramE): 將指定元素插入此佇列中(如果立即可行
且不會違反容量限制),成功時返回 true,如果當前沒有可用的空間,則拋
出 IllegalStateException。如果該元素是 NULL,則會丟擲 NullPointerException 異常。
2: public abstract boolean offer(E paramE): 將指定元素插入此佇列中(如果立即可行
且不會違反容量限制),成功時返回 true,如果當前沒有可用的空間,則返回 false。
3: public abstract void put(E paramE) throws InterruptedException: 將指定元素插
入此佇列中,將等待可用的空間(如果有必要)
public void put(E paramE) throws InterruptedException {
checkNotNull(paramE);
ReentrantLock localReentrantLock = this.lock;
localReentrantLock.lockInterruptibly();
try {
while (this.count == this.items.length)
this.notFull.await();//如果佇列滿了,則執行緒阻塞等待
enqueue(paramE);13/04/2018 Page 81 of 283
localReentrantLock.unlock();
} finally {
localReentrantLock.unlock();
}
}
4: offer(E o, long timeout, TimeUnit unit): 可以設定等待的時間, 如果在指定的時間
內, 還不能往佇列中加入 BlockingQueue, 則返回失敗。
獲取資料操作:
1: poll(time):取走 BlockingQueue 裡排在首位的物件,若不能立即取出,則可以等 time 引數
規定的時間,取不到時返回 null;
2: poll(long timeout, TimeUnit unit): 從 BlockingQueue 取出一個隊首的物件, 如果在
指定時間內, 佇列一旦有資料可取, 則立即返回佇列中的資料。否則直到時間超時還沒有數
據可取,返回失敗。
3: take():取走 BlockingQueue 裡排在首位的物件,若 BlockingQueue 為空,阻斷進入等待狀
態直到 BlockingQueue 有新的資料被加入。
4.drainTo():一次性從 BlockingQueue 獲取所有可用的資料物件(還可以指定獲取資料的個
數),通過該方法,可以提升獲取資料效率;不需要多次分批加鎖或釋放鎖。
4.1.14.2. Java 中的阻塞佇列
1. ArrayBlockingQueue :由陣列結構組成的有界阻塞佇列。
2. LinkedBlockingQueue :由連結串列結構組成的有界阻塞佇列。
3. PriorityBlockingQueue :支援優先順序排序的無界阻塞佇列。
4. DelayQueue:使用優先順序佇列實現的無界阻塞佇列。
5. SynchronousQueue:不儲存元素的阻塞佇列。
6. LinkedTransferQueue:由連結串列結構組成的無界阻塞佇列。
7. LinkedBlockingDeque:由連結串列結構組成的雙向阻塞佇列13/04/2018 Page 82 of 283
4.1.14.3. ArrayBlockingQueue(公平、非公平)
用陣列實現的有界阻塞佇列。此佇列按照先進先出(FIFO)的原則對元素進行排序。 預設情況下
不保證訪問者公平的訪問佇列,所謂公平訪問佇列是指阻塞的所有生產者執行緒或消費者執行緒,當
佇列可用時,可以按照阻塞的先後順序訪問佇列,即先阻塞的生產者執行緒,可以先往佇列裡插入
元素,先阻塞的消費者執行緒,可以先從佇列裡獲取元素。通常情況下為了保證公平性會降低吞吐
量。我們可以使用以下程式碼建立一個公平的阻塞佇列:
ArrayBlockingQueue fairQueue = new ArrayBlockingQueue(1000,true);
4.1.14.4. LinkedBlockingQueue(兩個獨立鎖提高併發)
基於連結串列的阻塞佇列,同 ArrayListBlockingQueue 類似,此佇列按照先進先出(FIFO)的原則對
元素進行排序。而 LinkedBlockingQueue 之所以能夠高效的處理併發資料,還因為其對於生產者
端和消費者端分別採用了獨立的鎖來控制資料同步,這也意味著在高併發的情況下生產者和消費
者可以並行地操作佇列中的資料,以此來提高整個佇列的併發效能。
LinkedBlockingQueue 會預設一個類似無限大小的容量(Integer.MAX_VALUE)。
4.1.14.5. PriorityBlockingQueue(compareTo 排序實現優先)
是一個支援優先順序的無界佇列。預設情況下元素採取自然順序升序排列。 可以自定義實現
compareTo()方法來指定元素進行排序規則,或者初始化 PriorityBlockingQueue 時,指定構造
引數 Comparator 來對元素進行排序。需要注意的是不能保證同優先順序元素的順序。
4.1.14.6. DelayQueue(快取失效、定時任務 )
是一個支援延時獲取元素的無界阻塞佇列。佇列使用 PriorityQueue 來實現。佇列中的元素必須實
現 Delayed 介面,在建立元素時可以指定多久才能從佇列中獲取當前元素。只有在延遲期滿時才
能從佇列中提取元素。我們可以將 DelayQueue 運用在以下應用場景:
1. 快取系統的設計:可以用 DelayQueue 儲存快取元素的有效期,使用一個執行緒迴圈查詢
DelayQueue,一旦能從 DelayQueue 中獲取元素時,表示快取有效期到了。13/04/2018 Page 83 of 283
2. 定 時 任 務 調 度 : 使 用 DelayQueue 保 存 當 天 將 會 執 行 的 任 務 和 執 行 時 間 , 一 旦 從
DelayQueue 中獲取到任務就開始執行,從比如 TimerQueue 就是使用 DelayQueue 實現的。
4.1.14.7. SynchronousQueue(不儲存資料、可用於傳遞資料)
是一個不儲存元素的阻塞佇列。每一個 put 操作必須等待一個 take 操作,否則不能繼續新增元素。
SynchronousQueue 可以看成是一個傳球手,負責把生產者執行緒處理的資料直接傳遞給消費者線
程。佇列本身並不儲存任何元素,非常適合於傳遞性場景,比如在一個執行緒中使用的資料,傳遞給
另 外 一 個 線 程 使 用 , SynchronousQueue 的 吞 吐 量 高 於 LinkedBlockingQueue 和
ArrayBlockingQueue。
4.1.14.8. LinkedTransferQueue
是 一 個 由 鏈 表 結 構 組 成 的 無 界 阻 塞 TransferQueue 隊 列 。 相 對 於 其 他 阻 塞 隊 列 ,
LinkedTransferQueue 多了 tryTransfer 和 transfer 方法。
1. transfer 方法: 如果當前有消費者正在等待接收元素(消費者使用 take()方法或帶時間限制的
poll()方法時), transfer 方法可以把生產者傳入的元素立刻 transfer(傳輸)給消費者。如
果沒有消費者在等待接收元素, transfer 方法會將元素存放在佇列的 tail 節點,並等到該元素
被消費者消費了才返回。
2. tryTransfer 方法。則是用來試探下生產者傳入的元素是否能直接傳給消費者。如果沒有消費
者等待接收元素,則返回 false。和 transfer 方法的區別是 tryTransfer 方法無論消費者是否
接收,方法立即返回。而 transfer 方法是必須等到消費者消費了才返回。
對於帶有時間限制的 tryTransfer(E e, long timeout, TimeUnit unit)方法,則是試圖把生產者傳
入的元素直接傳給消費者,但是如果沒有消費者消費該元素則等待指定的時間再返回,如果超時
還沒消費元素,則返回 false,如果在超時時間內消費了元素,則返回 true。
4.1.14.9. LinkedBlockingDeque
是一個由連結串列結構組成的雙向阻塞佇列。所謂雙向佇列指的你可以從佇列的兩端插入和移出元素。
雙端佇列因為多了一個操作佇列的入口,在多執行緒同時入隊時,也就減少了一半的競爭。相比其
他的阻塞佇列 , LinkedBlockingDeque 多了 addFirst, addLast, offerFirst, offerLast,
peekFirst, peekLast 等方法,以 First 單詞結尾的方法,表示插入,獲取(peek)或移除雙端隊
列的第一個元素。以 Last 單詞結尾的方法,表示插入,獲取或移除雙端佇列的最後一個元素。另
外插入方法 add 等同於 addLast,移除方法 remove 等效於 removeFirst。但是 take 方法卻等同
於 takeFirst,不知道是不是 Jdk 的 bug,使用時還是用帶有 First 和 Last 字尾的方法更清楚。
在初始化 LinkedBlockingDeque 時可以設定容量防止其過渡膨脹。另外雙向阻塞佇列可以運用在
“工作竊取”模式中。13/04/2018 Page 84 of 283
4.1.15. CyclicBarrier、 CountDownLatch、 Semaphore 的用法
4.1.15.1. CountDownLatch(執行緒計數器 )
CountDownLatch 類位於 java.util.concurrent 包下,利用它可以實現類似計數器的功能。比如有
一個任務 A,它要等待其他 4 個任務執行完畢之後才能執行,此時就可以利用 CountDownLatch
來實現這種功能了。
final CountDownLatch latch = new CountDownLatch(2);
new Thread(){public void run() {
System.out.println("子執行緒"+Thread.currentThread().getName()+"正在執行");
Thread.sleep(3000);
System.out.println("子執行緒"+Thread.currentThread().getName()+"執行完畢");
latch.countDown();
};}.start();
new Thread(){ public void run() {
System.out.println("子執行緒"+Thread.currentThread().getName()+"正在執行");
Thread.sleep(3000);
System.out.println("子執行緒"+Thread.currentThread().getName()+"執行完畢");
latch.countDown();
};}.start();
System.out.println("等待 2 個子執行緒執行完畢...");
latch.await();
System.out.println("2 個子執行緒已經執行完畢");
System.out.println("繼續執行主執行緒");
}
4.1.15.2. CyclicBarrier(迴環柵欄-等待至 barrier 狀態再全部同時執行)
字面意思迴環柵欄,通過它可以實現讓一組執行緒等待至某個狀態之後再全部同時執行。叫做迴環
是因為當所有等待執行緒都被釋放以後, CyclicBarrier 可以被重用。我們暫且把這個狀態就叫做
barrier,當呼叫 await()方法之後,執行緒就處於 barrier 了。
CyclicBarrier 中最重要的方法就是 await 方法,它有 2 個過載版本:
1. public int await(): 用來掛起當前執行緒,直至所有執行緒都到達 barrier 狀態再同時執行後續任
務;
2. public int await(long timeout, TimeUnit unit): 讓這些執行緒等待至一定的時間,如果還有
執行緒沒有到達 barrier 狀態就直接讓到達 barrier 的執行緒執行後續任務。13/04/2018 Page 85 of 283
具體使用如下, 另外 CyclicBarrier 是可以重用的。
public static void main(String[] args) {
int N = 4;
CyclicBarrier barrier = new CyclicBarrier(N);
for(int i=0;i<N;i++)
new Writer(barrier).start();
}
static class Writer extends Thread{
private CyclicBarrier cyclicBarrier;
public Writer(CyclicBarrier cyclicBarrier) {
this.cyclicBarrier = cyclicBarrier;
}
@Override
public void run() {
try {
Thread.sleep(5000); //以睡眠來模擬執行緒需要預定寫入資料操作
System.out.println("執行緒"+Thread.currentThread().getName()+"寫入資料完
畢,等待其他執行緒寫入完畢");
cyclicBarrier.await();
} catch (InterruptedException e) {
e.printStackTrace();
}catch(BrokenBarrierException e){
e.printStackTrace();
}
System.out.println("所有執行緒寫入完畢,繼續處理其他任務,比如資料操作");
}
}
4.1.15.3. Semaphore(訊號量-控制同時訪問的執行緒個數)
Semaphore 翻譯成字面意思為 訊號量, Semaphore 可以控制同時訪問的執行緒個數, 通過
acquire() 獲取一個許可,如果沒有就等待,而 release() 釋放一個許可。
Semaphore 類中比較重要的幾個方法:
1. public void acquire(): 用來獲取一個許可,若無許可能夠獲得,則會一直等待,直到獲得許
可。
2. public void acquire(int permits):獲取 permits 個許可
3. public void release() { } :釋放許可。注意,在釋放許可之前,必須先獲獲得許可。
4. public void release(int permits) { }:釋放 permits 個許可
上面 4 個方法都會被阻塞,如果想立即得到執行結果,可以使用下面幾個方法13/04/2018 Page 86 of 283
1. public boolean tryAcquire():嘗試獲取一個許可,若獲取成功,則立即返回 true,若獲取失
敗,則立即返回 false
2. public boolean tryAcquire(long timeout, TimeUnit unit):嘗試獲取一個許可,若在指定的
時間內獲取成功,則立即返回 true,否則則立即返回 false
3. public boolean tryAcquire(int permits):嘗試獲取 permits 個許可,若獲取成功,則立即返
回 true,若獲取失敗,則立即返回 false
4. public boolean tryAcquire(int permits, long timeout, TimeUnit unit): 嘗試獲取 permits
個許可,若在指定的時間內獲取成功,則立即返回 true,否則則立即返回 false
5. 還可以通過 availablePermits()方法得到可用的許可數目。
例子:若一個工廠有 5 臺機器,但是有 8 個工人,一臺機器同時只能被一個工人使用,只有使用完
了,其他工人才能繼續使用。那麼我們就可以通過 Semaphore 來實現:
int N = 8; //工人數
Semaphore semaphore = new Semaphore(5); //機器數目
for(int i=0;i<N;i++)
new Worker(i,semaphore).start();
}
static class Worker extends Thread{
private int num;
private Semaphore semaphore;
public Worker(int num,Semaphore semaphore){
this.num = num;
this.semaphore = semaphore;
}
@Override
public void run() {
try {
semaphore.acquire();
System.out.println("工人"+this.num+"佔用一個機器在生產...");
Thread.sleep(2000);
System.out.println("工人"+this.num+"釋放出機器");
semaphore.release();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
CountDownLatch 和 CyclicBarrier 都能夠實現執行緒之間的等待,只不過它們側重點不
同; CountDownLatch 一般用於某個執行緒 A 等待若干個其他執行緒執行完任務之後,它才13/04/2018 Page 87 of 283
執行; 而 CyclicBarrier 一般用於一組執行緒互相等待至某個狀態,然後這一組執行緒再同時
執行;另外, CountDownLatch 是不能夠重用的,而 CyclicBarrier 是可以重用的。
Semaphore 其實和鎖有點類似,它一般用於控制對某組資源的訪問許可權。
4.1.16. volatile 關鍵字的作用(變數可見性、禁止重排序)
Java 語言提供了一種稍弱的同步機制,即 volatile 變數,用來確保將變數的更新操作通知到其他
執行緒。 volatile 變數具備兩種特性, volatile 變數不會被快取在暫存器或者對其他處理器不可見的
地方,因此在讀取 volatile 型別的變數時總會返回最新寫入的值。
變數可見性
其一是保證該變數對所有執行緒可見,這裡的可見性指的是當一個執行緒修改了變數的值,那麼新的
值對於其他執行緒是可以立即獲取的。
禁止重排序
volatile 禁止了指令重排。
比 sychronized 更輕量級的同步鎖
在訪問 volatile 變數時不會執行加鎖操作,因此也就不會使執行執行緒阻塞,因此 volatile 變數是一
種比 sychronized 關鍵字更輕量級的同步機制。 volatile 適合這種場景:一個變數被多個執行緒共
享,執行緒直接給這個變數賦值。
當對非 volatile 變數進行讀寫的時候,每個執行緒先從記憶體拷貝變數到 CPU 快取中。如果計算機有
多個 CPU,每個執行緒可能在不同的 CPU 上被處理,這意味著每個執行緒可以拷貝到不同的 CPU
cache 中。而宣告變數是 volatile 的, JVM 保證了每次讀變數都從記憶體中讀,跳過 CPU cache
這一步。
適用場景
值得說明的是對 volatile 變數的單次讀/寫操作可以保證原子性的,如 long 和 double 型別變數,
但是並不能保證 i++這種操作的原子性,因為本質上 i++是讀、寫兩次操作。在某些場景下可以
代替 Synchronized。但是,volatile 的不能完全取代 Synchronized 的位置,只有在一些特殊的場13/04/2018 Page 88 of 283
景下,才能適用 volatile。總的來說,必須同時滿足下面兩個條件才能保證在併發環境的執行緒安
全:
(1)對變數的寫操作不依賴於當前值(比如 i++),或者說是單純的變數賦值(boolean
flag = true) 。
(2)該變數沒有包含在具有其他變數的不變式中, 也就是說,不同的 volatile 變數之間,不
能互相依賴。 只有在狀態真正獨立於程式內其他內容時才能使用 volatile。
4.1.17. 如何在兩個執行緒之間共享資料
Java 裡面進行多執行緒通訊的主要方式就是共享記憶體的方式,共享記憶體主要的關注點有兩個:可見
性和有序性原子性。 Java 記憶體模型(JMM)解決了可見性和有序性的問題,而鎖解決了原子性的
問題, 理想情況下我們希望做到“同步”和“互斥”。 有以下常規實現方法:
將資料抽象成一個類,並將資料的操作作為這個類的方法
1. 將資料抽象成一個類,並將對這個資料的操作作為這個類的方法,這麼設計可以和容易做到
同步,只要在方法上加” synchronized“
public class MyData {
private int j=0;
public synchronized void add(){
j++;
System.out.println("執行緒"+Thread.currentThread().getName()+"j 為: "+j);
}
public synchronized void dec(){
j--;
System.out.println("執行緒"+Thread.currentThread().getName()+"j 為: "+j);
}
public int getData(){
return j;
}
}
public class AddRunnable implements Runnable{
MyData data;
public AddRunnable(MyData data){
this.data= data;
}13/04/2018 Page 89 of 283
public void run() {
data.add();
}
}
public class DecRunnable implements Runnable {
MyData data;
public DecRunnable(MyData data){
this.data = data;
}
public void run() {
data.dec();
}
}
public static void main(String[] args) {
MyData data = new MyData();
Runnable add = new AddRunnable(data);
Runnable dec = new DecRunnable(data);
for(int i=0;i<2;i++){
new Thread(add).start();
new Thread(dec).start();
}
Runnable 物件作為一個類的內部類
2. 將 Runnable 物件作為一個類的內部類,共享資料作為這個類的成員變數,每個執行緒對共享數
據的操作方法也封裝在外部類,以便實現對資料的各個操作的同步和互斥,作為內部類的各
個 Runnable 物件呼叫外部類的這些方法。
public class MyData {
private int j=0;
public synchronized void add(){
j++;
System.out.println("執行緒"+Thread.currentThread().getName()+"j 為: "+j);
}
public synchronized void dec(){
j--;
System.out.println("執行緒"+Thread.currentThread().getName()+"j 為: "+j);
}
public int getData(){
return j;13/04/2018 Page 90 of 283
}
}
public class TestThread {
public static void main(String[] args) {
final MyData data = new MyData();
for(int i=0;i<2;i++){
new Thread(new Runnable(){
public void run() {
data.add();
}
}).start();
new Thread(new Runnable(){
public void run() {
data.dec();
}
}).start();
}
}
}
4.1.18. ThreadLocal 作用(執行緒本地儲存)
ThreadLocal,很多地方叫做執行緒本地變數,也有些地方叫做執行緒本地儲存, ThreadLocal 的作用
是提供執行緒內的區域性變數, 這種變數線上程的生命週期內起作用, 減少同一個執行緒內多個函式或
者元件之間一些公共變數的傳遞的複雜度。
ThreadLocalMap(執行緒的一個屬性)
1. 每個執行緒中都有一個自己的 ThreadLocalMap 類物件,可以將執行緒自己的物件保持到其中,
各管各的,執行緒可以正確的訪問到自己的物件。
2. 將一個共用的 ThreadLocal 靜態例項作為 key,將不同物件的引用儲存到不同執行緒的
ThreadLocalMap 中,然後線上程執行的各處通過這個靜態 ThreadLocal 例項的 get()方法取
得自己執行緒儲存的那個物件,避免了將這個物件作為引數傳遞的麻煩。
3. ThreadLocalMap 其實就是執行緒裡面的一個屬性,它在 Thread 類中定義
ThreadLocal.ThreadLocalMap threadLocals = null;13/04/2018 Page 91 of 283
使用場景
最常見的 ThreadLocal 使用場景為 用來解決 資料庫連線、 Session 管理等。
private static final ThreadLocal threadSession = new ThreadLocal();
public static Session getSession() throws InfrastructureException {
Session s = (Session) threadSession.get();
try {
if (s == null) {
s = getSessionFactory().openSession();
threadSession.set(s);
}
} catch (HibernateException ex) {
throw new InfrastructureException(ex);
}
return s;
}
4.1.19. synchronized 和 ReentrantLock 的區別
4.1.19.1. 兩者的共同點:
1. 都是用來協調多執行緒對共享物件、變數的訪問
2. 都是可重入鎖,同一執行緒可以多次獲得同一個鎖
3. 都保證了可見性和互斥性13/04/2018 Page 92 of 283
4.1.19.2. 兩者的不同點:
1. ReentrantLock 顯示的獲得、釋放鎖, synchronized 隱式獲得釋放鎖
2. ReentrantLock 可響應中斷、可輪迴, synchronized 是不可以響應中斷的,為處理鎖的
不可用性提供了更高的靈活性
3. ReentrantLock 是 API 級別的, synchronized 是 JVM 級別的
4. ReentrantLock 可以實現公平鎖
5. ReentrantLock 通過 Condition 可以繫結多個條件
6. 底層實現不一樣, synchronized 是同步阻塞,使用的是悲觀併發策略, lock 是同步非阻
塞,採用的是樂觀併發策略
7. Lock 是一個介面,而 synchronized 是 Java 中的關鍵字, synchronized 是內建的語言
實現。
8. synchronized 在發生異常時,會自動釋放執行緒佔有的鎖,因此不會導致死鎖現象發生;
而 Lock 在發生異常時,如果沒有主動通過 unLock()去釋放鎖,則很可能造成死鎖現象,
因此使用 Lock 時需要在 finally 塊中釋放鎖。
9. Lock 可以讓等待鎖的執行緒響應中斷,而 synchronized 卻不行,使用 synchronized 時,
等待的執行緒會一直等待下去,不能夠響應中斷。
10. 通過 Lock 可以知道有沒有成功獲取鎖,而 synchronized 卻無法辦到。
11. Lock 可以提高多個執行緒進行讀操作的效率,既就是實現讀寫鎖等。
4.1.20. ConcurrentHashMap 併發
4.1.20.1. 減小鎖粒度
減小鎖粒度是指縮小鎖定物件的範圍,從而減小鎖衝突的可能性,從而提高系統的併發能力。減
小鎖粒度是一種削弱多執行緒鎖競爭的有效手段, 這種技術典型的應用是 ConcurrentHashMap(高
效能的 HashMap)類的實現。對於 HashMap 而言,最重要的兩個方法是 get 與 set 方法,如果我
們對整個 HashMap 加鎖,可以得到執行緒安全的物件,但是加鎖粒度太大。 Segment 的大小也被
稱為 ConcurrentHashMap 的併發度。
4.1.20.2. ConcurrentHashMap 分段鎖
ConcurrentHashMap,它內部細分了若干個小的 HashMap,稱之為段(Segment)。 預設情況下
一個 ConcurrentHashMap 被進一步細分為 16 個段,既就是鎖的併發度。
如果需要在 ConcurrentHashMap 中新增一個新的表項,並不是將整個 HashMap 加鎖,而是首
先根據 hashcode 得到該表項應該存放在哪個段中,然後對該段加鎖,並完成 put 操作。在多執行緒
環境中,如果多個執行緒同時進行 put操作,只要被加入的表項不存放在同一個段中,則執行緒間可以
做到真正的並行。13/04/2018 Page 93 of 283
ConcurrentHashMap 是由 Segment 陣列結構和 HashEntry 陣列結構組成
ConcurrentHashMap 是由 Segment 陣列結構和 HashEntry 陣列結構組成。 Segment 是一種可
重入鎖 ReentrantLock,在 ConcurrentHashMap 裡扮演鎖的角色, HashEntry 則用於儲存鍵值
對資料。一個 ConcurrentHashMap 裡包含一個 Segment 陣列, Segment 的結構和 HashMap
類似,是一種陣列和連結串列結構, 一個 Segment 裡包含一個 HashEntry 陣列,每個 HashEntry 是
一個連結串列結構的元素, 每個 Segment 守護一個 HashEntry 數組裡的元素,當對 HashEntry 陣列的
資料進行修改時,必須首先獲得它對應的 Segment 鎖。
4.1.21. Java 中用到的執行緒排程
4.1.21.1. 搶佔式排程:
搶佔式排程指的是每條執行緒執行的時間、執行緒的切換都由系統控制,系統控制指的是在系統某種
執行機制下,可能每條執行緒都分同樣的執行時間片,也可能是某些執行緒執行的時間片較長,甚至
某些執行緒得不到執行的時間片。在這種機制下,一個執行緒的堵塞不會導致整個程序堵塞。
4.1.21.2. 協同式排程:
協同式排程指某一執行緒執行完後主動通知系統切換到另一執行緒上執行,這種模式就像接力賽一樣,
一個人跑完自己的路程就把接力棒交接給下一個人,下個人繼續往下跑。執行緒的執行時間由執行緒
本身控制,執行緒切換可以預知,不存在多執行緒同步問題,但它有一個致命弱點:如果一個執行緒編
寫有問題,執行到一半就一直堵塞,那麼可能導致整個系統崩潰。13/04/2018 Page 94 of 283
4.1.21.3. JVM 的執行緒排程實現(搶佔式排程)
java 使用的執行緒調使用搶佔式排程, Java 中執行緒會按優先順序分配 CPU 時間片執行, 且優先順序越高
越優先執行,但優先順序高並不代表能獨自佔用執行時間片,可能是優先順序高得到越多的執行時間
片,反之,優先順序低的分到的執行時間少但不會分配不到執行時間。
4.1.21.4. 執行緒讓出 cpu 的情況:
1. 當前執行執行緒主動放棄 CPU, JVM 暫時放棄 CPU 操作(基於時間片輪轉排程的 JVM 操作系
統不會讓執行緒永久放棄 CPU,或者說放棄本次時間片的執行權),例如呼叫 yield()方法。
2. 當前執行執行緒因為某些原因進入阻塞狀態,例如阻塞在 I/O 上。
3. 當前執行執行緒結束,即執行完 run()方法裡面的任務。
4.1.22. 程序排程演算法
4.1.22.1. 優先排程演算法
1. 先來先服務排程演算法(FCFS)
當在作業排程中採用該演算法時,每次排程都是從後備作業佇列中選擇一個或多個最先進入該隊
列的作業,將它們調入記憶體,為它們分配資源、建立程序,然後放入就緒佇列。在程序排程中採
用 FCFS 演算法時,則每次排程是從就緒佇列中選擇一個最先進入該佇列的程序,為之分配處理機,13/04/2018 Page 95 of 283
使之投入執行。該程序一直執行到完成或發生某事件而阻塞後才放棄處理機, 特點是:演算法比較
簡單,可以實現基本上的公平。
2. 短作業(程序)優先排程演算法
短作業優先(SJF)的排程演算法是從後備佇列中選擇一個或若干個估計執行時間最短的作業,將它們
調入記憶體執行。而短程序優先(SPF)排程演算法則是從就緒佇列中選出一個估計執行時間最短的程序,
將處理機分配給它,使它立即執行並一直執行到完成,或發生某事件而被阻塞放棄處理機時再重
新排程。 該演算法未照顧緊迫型作業。
4.1.22.2. 高優先權優先排程演算法
為了照顧緊迫型作業,使之在進入系統後便獲得優先處理,引入了最高優先權優先(FPF)排程
演算法。當把該演算法用於作業排程時,系統將從後備佇列中選擇若干個優先權最高的作業裝入記憶體。
當用於程序排程時,該演算法是把處理機分配給就緒佇列中優先權最高的程序。
1. 非搶佔式優先權演算法
在這種方式下,系統一旦把處理機分配給就緒佇列中優先權最高的程序後,該程序便一直執行下
去,直至完成;或因發生某事件使該程序放棄處理機時。這種排程演算法主要用於批處理系統中;
也可用於某些對實時性要求不嚴的實時系統中。
2. 搶佔式優先權排程演算法
在這種方式下,系統同樣是把處理機分配給優先權最高的程序,使之執行。 但在其執行期間,只
要又出現了另一個其優先權更高的程序,程序排程程式就立即停止當前程序(原優先權最高的程序)
的執行,重新將處理機分配給新到的優先權最高的程序。顯然,這種搶佔式的優先權排程演算法能
更好地滿足緊迫作業的要求,故而常用於要求比較嚴格的實時系統中,以及對效能要求較高的批
處理和分時系統中。
2.高響應比優先排程演算法
在批處理系統中,短作業優先演算法是一種比較好的演算法,其主要的不足之處是長作業的執行
得不到保證。如果我們能為每個作業引入前面所述的動態優先權,並使作業的優先順序隨著等待時
間的增加而以速率 a 提高,則長作業在等待一定的時間後,必然有機會分配到處理機。該優先權的
變化規律可描述為:
(1) 如果作業的等待時間相同,則要求服務的時間愈短,其優先權愈高,因而該演算法有利於
短作業。
(2) 當要求服務的時間相同時,作業的優先權決定於其等待時間,等待時間愈長,其優先權
愈高,因而它實現的是先來先服務。13/04/2018 Page 96 of 283
(3) 對於長作業,作業的優先順序可以隨等待時間的增加而提高,當其等待時間足夠長時,其
優先順序便可升到很高,從而也可獲得處理機。簡言之,該演算法既照顧了短作業,又考慮了作業到
達的先後次序,不會使長作業長期得不到服務。因此,該演算法實現了一種較好的折衷。當然,在
利用該演算法時,每要進行排程之前,都須先做響應比的計算,這會增加系統開銷。
4.1.22.3. 基於時間片的輪轉排程演算法
1. 時間片輪轉法
在早期的時間片輪轉法中,系統將所有的就緒程序按先來先服務的原則排成一個佇列,每次排程
時,把 CPU 分配給隊首程序,並令其執行一個時間片。時間片的大小從幾 ms 到幾百 ms。 當執行
的時間片用完時,由一個計時器發出時鐘中斷請求,排程程式便據此訊號來停止該程序的執行,
並將它送往就緒佇列的末尾;然後,再把處理機分配給就緒佇列中新的隊首程序,同時也讓它執
行一個時間片。 這樣就可以保證就緒佇列中的所有程序在一給定的時間內均能獲得一時間片的處
理機執行時間。
2. 多級反饋佇列排程演算法
(1) 應設定多個就緒佇列,併為各個佇列賦予不同的優先順序。第一個佇列的優先順序最高,第二
個佇列次之,其餘各佇列的優先權逐個降低。該演算法賦予各個佇列中程序執行時間片的大小也各
不相同,在優先權愈高的佇列中,為每個程序所規定的執行時間片就愈小。例如,第二個佇列的
時間片要比第一個佇列的時間片長一倍,……,第 i+1 個佇列的時間片要比第 i 個佇列的時間片長
一倍。
(2) 當一個新程序進入記憶體後,首先將它放入第一佇列的末尾,按 FCFS 原則排隊等待排程。當
輪到該程序執行時,如它能在該時間片內完成,便可準備撤離系統;如果它在一個時間片結束時
尚未完成,排程程式便將該程序轉入第二佇列的末尾,再同樣地按 FCFS 原則等待排程執行;如果
它在第二佇列中執行一個時間片後仍未完成,再依次將它放入第三佇列,……,如此下去,當一個
長作業(程序)從第一佇列依次降到第 n 佇列後,在第 n 佇列便採取按時間片輪轉的方式執行。
(3) 僅當第一佇列空閒時,排程程式才排程第二佇列中的程序執行;僅當第 1~ (i-1)佇列均空時,
才會排程第 i 佇列中的程序執行。如果處理機正在第 i 佇列中為某程序服務時,又有新程序進入優
先權較高的佇列(第 1~ (i-1)中的任何一個佇列),則此時新程序將搶佔正在執行程序的處理機,即
由排程程式把正在執行的程序放回到第 i 佇列的末尾,把處理機分配給新到的高優先權程序。
在多級反饋佇列排程演算法中,如果規定第一個佇列的時間片略大於多數人機互動所需之處理時間
時,便能夠較好的滿足各種型別使用者的需要。
4.1.23. 什麼是 CAS(比較並交換-樂觀鎖機制-鎖自旋)
4.1.23.1. 概念及特性
CAS(Compare And Swap/Set)比較並交換, CAS 演算法的過程是這樣:它包含 3 個引數
CAS(V,E,N)。 V 表示要更新的變數(記憶體值), E 表示預期值(舊的), N 表示新值。當且僅當 V 值等13/04/2018 Page 97 of 283
於 E 值時,才會將 V 的值設為 N,如果 V 值和 E 值不同,則說明已經有其他執行緒做了更新,則當
前執行緒什麼都不做。最後, CAS 返回當前 V 的真實值。
CAS 操作是抱著樂觀的態度進行的(樂觀鎖),它總是認為自己可以成功完成操作。 當多個執行緒同時
使用 CAS 操作一個變數時,只有一個會勝出,併成功更新,其餘均會失敗。失敗的執行緒不會被掛
起,僅是被告知失敗,並且允許再次嘗試,當然也允許失敗的執行緒放棄操作。基於這樣的原理,
CAS 操作即使沒有鎖,也可以發現其他執行緒對當前執行緒的干擾,並進行恰當的處理。
4.1.23.2. 原子包 java.util.concurrent.atomic(鎖自旋)
JDK1.5 的原子包: java.util.concurrent.atomic 這個包裡面提供了一組原子類。其基本的特性就
是在多執行緒環境下,當有多個執行緒同時執行這些類的例項包含的方法時,具有排他性, 即當某個
執行緒進入方法,執行其中的指令時,不會被其他執行緒打斷,而別的執行緒就像自旋鎖一樣,一直等
到該方法執行完成,才由 JVM 從等待佇列中選擇一個另一個執行緒進入,這只是一種邏輯上的理解。
相對於對於 synchronized 這種阻塞演算法, CAS 是非阻塞演算法的一種常見實現。 由於一般 CPU 切
換時間比 CPU 指令集操作更加長, 所以 J.U.C 在效能上有了很大的提升。如下程式碼:
public class AtomicInteger extends Number implements java.io.Serializable {
private volatile int value;
public final int get() {
return value;
}
public final int getAndIncrement() {
for (;;) { //CAS 自旋,一直嘗試,直達成功
int current = get();
int next = current + 1;
if (compareAndSet(current, next))
return current;
}
}
public final boolean compareAndSet(int expect, int update) {
return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
}
}13/04/2018 Page 98 of 283
getAndIncrement 採用了 CAS 操作,每次從記憶體中讀取資料然後將此資料和+1 後的結果進行
CAS 操作,如果成功就返回結果,否則重試直到成功為止。而 compareAndSet 利用 JNI 來完成
CPU 指令的操作。
4.1.23.3. ABA 問題
CAS 會導致“ABA 問題”。 CAS 演算法實現一個重要前提需要取出記憶體中某時刻的資料,而在下時
刻比較並替換,那麼在這個時間差類會導致資料的變化。
比如說一個執行緒 one 從記憶體位置 V 中取出 A,這時候另一個執行緒 two 也從記憶體中取出 A,並且
two 進行了一些操作變成了 B,然後 two 又將 V 位置的資料變成 A,這時候執行緒 one 進行 CAS 操
作發現記憶體中仍然是 A,然後 one 操作成功。儘管執行緒 one 的 CAS 操作成功,但是不代表這個過
程就是沒有問題的。
部分樂觀鎖的實現是通過版本號(version)的方式來解決 ABA 問題,樂觀鎖每次在執行資料的修
改操作時,都會帶上一個版本號,一旦版本號和資料的版本號一致就可以執行修改操作並對版本
號執行+1 操作,否則就執行失敗。因為每次操作的版本號都會隨之增加,所以不會出現 ABA 問
題,因為版本號只會增加不會減少。
4.1.24. 什麼是 AQS(抽象的佇列同步器)
AbstractQueuedSynchronizer 類如其名,抽象的佇列式的同步器, AQS 定義了一套多執行緒訪問
共享資源的同步器框架,許多同步類實現都依賴於它,如常用的
ReentrantLock/Semaphore/CountDownLatch。13/04/2018 Page 99 of 283
它維護了一個 volatile int state(代表共享資源)和一個 FIFO 執行緒等待佇列(多執行緒爭用資源被
阻塞時會進入此佇列)。這裡 volatile 是核心關鍵詞,具體 volatile 的語義,在此不述。 state 的
訪問方式有三種:
getState()
setState()
compareAndSetState()
AQS 定義兩種資源共享方式
Exclusive 獨佔資源-ReentrantLock
Exclusive(獨佔,只有一個執行緒能執行,如 ReentrantLock)
Share 共享資源-Semaphore/CountDownLatch
Share(共享,多個執行緒可同時執行,如 Semaphore/CountDownLatch)。
AQS 只是一個框架,具體資源的獲取/釋放方式交由自定義同步器去實現, AQS 這裡只定義了一個
介面,具體資源的獲取交由自定義同步器去實現了(通過 state 的 get/set/CAS)之所以沒有定義成
abstract ,是 因 為獨 佔模 式 下 只 用實現 tryAcquire-tryRelease ,而 共享 模 式 下 只用 實 現
tryAcquireShared-tryReleaseShared。如果都定義成abstract,那麼每個模式也要去實現另一模
式下的介面。不同的自定義同步器爭用共享資源的方式也不同。自定義同步器在實現時只需要實
現共享資源 state 的獲取與釋放方式即可,至於具體執行緒等待佇列的維護(如獲取資源失敗入隊/
喚醒出隊等), AQS 已經在頂層實現好了。自定義同步器實現時主要實現以下幾種方法:
1. isHeldExclusively():該執行緒是否正在獨佔資源。只有用到 condition 才需要去實現它。
2. tryAcquire(int):獨佔方式。嘗試獲取資源,成功則返回 true,失敗則返回 false。
3. tryRelease(int):獨佔方式。嘗試釋放資源,成功則返回 true,失敗則返回 false。
4. tryAcquireShared(int):共享方式。嘗試獲取資源。負數表示失敗; 0 表示成功,但沒有剩餘
可用資源;正數表示成功,且有剩餘資源。
5. tryReleaseShared(int):共享方式。嘗試釋放資源,如果釋放後允許喚醒後續等待結點返回
true,否則返回 false。13/04/2018 Page 100 of 283
同步器的實現是 ABS 核心(state 資源狀態計數)
同步器的實現是 ABS 核心,以 ReentrantLock 為例, state 初始化為 0,表示未鎖定狀態。 A 執行緒
lock()時,會呼叫 tryAcquire()獨佔該鎖並將 state+1。此後,其他執行緒再 tryAcquire()時就會失
敗,直到 A 執行緒 unlock()到 state=0(即釋放鎖)為止,其它執行緒才有機會獲取該鎖。當然,釋放
鎖之前, A 執行緒自己是可以重複獲取此鎖的(state 會累加),這就是可重入的概念。但要注意,
獲取多少次就要釋放多麼次,這樣才能保證 state 是能回到零態的。
以 CountDownLatch 以例,任務分為 N 個子執行緒去執行, state 也初始化為 N(注意 N 要與
執行緒個數一致)。這 N 個子執行緒是並行執行的, 每個子執行緒執行完後 countDown()一次, state
會 CAS 減 1。等到所有子執行緒都執行完後(即 state=0),會 unpark()主呼叫執行緒,然後主呼叫執行緒
就會從 await()函式返回,繼續後餘動作。
ReentrantReadWriteLock 實現獨佔和共享兩種方式
一般來說,自定義同步器要麼是獨佔方法,要麼是共享方式,他們也只需實現 tryAcquiretryRelease、 tryAcquireShared-tryReleaseShared 中的一種即可。 但 AQS 也支援自定義同步器
同時實現獨佔和共享兩種方式,如 ReentrantReadWriteLock。13/04/2018 Page 101 of 283
5. JAVA 基礎
5.1.1. JAVA 異常分類及處理
5.1.1.1. 概念
如果某個方法不能按照正常的途徑完成任務,就可以通過另一種路徑退出方法。在這種情況下
會丟擲一個封裝了錯誤資訊的物件。此時,這個方法會立刻退出同時不返回任何值。另外,呼叫
這個方法的其他程式碼也無法繼續執行,異常處理機制會將程式碼執行交給異常處理器。
5.1.1.2. 異常分類
Throwable 是 Java 語言中所有錯誤或異常的超類。下一層分為 Error 和 Exception
Error
1. Error 類是指 java 執行時系統的內部錯誤和資源耗盡錯誤。應用程式不會丟擲該類物件。如果
出現了這樣的錯誤,除了告知使用者,剩下的就是盡力使程式安全的終止。
Exception(RuntimeException、 CheckedException)
2. Exception 又 有 兩 個 分 支 , 一 個 是 運 行 時 異 常 RuntimeException , 一 個 是
CheckedException。
RuntimeException 如 : NullPointerException 、 ClassCastException ; 一 個 是 檢 查 異 常
CheckedException,如 I/O 錯誤導致的 IOException、 SQLException。 RuntimeException 是
那些可能在 Java 虛擬機器正常執行期間丟擲的異常的超類。 如果出現 RuntimeException,那麼一
定是程式設計師的錯誤.13/04/2018 Page 102 of 283
檢查異常 CheckedException: 一般是外部錯誤,這種異常都發生在編譯階段, Java 編譯器會強
製程序去捕獲此類異常,即會出現要求你把這段可能出現異常的程式進行 try catch,該類異常一
般包括幾個方面:
1. 試圖在檔案尾部讀取資料
2. 試圖開啟一個錯誤格式的 URL
3. 試圖根據給定的字串查詢 class 物件,而這個字串表示的類並不存在
5.1.1.3. 異常的處理方式
遇到問題不進行具體處理,而是繼續拋給呼叫者 (throw,throws)
丟擲異常有三種形式,一是 throw,一個 throws,還有一種系統自動拋異常。
public static void main(String[] args) {
String s = "abc";
if(s.equals("abc")) {
throw new NumberFormatException();
} else {
System.out.println(s);
}
}
int div(int a,int b) throws Exception{
return a/b;}
try catch 捕獲異常針對性處理方式
5.1.1.4. Throw 和 throws 的區別:
位置不同
1. throws 用在函式上,後面跟的是異常類,可以跟多個; 而 throw 用在函式內,後面跟的
是異常物件。
功能不同:
2. throws 用來宣告異常,讓呼叫者只知道該功能可能出現的問題,可以給出預先的處理方
式; throw 丟擲具體的問題物件,執行到 throw,功能就已經結束了,跳轉到呼叫者,並
將具體的問題物件拋給呼叫者。也就是說 throw 語句獨立存在時,下面不要定義其他語
句,因為執行不到。
3. throws 表示出現異常的一種可能性,並不一定會發生這些異常; throw 則是丟擲了異常,
執行 throw 則一定丟擲了某種異常物件。13/04/2018 Page 103 of 283
4. 兩者都是消極處理異常的方式,只是丟擲或者可能丟擲異常,但是不會由函式去處理異
常,真正的處理異常由函式的上層呼叫處理。
5.1.2. JAVA 反射
5.1.2.1. 動態語言
動態語言,是指程式在執行時可以改變其結構:新的函式可以引進,已有的函式可以被刪除等結
構上的變化。比如常見的 JavaScript 就是動態語言,除此之外 Ruby,Python 等也屬於動態語言,
而 C、 C++則不屬於動態語言。 從反射角度說 JAVA 屬於半動態語言。
5.1.2.2. 反射機制概念 (執行狀態中知道類所有的屬性和方法)
在 Java 中的反射機制是指在執行狀態中,對於任意一個類都能夠知道這個類所有的屬性和方法;
並且對於任意一個物件,都能夠呼叫它的任意一個方法;這種動態獲取資訊以及動態呼叫物件方
法的功能成為 Java 語言的反射機制。
5.1.2.3. 反射的應用場合
編譯時型別和執行時型別
在 Java 程式中許多物件在執行是都會出現兩種型別:編譯時型別和執行時型別。 編譯時的型別由
宣告物件時實用的型別來決定,執行時的型別由實際賦值給物件的型別決定 。 如:
Person p=new Student();
其中編譯時型別為 Person,執行時型別為 Student。13/04/2018 Page 104 of 283
的編譯時型別無法獲取具體方法
程式在執行時還可能接收到外部傳入的物件, 該物件的編譯時型別為 Object,但是程式有需要呼叫
該物件的執行時型別的方法。為了解決這些問題, 程式需要在執行時發現物件和類的真實資訊。
然而,如果編譯時根本無法預知該物件和類屬於哪些類,程式只能依靠執行時資訊來發現該物件
和類的真實資訊,此時就必須使用到反射了。
5.1.2.4. Java 反射 API
反射 API 用來生成 JVM 中的類、介面或則物件的資訊。
1. Class 類:反射的核心類,可以獲取類的屬性,方法等資訊。
2. Field 類: Java.lang.reflec 包中的類, 表示類的成員變數,可以用來獲取和設定類之中的屬性
值。
3. Method 類: Java.lang.reflec 包中的類,表示類的方法,它可以用來獲取類中的方法資訊或
者執行方法。
4. Constructor 類: Java.lang.reflec 包中的類,表示類的構造方法。
5.1.2.5. 反射使用步驟(獲取 Class 物件、呼叫物件方法)
1. 獲取想要操作的類的 Class 物件,他是反射的核心,通過 Class 物件我們可以任意呼叫類的方
法。
2. 呼叫 Class 類中的方法,既就是反射的使用階段。
3. 使用反射 API 來操作這些資訊。
5.1.2.6. 獲取 Class 物件的 3 種方法
呼叫某個物件的 getClass()方法
Person p=new Person();
Class clazz=p.getClass();
呼叫某個類的 class 屬性來獲取該類對應的 Class 物件
Class clazz=Person.class;
使用 Class 類中的 forName()靜態方法(最安全/效能最好)
Class clazz=Class.forName("類的全路徑"); (最常用)
當我們獲得了想要操作的類的 Class 物件後,可以通過 Class 類中的方法獲取並檢視該類中的方法
和屬性。
//獲取 Person 類的 Class 物件
Class clazz=Class.forName("reflection.Person");13/04/2018 Page 105 of 283
//獲取 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.2.7. 建立物件的兩種方法
Class 物件的 newInstance()
1. 使用 Class 物件的 newInstance()方法來建立該 Class 物件對應類的例項,但是這種方法要求
該 Class 物件對應的類有預設的空構造器。
呼叫 Constructor 物件的 newInstance()
2. 先使用 Class 物件獲取指定的 Constructor 物件,再呼叫 Constructor 物件的 newInstance()
方法來建立 Class 物件對應類的例項,通過這種方法可以選定構造方法建立例項。
//獲取 Person 類的 Class 物件
Class clazz=Class.forName("reflection.Person");
//使用.newInstane 方法建立物件
Person p=(Person) clazz.newInstance();
//獲取構造方法並建立物件
Constructor c=clazz.getDeclaredConstructor(String.class,String.class,int.class);
//建立物件並設定屬性13/04/2018 Page 106 of 283
Person p1=(Person) c.newInstance("李四","男",20);
5.1.3. JAVA 註解
5.1.3.1. 概念
Annotation(註解)是 Java 提供的一種對元程式中元素關聯資訊和元資料(metadata)的途徑
和方法。 Annatation(註解)是一個介面,程式可以通過反射來獲取指定程式中元素的 Annotation
物件,然後通過該 Annotation 物件來獲取註解中的元資料資訊。
5.1.3.2. 4 種標準元註解
元註解的作用是負責註解其他註解。 Java5.0 定義了 4 個標準的 meta-annotation 型別,它們被
用來提供對其它 annotation 型別作說明。
@Target 修飾的物件範圍
@Target說明了Annotation所修飾的物件範圍: Annotation可被用於 packages、 types(類、
介面、列舉、 Annotation 型別)、型別成員(方法、構造方法、成員變數、列舉值)、方法引數
和本地變數(如迴圈變數、 catch 引數) 。在 Annotation 型別的宣告中使用了 target 可更加明晰
其修飾的目標
@Retention 定義 被保留的時間長短
Retention 定義了該 Annotation 被保留的時間長短:表示需要在什麼級別儲存註解資訊,用於描
述註解的生命週期(即:被描述的註解在什麼範圍內有效),取值(RetentionPoicy)由:
SOURCE:在原始檔中有效(即原始檔保留)
CLASS:在 class 檔案中有效(即 class 保留)
RUNTIME:在執行時有效(即執行時保留)
@Documented 描述-javadoc
@ Documented 用於描述其它型別的 annotation 應該被作為被標註的程式成員的公共 API,因
此可以被例如 javadoc 此類的工具文件化。
@Inherited 闡述了某個被標註的型別是被繼承的
@Inherited 元註解是一個標記註解, @Inherited 闡述了某個被標註的型別是被繼承的。如果一
個使用了@Inherited 修飾的 annotation 型別被用於一個 class,則這個 annotation 將被用於該
class 的子類。13/04/2018 Page 107 of 283
5.1.3.3. 註解處理器
如果沒有用來讀取註解的方法和工作,那麼註解也就不會比註釋更有用處了。使用註解的過程中,
很重要的一部分就是創建於使用註解處理器。 Java SE5 擴充套件了反射機制的 API,以幫助程式設計師快速
的構造自定義註解處理器。 下面實現一個註解處理器。
/1: *** 定義註解*/
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface FruitProvider {
/**供應商編號*/
public int id() default -1;
/*** 供應商名稱*/
public String name() default "";13/04/2018 Page 108 of 283
/** * 供應商地址*/
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 = "供應商資訊: ";
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);
}
}
}
}13/04/2018 Page 109 of 283
public class FruitRun {
public static void main(String[] args) {
FruitInfoUtil.getFruitInfo(Apple.class);
/***********輸出結果***************/
// 供應商編號: 1 供應商名稱:陝西紅富士集團 供應商地址:陝西省西安市延
}
}
5.1.4. JAVA 內部類
Java 類中不僅可以定義變數和方法,還可以定義類,這樣定義在類內部的類就被稱為內部類。根
據定義的方式不同,內部類分為靜態內部類,成員內部類,區域性內部類,匿名內部類四種。
5.1.4.1. 靜態內部類
定義在類內部的靜態類,就是靜態內部類。
public class Out {
private static int a;
private int b;
public static class Inner {
public void print() {
System.out.println(a);
}
}
}
1. 靜態內部類可以訪問外部類所有的靜態變數和方法,即使是 private 的也一樣。
2. 靜態內部類和一般類一致,可以定義靜態變數、方法,構造方法等。
3. 其它類使用靜態內部類需要使用“外部類.靜態內部類”方式,如下所示: Out.Inner inner =
new Out.Inner();inner.print();
4. Java集合類HashMap內部就有一個靜態內部類Entry。 Entry是HashMap存放元素的抽象,
HashMap 內部維護 Entry 陣列用了存放元素,但是 Entry 對使用者是透明的。像這種和外部
類關係密切的,且不依賴外部類例項的,都可以使用靜態內部類。13/04/2018 Page 110 of 283
5.1.4.2. 成員內部類
定義在類內部的非靜態類,就是成員內部類。成員內部類不能定義靜態方法和變數(final 修飾的
除外)。這是因為成員內部類是非靜態的, 類初始化的時候先初始化靜態成員,如果允許成員內
部類定義靜態變數,那麼成員內部類的靜態變數初始化順序是有歧義的。
public class Out {
private static int a;
private int b;
public class Inner {
public void print() {
System.out.println(a);
System.out.println(b);
}
}
}
5.1.4.3. 區域性內部類(定義在方法中的類)
定義在方法中的類,就是區域性類。如果一個類只在某個方法中使用,則可以考慮使用區域性類。
public class Out {
private static int a;
private int b;
public void test(final int c) {
final int d = 1;
class Inner {
public void print() {
System.out.println(c);
}
}
}
}13/04/2018 Page 111 of 283
5.1.4.4. 匿名內部類(要繼承一個父類或者實現一個介面、直接使用
new 來生成一個物件的引用)
匿名內部類我們必須要繼承一個父類或者實現一個介面,當然也僅能只繼承一個父類或者實現一
個介面。同時它也是沒有 class 關鍵字,這是因為匿名內部類是直接使用 new 來生成一個物件的引
用。
public abstract class Bird {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public abstract int fly();
}
public class Test {
public void test(Bird bird){
System.out.println(bird.getName() + "能夠飛 " + bird.fly() + "米");
}
public static void main(String[] args) {
Test test = new Test();
test.test(new Bird() {
public int fly() {
return 10000;
}
public String getName() {
return "大雁";
}
});
}
}13/04/2018 Page 112 of 283
5.1.5. JAVA 泛型
泛型提供了編譯時型別安全檢測機制,該機制允許程式設計師在編譯時檢測到非法的型別。泛型的本
質是引數化型別,也就是說所操作的資料型別被指定為一個引數。 比如我們要寫一個排序方法,
能夠對整型陣列、字串陣列甚至其他任何型別的陣列進行排序,我們就可以使用 Java 泛型。
5.1.5.1. 泛型方法(<E>)
你可以寫一個泛型方法,該方法在呼叫時可以接收不同型別的引數。根據傳遞給泛型方法的引數
型別,編譯器適當地處理每一個方法呼叫。
// 泛型方法 printArray
public static < E > void printArray( E[] inputArray )
{
for ( E element : inputArray ){
System.out.printf( "%s ", element );
}
}
1. <? extends T>表示該萬用字元所代表的型別是 T 型別的子類。
2. <? super T>表示該萬用字元所代表的型別是 T 型別的父類。
5.1.5.2. 泛型類<T>
泛型類的宣告和非泛型類的宣告類似,除了在類名後面添加了型別引數宣告部分。和泛型方法一
樣,泛型類的型別引數宣告部分也包含一個或多個型別引數,引數間用逗號隔開。一個泛型引數,
也被稱為一個型別變數,是用於指定一個泛型型別名稱的識別符號。因為他們接受一個或多個引數,
這些類被稱為引數化的類或引數化的型別。
public class Box<T> {
private T t;
public void add(T t) {
this.t = t;
}
public T get() {
return t;
}13/04/2018 Page 113 of 283
5.1.5.3. 型別萬用字元?
類 型 通 配 符 一 般 是 使 用 ? 代 替 具 體 的 類 型 參 數 。 例 如 List<?> 在 邏 輯 上 是
List<String>,List<Integer> 等所有 List<具體型別實參>的父類。
5.1.5.4. 型別擦除
Java 中的泛型基本上都是在編譯器這個層次來實現的。在生成的 Java 位元組程式碼中是不包含泛
型中的型別資訊的。使用泛型的時候加上的型別引數,會被編譯器在編譯的時候去掉。這個
過程就稱為型別擦除。如在程式碼中定義的 List<Object>和 List<String>等型別,在編譯之後
都會變成 List。 JVM 看到的只是 List,而由泛型附加的型別資訊對 JVM 來說是不可見的。
型別擦除的基本過程也比較簡單,首先是找到用來替換型別引數的具體類。這個具體類一般
是 Object。如果指定了型別引數的上界的話,則使用這個上界。把程式碼中的型別引數都替換
成具體的類。
5.1.6. JAVA 序列化(建立可複用的 Java 物件)
儲存(持久化)物件及其狀態到記憶體或者磁碟
Java 平臺允許我們在記憶體中建立可複用的 Java 物件,但一般情況下,只有當 JVM 處於執行時,
這些物件才可能存在,即,這些物件的生命週期不會比 JVM 的生命週期更長。 但在現實應用中,
就可能要求在JVM停止執行之後能夠儲存(持久化)指定的物件,並在將來重新讀取被儲存的物件。
Java 物件序列化就能夠幫助我們實現該功能。
序列化物件以位元組陣列保持-靜態成員不儲存
使用 Java 物件序列化, 在儲存物件時,會把其狀態儲存為一組位元組,在未來, 再將這些位元組組裝
成物件。必須注意地是, 物件序列化儲存的是物件的”狀態”,即它的成員變數。由此可知,對
象序列化不會關注類中的靜態變數。
序列化使用者遠端物件傳輸
除了在持久化物件時會用到物件序列化之外,當使用 RMI(遠端方法呼叫),或在網路中傳遞物件時,
都會用到物件序列化。 Java序列化API為處理物件序列化提供了一個標準機制,該API簡單易用。
Serializable 實現序列化
在 Java 中, 只要一個類實現了 java.io.Serializable 介面,那麼它就可以被序列化。
ObjectOutputStream 和 ObjectInputStream 對物件進行序列化及反序列化
通過 ObjectOutputStream 和 ObjectInputStream 對物件進行序列化及反序列化。
writeObject 和 readObject 自定義序列化策略
在類中增加 writeObject 和 readObject 方法可以實現自定義序列化策略。
序列化 ID
虛擬機器是否允許反序列化,不僅取決於類路徑和功能程式碼是否一致,一個非常重要的一點是兩個
類的序列化 ID 是否一致(就是 private static final long serialVersionUID)13/04/2018 Page 114 of 283
序列化並不儲存靜態變數
序列化子父類說明
要想將父類物件也序列化,就需要讓父類也實現 Serializable 介面。
Transient 關鍵字阻止該變數被序列化到檔案中
1. 在變數宣告前加上 Transient 關鍵字,可以阻止該變數被序列化到檔案中,在被反序列
化後, transient 變數的值被設為初始值,如 int 型的是 0,物件型的是 null。
2. 伺服器端給客戶端傳送序列化物件資料,物件中有一些資料是敏感的,比如密碼字串
等,希望對該密碼欄位在序列化時,進行加密,而客戶端如果擁有解密的金鑰,只有在
客戶端進行反序列化時,才可以對密碼進行讀取,這樣可以一定程度保證序列化物件的
資料安全。
5.1.7. JAVA 複製
將一個物件的引用複製給另外一個物件,一共有三種方式。第一種方式是直接賦值,第二種方式
是淺拷貝,第三種是深拷貝。所以大家知道了哈,這三種概念實際上都是為了拷貝物件。
5.1.7.1. 直接賦值複製
直接賦值。在 Java 中, A a1 = a2,我們需要理解的是這實際上覆制的是引用,也就是
說 a1 和 a2 指向的是同一個物件。因此,當 a1 變化的時候, a2 裡面的成員變數也會跟
著變化。
5.1.7.2. 淺複製(複製引用但不復制引用的物件)
建立一個新物件,然後將當前物件的非靜態欄位複製到該新物件, 如果欄位是值型別的,
那麼對該欄位執行復制;如果該欄位是引用型別的話,則複製引用但不復制引用的物件。
因此,原始物件及其副本引用同一個物件。
class Resume implements Cloneable{
public Object clone() {
try {
return (Resume)super.clone();
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
}13/04/2018 Page 115 of 283
5.1.7.3. 深複製(複製物件和其應用物件)
深拷貝不僅複製物件本身,而且複製物件包含的引用指向的所有物件。
class Student implements Cloneable {
String name;
int age;
Professor p;
Student(String name, int age, Professor p) {
this.name = name;
this.age = age;
this.p = p;
}
public Object clone() {
Student o = null;
try {
o = (Student) super.clone();
} catch (CloneNotSupportedException e) {
System.out.println(e.toString());
}
o.p = (Professor) p.clone();
return o;
}
}
5.1.7.4. 序列化(深 clone 一中實現)
在 Java 語言裡深複製一個物件,常常可以先使物件實現 Serializable 介面,然後把對
象(實際上只是物件的一個拷貝)寫到一個流裡,再從流裡讀出來,便可以重建物件。13/04/2018 Page 116 of 283
6. Spring 原理
它是一個全面的、企業應用開發一站式的解決方案,貫穿表現層、業務層、持久層。但是 Spring
仍然可以和其他的框架無縫整合。
6.1.1. Spring 特點
6.1.1.1. 輕量級
6.1.1.2. 控制反轉
6.1.1.3. 面向切面
6.1.1.4. 容器
6.1.1.5. 框架集合13/04/2018 Page 117 of 283
6.1.2. Spring 核心元件
6.1.3. Spring 常用模組13/04/2018 Page 118 of 283
6.1.4. Spring 主要包
6.1.5. Spring 常用註解
bean 注入與裝配的的方式有很多種,可以通過 xml, get set 方式,建構函式或者註解等。簡單易
用的方式就是使用 Spring 的註解了, Spring 提供了大量的註解方式。13/04/2018 Page 119 of 283
6.1.6. Spring 第三方結合13/04/2018 Page 120 of 283
6.1.7. Spring IOC 原理
6.1.7.1. 概念
Spring 通過一個配置檔案描述 Bean 及 Bean 之間的依賴關係,利用 Java 語言的反射功能例項化
Bean 並建立 Bean 之間的依賴關係。 Spring 的 IoC 容器在完成這些底層工作的基礎上,還提供
了 Bean 例項快取、生命週期管理、 Bean 例項代理、事件釋出、資源裝載等高階服務。
6.1.7.2. Spring 容器高層檢視
Spring 啟動時讀取應用程式提供的 Bean 配置資訊,並在 Spring 容器中生成一份相應的 Bean 配
置登錄檔,然後根據這張登錄檔例項化 Bean,裝配好 Bean 之間的依賴關係,為上層應用提供準
備就緒的執行環境。 其中 Bean 快取池為 HashMap 實現
6.1.7.3. IOC 容器實現
BeanFactory-框架基礎設施
BeanFactory 是 Spring 框架的基礎設施,面向 Spring 本身; ApplicationContext 面向使用
Spring 框架的開發者,幾乎所有的應用場合我們都直接使用 ApplicationContext 而非底層
的 BeanFactory。13/04/2018 Page 121 of 283
1.1..1.1.1 BeanDefinitionRegistry 登錄檔
1. Spring 配置檔案中每一個節點元素在 Spring 容器裡都通過一個 BeanDefinition 物件表示,
它描述了 Bean 的配置資訊。而 BeanDefinitionRegistry 介面提供了向容器手工註冊
BeanDefinition 物件的方法。
1.1..1.1.2 BeanFactory 頂層介面
2. 位於類結構樹的頂端 ,它最主要的方法就是 getBean(String beanName),該方法從容器中
返回特定名稱的 Bean, BeanFactory 的功能通過其他的介面得到不斷擴充套件:
1.1..1.1.3 ListableBeanFactory
3. 該介面定義了訪問容器中 Bean 基本資訊的若干方法,如檢視 Bean 的個數、獲取某一型別
Bean 的配置名、檢視容器中是否包括某一 Bean 等方法;
1.1..1.1.4 HierarchicalBeanFactory 父子級聯
4. 父子級聯 IoC 容器的介面,子容器可以通過介面方法訪問父容器; 通過
HierarchicalBeanFactory 介面, Spring 的 IoC 容器可以建立父子層級關聯的容器體系,子
容器可以訪問父容器中的 Bean,但父容器不能訪問子容器的 Bean。 Spring 使用父子容器實
現了很多功能,比如在 Spring MVC 中,展現層 Bean 位於一個子容器中,而業務層和持久
層的 Bean 位於父容器中。這樣,展現層 Bean 就可以引用業務層和持久層的 Bean,而業務
層和持久層的 Bean 則看不到展現層的 Bean。
1.1..1.1.5 ConfigurableBeanFactory
5. 是一個重要的介面,增強了 IoC 容器的可定製性,它定義了設定類裝載器、屬性編輯器、容
器初始化後置處理器等方法;13/04/2018 Page 122 of 283
1.1..1.1.6 AutowireCapableBeanFactory 自動裝配
6. 定義了將容器中的 Bean 按某種規則(如按名字匹配、按型別匹配等)進行自動裝配的方法;
1.1..1.1.7 SingletonBeanRegistry 執行期間註冊單例 Bean
7. 定義了允許在執行期間向容器註冊單例項 Bean 的方法;對於單例項( singleton)的 Bean
來說, BeanFactory 會快取 Bean 例項,所以第二次使用 getBean() 獲取 Bean 時將直接從
IoC 容器的快取中獲取 Bean 例項。 Spring 在 DefaultSingletonBeanRegistry 類中提供了一
個用於快取單例項 Bean 的快取器,它是一個用 HashMap 實現的快取器, 單例項的 Bean 以
beanName 為鍵儲存在這個 HashMap 中。
1.1..1.1.8 依賴日誌框框
8. 在初始化 BeanFactory 時,必須為其提供一種日誌框架,比如使用 Log4J, 即在類路徑下提
供 Log4J 配置檔案,這樣啟動 Spring 容器才不會報錯。
ApplicationContext 面向開發應用
ApplicationContext 由 BeanFactory 派 生 而 來 , 提 供 了 更 多 面 向 實 際 應 用 的 功 能 。
ApplicationContext 繼承了 HierarchicalBeanFactory 和 ListableBeanFactory 介面,在此基礎
上,還通過多個其他的介面擴充套件了 BeanFactory 的功能:
1. ClassPathXmlApplicationContext:預設從類路徑載入配置檔案13/04/2018 Page 123 of 283
2. FileSystemXmlApplicationContext:預設從檔案系統中裝載配置檔案
3. ApplicationEventPublisher:讓容器擁有釋出應用上下文事件的功能,包括容器啟動事
件、關閉事件等。
4. MessageSource:為應用提供 i18n 國際化訊息訪問的功能;
5. ResourcePatternResolver : 所 有 ApplicationContext 實現類都實現了類似於
PathMatchingResourcePatternResolver 的功能,可以通過帶字首的 Ant 風格的資源文
件路徑裝載 Spring 的配置檔案。
6. LifeCycle:該介面是 Spring 2.0 加入的,該介面提供了 start()和 stop()兩個方法,主要
用於控制非同步處理過程。在具體使用時,該介面同時被 ApplicationContext 實現及具體
Bean 實現, ApplicationContext 會將 start/stop 的資訊傳遞給容器中所有實現了該接
口的 Bean,以達到管理和控制 JMX、任務排程等目的。
7. ConfigurableApplicationContext 擴充套件於 ApplicationContext, 它新增加了兩個主要
的方法: refresh()和 close(),讓 ApplicationContext 具有啟動、重新整理和關閉應用上下
文的能力。在應用上下文關閉的情況下呼叫 refresh()即可啟動應用上下文,在已經啟動
的狀態下,呼叫 refresh()則清除快取並重新裝載配置資訊,而呼叫 close()則可關閉應用
上下文。
WebApplication 體系架構
WebApplicationContext 是專門為 Web 應用準備的,它允許從相對於 Web 根目錄的
路徑中裝載配置檔案完成初始化工作 。從 WebApplicationContext 中可以獲得
ServletContext 的引用, 整個 Web 應用上下文物件將作為屬性放置到 ServletContext
中,以便 Web 應用環境可以訪問 Spring 應用上下文。
6.1.7.4. Spring Bean 作用域
Spring 3 中為 Bean 定義了 5 中作用域, 分別為 singleton(單例)、 prototype(原型)、
request、 session 和 global session, 5 種作用域說明如下:
singleton:單例模式(多執行緒下不安全)
1. singleton:單例模式, Spring IoC 容器中只會存在一個共享的 Bean 例項,無論有多少個
Bean 引用它,始終指向同一物件。 該模式在多執行緒下是不安全的。 Singleton 作用域是
Spring 中的預設作用域,也可以顯示的將 Bean 定義為 singleton 模式,配置為:
<bean id="userDao" class="com.ioc.UserDaoImpl" scope="singleton"/>13/04/2018 Page 124 of 283
prototype:原型模式每次使用時建立
2. prototype:原型模式,每次通過 Spring 容器獲取 prototype 定義的 bean 時,容器都將建立
一個新的 Bean 例項,每個 Bean 例項都有自己的屬性和狀態,而 singleton 全域性只有一個對
象。根據經驗, 對有狀態的bean使用prototype作用域,而對無狀態的bean使用singleton
作用域。
Request:一次 request 一個例項
3. request:在一次 Http 請求中,容器會返回該 Bean 的同一例項。而對不同的 Http 請求則會
產生新的 Bean,而且該 bean 僅在當前 Http Request 內有效,當前 Http 請求結束,該 bean
例項也將會被銷燬。
<bean id="loginAction" class="com.cnblogs.Login" scope="request"/>
session
4. session:在一次 Http Session 中,容器會返回該 Bean 的同一例項。而對不同的 Session 請
求則會建立新的例項,該 bean 例項僅在當前 Session 內有效。 同 Http 請求相同,每一次
session 請求建立新的例項,而不同的例項之間不共享屬性,且例項僅在自己的 session 請求
內有效,請求結束,則例項將被銷燬。
<bean id="userPreference" class="com.ioc.UserPreference" scope="session"/>
global Session
5. global Session:在一個全域性的 Http Session 中,容器會返回該 Bean 的同一個例項,僅在
使用 portlet context 時有效。
6.1.7.5. Spring Bean 生命週期
例項化
1. 例項化一個 Bean, 也就是我們常說的 new。
IOC 依賴注入
2. 按照 Spring 上下文對例項化的 Bean 進行配置, 也就是 IOC 注入。
setBeanName 實現
3. 如果這個 Bean 已經實現了 BeanNameAware 介面,會呼叫它實現的 setBeanName(String)
方法,此處傳遞的就是 Spring 配置檔案中 Bean 的 id 值
BeanFactoryAware 實現
4. 如果這個 Bean 已經實現了 BeanFactoryAware 介面,會呼叫它實現的 setBeanFactory,
setBeanFactory(BeanFactory)傳遞的是 Spring 工廠自身(可以用這個方式來獲取其它 Bean,
只需在 Spring 配置檔案中配置一個普通的 Bean 就可以)。13/04/2018 Page 125 of 283
ApplicationContextAware 實現
5. 如果這個 Bean 已經實現了 ApplicationContextAware 介面,會呼叫
setApplicationContext(ApplicationContext)方法,傳入 Spring 上下文(同樣這個方式也
可以實現步驟 4 的內容,但比 4 更好,因為 ApplicationContext 是 BeanFactory 的子接
口,有更多的實現方法)
postProcessBeforeInitialization 介面實現-初始化預處理
6. 如果這個 Bean 關聯了 BeanPostProcessor 介面,將會呼叫
postProcessBeforeInitialization(Object obj, String s)方法, BeanPostProcessor 經常被用
作是 Bean 內容的更改,並且由於這個是在 Bean 初始化結束時呼叫那個的方法,也可以被應
用於記憶體或快取技術。
init-method
7. 如果 Bean 在 Spring 配置檔案中配置了 init-method 屬性會自動呼叫其配置的初始化方法。
postProcessAfterInitialization
8. 如果這個 Bean 關聯了 BeanPostProcessor 介面,將會呼叫
postProcessAfterInitialization(Object obj, String s)方法。
注: 以上工作完成以後就可以應用這個 Bean 了,那這個 Bean 是一個 Singleton 的,所以一
般情況下我們呼叫同一個 id 的 Bean 會是在內容地址相同的例項,當然在 Spring 配置檔案中
也可以配置非 Singleton。
Destroy 過期自動清理階段
9. 當 Bean 不再需要時,會經過清理階段,如果 Bean 實現了 DisposableBean 這個介面,會調
用那個其實現的 destroy()方法;
destroy-method 自配置清理
10. 最後,如果這個 Bean 的 Spring 配置中配置了 destroy-method 屬性,會自動呼叫其配置的
銷燬方法。13/04/2018 Page 126 of 283
11. bean 標籤有兩個重要的屬性(init-method 和 destroy-method)。用它們你可以自己定製
初始化和登出方法。它們也有相應的註解(@PostConstruct 和@PreDestroy) 。
<bean id="" class="" init-method="初始化方法" destroy-method="銷燬方法">
6.1.7.6. Spring 依賴注入四種方式
構造器注入
/*帶引數,方便利用構造器進行注入*/
public CatDaoImpl(String message){
this. message = message;
}
<bean id="CatDaoImpl" class="com.CatDaoImpl">
<constructor-arg value=" message "></constructor-arg>
</bean>13/04/2018 Page 127 of 283
setter 方法注入
public class Id {
private int id;
public int getId() { return id; }
public void setId(int id) { this.id = id; }
}
<bean id="id" class="com.id "> <property name="id" value="123"></property> </bean>
靜態工廠注入
靜態工廠顧名思義,就是通過呼叫靜態工廠的方法來獲取自己需要的物件,為了讓 spring 管理所
有物件,我們不能直接通過"工程類.靜態方法()"來獲取物件,而是依然通過 spring 注入的形式獲
取:
public class DaoFactory { //靜態工廠
public static final FactoryDao getStaticFactoryDaoImpl(){
return new StaticFacotryDaoImpl();
}
}
public class SpringAction {
private FactoryDao staticFactoryDao; //注入物件
//注入物件的 set 方法
public void setStaticFactoryDao(FactoryDao staticFactoryDao) {
this.staticFactoryDao = staticFactoryDao;
}
}
//factory-method="getStaticFactoryDaoImpl"指定呼叫哪個工廠方法
<bean name="springAction" class=" SpringAction" >
<!--使用靜態工廠的方法注入物件,對應下面的配置檔案-->
<property name="staticFactoryDao" ref="staticFactoryDao"></property>
</bean>
<!--此處獲取物件的方式是從工廠類中獲取靜態方法-->
<bean name="staticFactoryDao" class="DaoFactory"
factory-method="getStaticFactoryDaoImpl"></bean>
例項工廠
例項工廠的意思是獲取物件例項的方法不是靜態的,所以你需要首先 new 工廠類,再呼叫普通的
例項方法:
public class DaoFactory { //例項工廠
public FactoryDao getFactoryDaoImpl(){
return new FactoryDaoImpl();13/04/2018 Page 128 of 283
}
}
public class SpringAction {
private FactoryDao factoryDao; //注入物件
public void setFactoryDao(FactoryDao factoryDao) {
this.factoryDao = factoryDao;
}
}
<bean name="springAction" class="SpringAction">
<!--使用例項工廠的方法注入物件,對應下面的配置檔案-->
<property name="factoryDao" ref="factoryDao"></property>
</bean>
<!--此處獲取物件的方式是從工廠類中獲取例項方法-->
<bean name="daoFactory" class="com.DaoFactory"></bean>
<bean name="factoryDao" factory-bean="daoFactory"
factory-method="getFactoryDaoImpl"></bean>
6.1.7.7. 5 種不同方式的自動裝配
Spring 裝配包括手動裝配和自動裝配,手動裝配是有基於 xml 裝配、 構造方法、 setter 方法等
自動裝配有五種自動裝配的方式,可以用來指導 Spring 容器用自動裝配方式來進行依賴注入。
1. no:預設的方式是不進行自動裝配,通過顯式設定 ref 屬性來進行裝配。
2. byName:通過引數名 自動裝配, Spring 容器在配置檔案中發現 bean 的 autowire 屬性被設
置成 byname,之後容器試圖匹配、裝配和該 bean 的屬性具有相同名字的 bean。
3. byType:通過引數型別自動裝配, Spring 容器在配置檔案中發現 bean 的 autowire 屬性被
設定成 byType,之後容器試圖匹配、裝配和該 bean 的屬性具有相同型別的 bean。如果有多
個 bean 符合條件,則丟擲錯誤。
4. constructor:這個方式類似於 byType, 但是要提供給構造器引數,如果沒有確定的帶引數
的構造器引數型別,將會丟擲異常。
5. autodetect:首先嚐試使用 constructor 來自動裝配,如果無法工作,則使用 byType 方式。13/04/2018 Page 129 of 283
6.1.8. Spring APO 原理
6.1.8.1. 概念
"橫切"的技術,剖解開封裝的物件內部,並將那些影響了多個類的公共行為封裝到一個可重用模組,
並將其命名為"Aspect",即切面。所謂"切面",簡單說就是那些與業務無關,卻為業務模組所共
同調用的邏輯或責任封裝起來,便於減少系統的重複程式碼,降低模組之間的耦合度,並有利於未
來的可操作性和可維護性。
使用"橫切"技術, AOP 把軟體系統分為兩個部分:核心關注點和橫切關注點。業務處理的主要流
程是核心關注點,與之關係不大的部分是橫切關注點。橫切關注點的一個特點是,他們經常發生
在核心關注點的多處,而各處基本相似, 比如許可權認證、日誌、事物。 AOP 的作用在於分離系統
中的各種關注點,將核心關注點和橫切關注點分離開來。
AOP 主要應用場景有:
1. Authentication 許可權
2. Caching 快取
3. Context passing 內容傳遞
4. Error handling 錯誤處理
5. Lazy loading 懶載入
6. Debugging 除錯
7. logging, tracing, profiling and monitoring 記錄跟蹤 優化 校準
8. Performance optimization 效能優化
9. Persistence 持久化
10. Resource pooling 資源池
11. Synchronization 同步
12. Transactions 事務
6.1.8.2. AOP 核心概念
1、切面(aspect) : 類是對物體特徵的抽象,切面就是對橫切關注點的抽象
2、橫切關注點: 對哪些方法進行攔截,攔截後怎麼處理,這些關注點稱之為橫切關注點。
3、連線點(joinpoint) : 被攔截到的點,因為 Spring 只支援方法型別的連線點,所以在 Spring
中連線點指的就是被攔截到的方法,實際上連線點還可以是欄位或者構造器。
4、切入點(pointcut) : 對連線點進行攔截的定義
5、通知(advice) : 所謂通知指的就是指攔截到連線點之後要執行的程式碼, 通知分為前置、後置、
異常、最終、環繞通知五類。
6、目標物件: 代理的目標物件
7、織入(weave) : 將切面應用到目標物件並導致代理物件建立的過程13/04/2018 Page 130 of 283
8、引入(introduction) : 在不修改程式碼的前提下,引入可以在執行期為類動態地新增一些方法
或欄位。
參考: https://segmentfault.com/a/1190000007469968
6.1.8.1. AOP 兩種代理方式
Spring 提供了兩種方式來生成代理物件: JDKProxy 和 Cglib,具體使用哪種方式 生成由
AopProxyFactory 根據 AdvisedSupport 物件的配置來決定。 預設的策略是如果目標類是介面,
則使用 JDK 動態代理技術,否則使用 Cglib 來生成代理。
JDK 動態介面代理
1. JDK 動態代理主要涉及到 java.lang.reflect 包中的兩個類: Proxy 和 InvocationHandler。
InvocationHandler 是一個介面,通過實現該介面定義橫切邏輯,並通過反射機制呼叫目標類
的程式碼,動態將橫切邏輯和業務邏輯編制在一起。 Proxy 利用 InvocationHandler 動態建立
一個符合某一介面的例項,生成目標類的代理物件。13/04/2018 Page 131 of 283
CGLib 動態代理
2. : CGLib 全稱為 Code Generation Library,是一個強大的高效能, 高質量的程式碼生成類庫,
可以在執行期擴充套件 Java 類與實現 Java 介面, CGLib 封裝了 asm,可以再執行期動態生成新
的 class。和 JDK 動態代理相比較: JDK 建立代理有一個限制,就是隻能為介面建立代理例項,
而對於沒有通過介面定義業務方法的類,則可以通過 CGLib 建立動態代理。
6.1.8.2. 實現原理
@Aspect
public class TransactionDemo {
@Pointcut(value="execution(* com.yangxin.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");
}
}13/04/2018 Page 132 of 283
6.1.9. Spring MVC 原理
Spring 的模型-檢視-控制器(MVC)框架是圍繞一個 DispatcherServlet 來設計的,這個 Servlet
會把請求分發給各個處理器,並支援可配置的處理器對映、檢視渲染、本地化、時區與主題渲染
等,甚至還能支援檔案上傳。
6.1.9.1. MVC 流程13/04/2018 Page 133 of 283
Http 請求到 DispatcherServlet
(1) 客戶端請求提交到 DispatcherServlet。
HandlerMapping 尋找處理器
(2) 由 DispatcherServlet 控制器查詢一個或多個 HandlerMapping,找到處理請求的
Controller。
呼叫處理器 Controller
(3) DispatcherServlet 將請求提交到 Controller。
Controller 呼叫業務邏輯處理後,返回 ModelAndView
(4)(5)呼叫業務處理和返回結果: Controller 呼叫業務邏輯處理後,返回 ModelAndView。
DispatcherServlet 查詢 ModelAndView
(6)(7)處理檢視對映並返回模型: DispatcherServlet 查詢一個或多個 ViewResoler 檢視解析器,
找到 ModelAndView 指定的檢視。
ModelAndView 反饋瀏覽器 HTTP
(8) Http 響應:檢視負責將結果顯示到客戶端。
6.1.9.1. MVC 常用註解13/04/2018 Page 134 of 283
6.1.10. Spring Boot 原理
Spring Boot 是由 Pivotal 團隊提供的全新框架,其設計目的是用來簡化新 Spring 應用的初始搭
建以及開發過程。該框架使用了特定的方式來進行配置,從而使開發人員不再需要定義樣板化的
配置。通過這種方式, Spring Boot 致力於在蓬勃發展的快速應用開發領域(rapid application
development)成為領導者。 其特點如下:
1. 建立獨立的 Spring 應用程式
2. 嵌入的 Tomcat,無需部署 WAR 檔案
3. 簡化 Maven 配置
4. 自動配置 Spring
5. 提供生產就緒型功能,如指標,健康檢查和外部配置
6. 絕對沒有程式碼生成和對 XML 沒有要求配置 [1]
6.1.11. JPA 原理
6.1.11.1. 事務
事務是計算機應用中不可或缺的元件模型,它保證了使用者操作的原子性 ( Atomicity )、一致性
( Consistency )、隔離性 ( Isolation ) 和永續性 ( Durabilily )。
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();
// 將 A 賬戶中的金額減少 500
stmt.execute("update t_account set amount = amount - 500 where account_id = 'A'");13/04/2018 Page 135 of 283
// 將 B 賬戶中的金額增加 500
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();// 從資料庫 A 中取得資料庫連線
connB = getDataSourceB().getConnection();// 從資料庫 B 中取得資料庫連線
userTx.begin(); // 啟動事務
stmtA = connA.createStatement();// 將 A 賬戶中的金額減少 500
stmtA.execute("update t_account set amount = amount - 500 where account_id = 'A'");
// 將 B 賬戶中的金額增加 500
stmtB = connB.createStatement();13/04/2018 Page 136 of 283
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){ }
}
6.1.11.1. 兩階段提交
兩階段提交主要保證了分散式事務的原子性:即所有結點要麼全做要麼全不做,所謂的兩個階段
是指: 第一階段:準備階段;第二階段:提交階段。
1 準備階段
事務協調者(事務管理器)給每個參與者(資源管理器)傳送 Prepare 訊息,每個參與者要麼直接返回
失敗(如許可權驗證失敗), 要麼在本地執行事務,寫本地的 redo 和 undo 日誌,但不提交, 到達一
種“萬事俱備,只欠東風”的狀態。
2 提交階段:
如果協調者收到了參與者的失敗訊息或者超時,直接給每個參與者傳送回滾(Rollback)訊息;否則,
傳送提交(Commit)訊息;參與者根據協調者的指令執行提交或者回滾操作,釋放所有事務處理過
程中使用的鎖資源。 (注意:必須在最後階段釋放鎖資源)13/04/2018 Page 137 of 283
將提交分成兩階段進行的目的很明確,就是儘可能晚地提交事務,讓事務在提交前儘可能地完成
所有能完成的工作。
6.1.12. Mybatis 快取
Mybatis 中有一級快取和二級快取,預設情況下一級快取是開啟的,而且是不能關閉的。一級快取
是指 SqlSession 級別的快取,當在同一個 SqlSession 中進行相同的 SQL 語句查詢時,第二次以
後的查詢不會從資料庫查詢,而是直接從快取中獲取,一級快取最多快取 1024 條 SQL。二級快取
是指可以跨 SqlSession 的快取。 是 mapper 級別的快取,對於 mapper 級別的快取不同的
sqlsession 是可以共享的。13/04/2018 Page 138 of 283
6.1.12.1. Mybatis 的一級快取原理(sqlsession 級別)
第一次發出一個查詢 sql, sql 查詢結果寫入 sqlsession 的一級快取中,快取使用的資料結構是一
個 map。
key: MapperID+offset+limit+Sql+所有的入參
value:使用者資訊
同一個 sqlsession 再次發出相同的 sql,就從快取中取出資料。如果兩次中間出現 commit 操作
(修改、新增、刪除),本 sqlsession 中的一級快取區域全部清空,下次再去快取中查詢不到所
以要從資料庫查詢, 從資料庫查詢到再寫入快取。
6.1.12.2. 二級快取原理(mapper 基本)
二級快取的範圍是 mapper 級別(mapper 同一個名稱空間), mapper 以名稱空間為單位建立緩
存資料結構,結構是 map。 mybatis 的二級快取是通過 CacheExecutor 實現的。 CacheExecutor13/04/2018 Page 139 of 283
其實是 Executor 的代理物件。所有的查詢操作,在 CacheExecutor 中都會先匹配快取中是否存
在,不存在則查詢資料庫。
key: MapperID+offset+limit+Sql+所有的入參
具體使用需要配置:
1. Mybatis 全域性配置中啟用二級快取配置
2. 在對應的 Mapper.xml 中配置 cache 節點
3. 在對應的 select 查詢節點中新增 useCache=true
6.1.13. Tomcat 架構
http://www.importnew.com/21112.html13/04/2018 Page 140 of 283
7. 微服務
7.1.1. 服務註冊發現
服務註冊就是維護一個登記簿,它管理系統內所有的服務地址。當新的服務啟動後,它會向登記
簿交待自己的地址資訊。服務的依賴方直接向登記簿要 Service Provider 地址就行了。當下用於服
務註冊的工具非常多 ZooKeeper, Consul, Etcd, 還有 Netflix 家的 eureka 等。 服務註冊有兩種
形式:客戶端註冊和第三方註冊。
7.1.1.1. 客戶端註冊(zookeeper)
客戶端註冊是服務自身要負責註冊與登出的工作。當服務啟動後向註冊中心註冊自身,當服務下
線時登出自己。期間還需要和註冊中心保持心跳。心跳不一定要客戶端來做,也可以由註冊中心
負責(這個過程叫探活)。這種方式的缺點是註冊工作與服務耦合在一起,不同語言都要實現一
套註冊邏輯。
7.1.1.2. 第三方註冊(獨立的服務 Registrar)
第三方註冊由一個獨立的服務Registrar負責註冊與登出。當服務啟動後以某種方式通知Registrar,
然後 Registrar 負責向註冊中心發起註冊工作。同時註冊中心要維護與服務之間的心跳,當服務不
可用時,向註冊中心登出服務。這種方式的缺點是 Registrar 必須是一個高可用的系統,否則註冊
工作沒法進展。13/04/2018 Page 141 of 283
7.1.1.3. 客戶端發現
客戶端發現是指客戶端負責查詢可用服務地址,以及負載均衡的工作。這種方式最方便直接,而
且也方便做負載均衡。再者一旦發現某個服務不可用立即換另外一個,非常直接。缺點也在於多
語言時的重複工作,每個語言實現相同的邏輯。13/04/2018 Page 142 of 283
7.1.1.4. 服務端發現
服務端發現需要額外的 Router 服務,請求先打到 Router,然後 Router 負責查詢服務與負載均衡。
這種方式雖然沒有客戶端發現的缺點,但是它的缺點是保證 Router 的高可用。
7.1.1.5. Consul
7.1.1.6. Eureka
7.1.1.7. SmartStack
7.1.1.8. Etcd
7.1.2. API 閘道器
API Gateway 是一個伺服器,也可以說是進入系統的唯一節點。這跟面向物件設計模式中的
Facade 模式很像。 API Gateway 封裝內部系統的架構,並且提供 API 給各個客戶端。它還可能有
其他功能,如授權、監控、負載均衡、快取、請求分片和管理、靜態響應處理等。下圖展示了一
個適應當前架構的 API Gateway。13/04/2018 Page 143 of 283
API Gateway 負責請求轉發、合成和協議轉換。所有來自客戶端的請求都要先經過 API Gateway,
然後路由這些請求到對應的微服務。 API Gateway 將經常通過呼叫多個微服務來處理一個請求以
及聚合多個服務的結果。它可以在 web 協議與內部使用的非 Web 友好型協議間進行轉換,如
HTTP 協議、 WebSocket 協議。
7.1.2.1. 請求轉發
服務轉發主要是對客戶端的請求安裝微服務的負載轉發到不同的服務上
7.1.2.2. 響應合併
把業務上需要呼叫多個服務接口才能完成的工作合併成一次呼叫對外統一提供服務。
7.1.2.3. 協議轉換
重點是支援 SOAP, JMS, Rest 間的協議轉換。
7.1.2.4. 資料轉換
重點是支援 XML 和 Json 之間的報文格式轉換能力(可選)13/04/2018 Page 144 of 283
7.1.2.5. 安全認證
1. 基於 Token 的客戶端訪問控制和安全策略
2. 傳輸資料和報文加密,到服務端解密,需要在客戶端有獨立的 SDK 代理包
3. 基於 Https 的傳輸加密,客戶端和服務端數字證書支援
4. 基於 OAuth2.0 的服務安全認證(授權碼,客戶端,密碼模式等)
7.1.3. 配置中心
配置中心一般用作系統的引數配置,它需要滿足如下幾個要求:高效獲取、實時感知、分散式訪
問。
7.1.3.1. zookeeper 配置中心
實現的架構圖如下所示,採取資料載入到記憶體方式解決高效獲取的問題,藉助 zookeeper 的節點
監聽機制來實現實時感知。
7.1.3.2. 配置中心資料分類
7.1.4. 事件排程(kafka)
訊息服務和事件的統一排程,常用用 kafka , activemq 等。
7.1.5. 服務跟蹤(starter-sleuth)
隨著微服務數量不斷增長,需要跟蹤一個請求從一個微服務到下一個微服務的傳播過程, Spring
Cloud Sleuth 正是解決這個問題,它在日誌中引入唯一 ID,以保證微服務呼叫之間的一致性,這
樣你就能跟蹤某個請求是如何從一個微服務傳遞到下一個。13/04/2018 Page 145 of 283
1. 為了實現請求跟蹤,當請求傳送到分散式系統的入口端點時,只需要服務跟蹤框架為該請求
建立一個唯一的跟蹤標識,同時在分散式系統內部流轉的時候,框架始終保持傳遞該唯一標
識,直到返回給請求方為止, 這個唯一標識就是前文中提到的 Trace ID。通過 Trace ID 的記
錄,我們就能將所有請求過程日誌關聯起來。
2. 為了統計各處理單元的時間延遲,當請求達到各個服務元件時,或是處理邏輯到達某個狀態
時,也通過一個唯一標識來標記它的開始、具體過程以及結束,該標識就是我們前文中提到
的 Span ID, 對於每個 Span 來說,它必須有開始和結束兩個節點,通過記錄開始 Span 和結
束 Span 的時間戳,就能統計出該 Span 的時間延遲,除了時間戳記錄之外,它還可以包含一
些其他元資料,比如:事件名稱、請求資訊等。
3. 在快速入門示例中,我們輕鬆實現了日誌級別的跟蹤資訊接入,這完全歸功於spring-cloudstarter-sleuth 元件的實現。在 Spring Boot 應用中,通過在工程中引入 spring-cloudstarter-sleuth 依賴之後, 它會自動的為當前應用構建起各通訊通道的跟蹤機制,比如:
通過諸如 RabbitMQ、 Kafka(或者其他任何 Spring Cloud Stream 繫結器實現的訊息
中介軟體)傳遞的請求。
通過 Zuul 代理傳遞的請求。
通過 RestTemplate 發起的請求。
7.1.6. 服務熔斷(Hystrix)
在微服務架構中通常會有多個服務層呼叫,基礎服務的故障可能會導致級聯故障,進而造成整個
系統不可用的情況,這種現象被稱為服務雪崩效應。服務雪崩效應是一種因“服務提供者”的不
可用導致“服務消費者”的不可用,並將不可用逐漸放大的過程。
熔斷器的原理很簡單,如同電力過載保護器。它可以實現快速失敗,如果它在一段時間內偵測到
許多類似的錯誤, 會強迫其以後的多個呼叫快速失敗,不再訪問遠端伺服器,從而防止應用程式
不斷地嘗試執行可能會失敗的操作,使得應用程式繼續執行而不用等待修正錯誤,或者浪費 CPU
時間去等到長時間的超時產生。熔斷器也可以使應用程式能夠診斷錯誤是否已經修正,如果已經
修正,應用程式會再次嘗試呼叫操作。13/04/2018 Page 146 of 283
7.1.6.1. Hystrix 斷路器機制
斷路器很好理解, 當 Hystrix Command 請求後端服務失敗數量超過一定比例(預設 50%), 斷路器會
切換到開路狀態(Open). 這時所有請求會直接失敗而不會發送到後端服務. 斷路器保持在開路狀態
一段時間後(預設 5 秒), 自動切換到半開路狀態(HALF-OPEN). 這時會判斷下一次請求的返回情況,
如果請求成功, 斷路器切回閉路狀態(CLOSED), 否則重新切換到開路狀態(OPEN). Hystrix 的斷路器
就像我們家庭電路中的保險絲, 一旦後端服務不可用, 斷路器會直接切斷請求鏈, 避免傳送大量無效
請求影響系統吞吐量, 並且斷路器有自我檢測並恢復的能力。
7.1.7. API 管理
SwaggerAPI 管理工具。13/04/2018 Page 147 of 283
8. Netty 與 RPC
8.1.1. Netty 原理
Netty 是一個高效能、非同步事件驅動的 NIO 框架,基於 JAVA NIO 提供的 API 實現。它提供了對
TCP、 UDP 和檔案傳輸的支援,作為一個非同步 NIO 框架, Netty 的所有 IO 操作都是非同步非阻塞
的, 通過 Future-Listener 機制,使用者可以方便的主動獲取或者通過通知機制獲得 IO 操作結果。
8.1.2. Netty 高效能
在 IO 程式設計過程中,當需要同時處理多個客戶端接入請求時,可以利用多執行緒或者 IO 多路複用技術
進行處理。 IO 多路複用技術通過把多個 IO 的阻塞複用到同一個 select 的阻塞上,從而使得系統在
單執行緒的情況下可以同時處理多個客戶端請求。與傳統的多執行緒/多程序模型比, I/O 多路複用的
最大優勢是系統開銷小,系統不需要建立新的額外程序或者執行緒,也不需要維護這些程序和執行緒
的執行,降低了系統的維護工作量,節省了系統資源。
與 Socket 類和 ServerSocket 類相對應, NIO 也提供了 SocketChannel 和 ServerSocketChannel
兩種不同的套接字通道實現。
8.1.2.1. 多路複用通訊方式
Netty 架構按照 Reactor 模式設計和實現,它的服務端通訊序列圖如下:
客戶端通訊序列圖如下:13/04/2018 Page 148 of 283
Netty 的 IO 執行緒 NioEventLoop 由於聚合了多路複用器 Selector,可以同時併發處理成百上千個
客戶端 Channel,由於讀寫操作都是非阻塞的,這就可以充分提升 IO 執行緒的執行效率,避免由於
頻繁 IO 阻塞導致的執行緒掛起。
8.1.2.1. 非同步通訊 NIO
由於 Netty 採用了非同步通訊模式, 一個 IO 執行緒可以併發處理 N 個客戶端連線和讀寫操作,這從根
本上解決了傳統同步阻塞 IO 一連線一執行緒模型,架構的效能、彈性伸縮能力和可靠性都得到了極
大的提升。13/04/2018 Page 149 of 283
8.1.2.2. 零拷貝(DIRECT BUFFERS 使用堆外直接記憶體)
1. Netty 的接收和傳送 ByteBuffer 採用 DIRECT BUFFERS,使用堆外直接記憶體進行 Socket 讀寫,
不需要進行位元組緩衝區的二次拷貝。如果使用傳統的堆記憶體(HEAP BUFFERS)進行 Socket 讀寫,
JVM 會將堆記憶體 Buffer 拷貝一份到直接記憶體中,然後才寫入 Socket 中。相比於堆外直接記憶體,
訊息在傳送過程中多了一次緩衝區的記憶體拷貝。
2. Netty 提供了組合 Buffer 物件,可以聚合多個 ByteBuffer 物件,使用者可以像操作一個 Buffer 那樣
方便的對組合 Buffer 進行操作,避免了傳統通過記憶體拷貝的方式將幾個小 Buffer 合併成一個大的
Buffer。
3. Netty的檔案傳輸採用了transferTo方法,它可以直接將檔案緩衝區的資料傳送到目標Channel,
避免了傳統通過迴圈 write 方式導致的記憶體拷貝問題
8.1.2.3. 記憶體池(基於記憶體池的緩衝區重用機制)
隨著 JVM 虛擬機器和 JIT 即時編譯技術的發展,物件的分配和回收是個非常輕量級的工作。但是對於緩
衝區 Buffer,情況卻稍有不同,特別是對於堆外直接記憶體的分配和回收,是一件耗時的操作。為了盡
量重用緩衝區, Netty 提供了基於記憶體池的緩衝區重用機制。
8.1.2.4. 高效的 Reactor 執行緒模型
常用的 Reactor 執行緒模型有三種, Reactor 單執行緒模型, Reactor 多執行緒模型, 主從 Reactor 多執行緒模
型。
Reactor 單執行緒模型
Reactor 單執行緒模型,指的是所有的 IO 操作都在同一個 NIO 執行緒上面完成, NIO 執行緒的職責如下:
1) 作為 NIO 服務端,接收客戶端的 TCP 連線;
2) 作為 NIO 客戶端,向服務端發起 TCP 連線;
3) 讀取通訊對端的請求或者應答訊息;
4) 向通訊對端傳送訊息請求或者應答訊息。13/04/2018 Page 150 of 283
由於 Reactor 模式使用的是非同步非阻塞 IO,所有的 IO 操作都不會導致阻塞,理論上一個執行緒可以獨
立處理所有 IO 相關的操作。從架構層面看,一個 NIO 執行緒確實可以完成其承擔的職責。例如,通過
Acceptor 接收客戶端的 TCP 連線請求訊息,鏈路建立成功之後,通過 Dispatch 將對應的 ByteBuffer
派發到指定的 Handler 上進行訊息解碼。使用者 Handler 可以通過 NIO 執行緒將訊息傳送給客戶端。
Reactor 多執行緒模型
Rector 多執行緒模型與單執行緒模型最大的區別就是有一組 NIO 執行緒處理 IO 操作。 有專門一個
NIO 執行緒-Acceptor 執行緒用於監聽服務端,接收客戶端的 TCP 連線請求; 網路 IO 操作-讀、寫
等由一個 NIO 執行緒池負責, 執行緒池可以採用標準的 JDK 執行緒池實現,它包含一個任務佇列和 N
個可用的執行緒,由這些 NIO 執行緒負責訊息的讀取、解碼、編碼和傳送;
主從 Reactor 多執行緒模型
服務端用於接收客戶端連線的不再是個 1 個單獨的 NIO 執行緒,而是一個獨立的 NIO 執行緒池。
Acceptor 接收到客戶端 TCP 連線請求處理完成後(可能包含接入認證等),將新建立的
SocketChannel 註冊到 IO 執行緒池(sub reactor 執行緒池)的某個 IO 執行緒上,由它負責
SocketChannel 的讀寫和編解碼工作。 Acceptor 執行緒池僅僅只用於客戶端的登陸、握手和安全
認證,一旦鏈路建立成功,就將鏈路註冊到後端 subReactor 執行緒池的 IO 執行緒上,由 IO 執行緒負
責後續的 IO 操作。13/04/2018 Page 151 of 283
8.1.2.5. 無鎖設計、執行緒繫結
Netty 採用了序列無鎖化設計,在 IO 執行緒內部進行序列操作,避免多執行緒競爭導致的效能下降。
表面上看,序列化設計似乎 CPU 利用率不高,併發程度不夠。但是,通過調整 NIO 執行緒池的執行緒
引數,可以同時啟動多個序列化的執行緒並行執行,這種區域性無鎖化的序列執行緒設計相比一個佇列-
多個工作執行緒模型效能更優。
Netty 的 NioEventLoop 讀取到訊息之後,直接呼叫 ChannelPipeline 的
fireChannelRead(Object msg),只要使用者不主動切換執行緒,一直會由 NioEventLoop 呼叫
到使用者的 Handler,期間不進行執行緒切換,這種序列化處理方式避免了多執行緒操作導致的鎖
的競爭,從效能角度看是最優的。
8.1.2.6. 高效能的序列化框架
Netty 預設提供了對 Google Protobuf 的支援,通過擴充套件 Netty 的編解碼介面,使用者可以實現其它的
高效能序列化框架,例如 Thrift 的壓縮二進位制編解碼框架。
1. SO_RCVBUF 和 SO_SNDBUF: 通常建議值為 128K 或者 256K。13/04/2018 Page 152 of 283
小包封大包,防止網路阻塞
2. SO_TCPNODELAY: NAGLE 演算法通過將緩衝區內的小封包自動相連,組成較大的封包,阻止大量
小封包的傳送阻塞網路,從而提高網路應用效率。但是對於時延敏感的應用場景需要關閉該優化算
法。
軟中斷 Hash 值和 CPU 繫結
3. 軟中斷:開啟 RPS 後可以實現軟中斷,提升網路吞吐量。 RPS 根據資料包的源地址,目的地址以
及目的和源埠,計算出一個 hash 值,然後根據這個 hash 值來選擇軟中斷執行的 cpu,從上層
來看, 也就是說將每個連線和 cpu 繫結,並通過這個 hash 值,來均衡軟中斷在多個 cpu 上,提升
網路並行處理效能。
8.1.3. Netty RPC 實現
8.1.3.1. 概念
RPC,即 Remote Procedure Call(遠端過程呼叫),呼叫遠端計算機上的服務,就像呼叫本地服務一
樣。 RPC 可以很好的解耦系統,如 WebService 就是一種基於 Http 協議的 RPC。這個 RPC 整體框架
如下:
8.1.3.2. 關鍵技術
1. 服務釋出與訂閱:服務端使用 Zookeeper 註冊服務地址,客戶端從 Zookeeper 獲取可用的服務
地址。
2. 通訊:使用 Netty 作為通訊框架。
3. Spring:使用 Spring 配置服務,載入 Bean,掃描註解。
4. 動態代理:客戶端使用代理模式透明化服務呼叫。
5. 訊息編解碼:使用 Protostuff 序列化和反序列化訊息。
8.1.3.3. 核心流程
1. 服務消費方(client)呼叫以本地呼叫方式呼叫服務;13/04/2018 Page 153 of 283
2. client stub 接收到呼叫後負責將方法、引數等組裝成能夠進行網路傳輸的訊息體;
3. client stub 找到服務地址,並將訊息傳送到服務端;
4. server stub 收到訊息後進行解碼;
5. server stub 根據解碼結果呼叫本地的服務;
6. 本地服務執行並將結果返回給 server stub;
7. server stub 將返回結果打包成訊息併發送至消費方;
8. client stub 接收到訊息,並進行解碼;
9. 服務消費方得到最終結果。
RPC 的目標就是要 2~8 這些步驟都封裝起來,讓使用者對這些細節透明。 JAVA 一般使用動態代
理方式實現遠端呼叫。
8.1.3.1. 訊息編解碼
息資料結構(介面名稱+方法名+引數型別和引數值+超時時間+ requestID)
客戶端的請求訊息結構一般需要包括以下內容:
1. 介面名稱: 在我們的例子裡介面名是“HelloWorldService”,如果不傳,服務端就不知道呼叫哪
個介面了;
2. 方法名:一個介面內可能有很多方法,如果不傳方法名服務端也就不知道呼叫哪個方法;
3. 引數型別和引數值:引數型別有很多,比如有 bool、 int、 long、 double、 string、 map、 list,
甚至如 struct(class);以及相應的引數值;
4. 超時時間:
5. requestID,標識唯一請求 id,在下面一節會詳細描述 requestID 的用處。
6. 服務端返回的訊息 : 一般包括以下內容。返回值+狀態 code+requestID13/04/2018 Page 154 of 283
序列化
目前網際網路公司廣泛使用 Protobuf、 Thrift、 Avro 等成熟的序列化解決方案來搭建 RPC 框架,這
些都是久經考驗的解決方案。
8.1.3.1. 通訊過程
核心問題(執行緒暫停、 訊息亂序)
如果使用 netty 的話, 一般會用 channel.writeAndFlush()方法來發送訊息二進位制串,這個方
法呼叫後對於整個遠端呼叫(從發出請求到接收到結果)來說是一個非同步的,即對於當前執行緒來說,
將請求傳送出來後,執行緒就可以往後執行了,至於服務端的結果,是服務端處理完成後,再以訊息
的形式傳送給客戶端的。於是這裡出現以下兩個問題:
1. 怎麼讓當前執行緒“暫停”,等結果回來後,再向後執行?
2. 如果有多個執行緒同時進行遠端方法呼叫,這時建立在 client server 之間的 socket 連線上
會有很多雙方傳送的訊息傳遞,前後順序也可能是隨機的, server 處理完結果後,將結
果訊息傳送給 client, client 收到很多訊息,怎麼知道哪個訊息結果是原先哪個執行緒呼叫
的?如下圖所示,執行緒 A 和執行緒 B 同時向 client socket 傳送請求 requestA 和 requestB,
socket 先後將 requestB 和 requestA 傳送至 server, 而 server 可能將 responseB 先返
回,儘管 requestB 請求到達時間更晚。我們需要一種機制保證 responseA 丟給
ThreadA, responseB 丟給 ThreadB。
通訊流程
requestID 生成-AtomicLong
1. client 執行緒每次通過 socket 呼叫一次遠端介面前, 生成一個唯一的 ID,即 requestID
(requestID 必需保證在一個 Socket 連線裡面是唯一的) ,一般常常使用 AtomicLong
從 0 開始累計數字生成唯一 ID;
存放回調物件 callback 到全域性 ConcurrentHashMap
2. 將 處 理 結 果 的 回 調 對 象 callback , 存 放 到 全 局 ConcurrentHashMap 裡 面
put(requestID, callback);
synchronized 獲取回撥物件 callback 的鎖並自旋 wait
3. 當執行緒呼叫 channel.writeAndFlush()傳送訊息後,緊接著執行 callback 的 get()方法試
圖獲取遠端返回的結果。在 get()內部,則使用 synchronized 獲取回撥物件 callback 的
鎖,再先檢測是否已經獲取到結果,如果沒有,然後呼叫 callback 的 wait()方法,釋放
callback 上的鎖,讓當前執行緒處於等待狀態。13/04/2018 Page 155 of 283
監聽訊息的執行緒收到訊息,找到 callback 上的鎖並喚醒
4. 服務端接收到請求並處理後,將 response 結果(此結果中包含了前面的 requestID)發
送給客戶端, 客戶端 socket 連線上專門監聽訊息的執行緒收到訊息,分析結果,取到
requestID , 再 從 前 面 的 ConcurrentHashMap 裡 面 get(requestID) , 從 而 找 到
callback 物件,再用 synchronized 獲取 callback 上的鎖, 將方法呼叫結果設定到
callback 物件裡,再呼叫 callback.notifyAll()喚醒前面處於等待狀態的執行緒。
public Object get() {
synchronized (this) { // 旋鎖
while (true) { // 是否有結果了
If (!isDone) {
wait(); //沒結果釋放鎖,讓當前執行緒處於等待狀態
}else{//獲取資料並處理
}
}
}
}
private void setDone(Response res) {
this.res = res;
isDone = true;
synchronized (this) { //獲取鎖,因為前面 wait()已經釋放了 callback 的鎖了
notifyAll(); // 喚醒處於等待的執行緒
}
}
8.1.4. RMI 實現方式
Java 遠端方法呼叫,即 Java RMI(Java Remote Method Invocation)是 Java 程式語言裡,一種用
於實現遠端過程呼叫的應用程式程式設計介面。它使客戶機上執行的程式可以呼叫遠端伺服器上的物件。遠
程方法呼叫特性使 Java 程式設計人員能夠在網路環境中分佈操作。 RMI 全部的宗旨就是儘可能簡化遠端接
口物件的使用。
8.1.4.1. 實現步驟
1. 編寫遠端服務介面,該介面必須繼承 java.rmi.Remote 介面,方法必須丟擲
java.rmi.RemoteException 異常;
2. 編寫遠端介面實現類,該實現類必須繼承 java.rmi.server.UnicastRemoteObject 類;
3. 執行 RMI 編譯器(rmic),建立客戶端 stub 類和服務端 skeleton 類;
4. 啟動一個 RMI 登錄檔,以便駐留這些服務;13/04/2018 Page 156 of 283
5. 在 RMI 登錄檔中註冊服務;
6. 客戶端查詢遠端物件,並呼叫遠端方法;
1: 建立遠端介面,繼承 java.rmi.Remote 介面
public interface GreetService extends java.rmi.Remote {
String sayHello(String name) throws RemoteException;
} 2
:實現遠端介面,繼承 java.rmi.server.UnicastRemoteObject 類
public class GreetServiceImpl extends java.rmi.server.UnicastRemoteObject
implements GreetService {
private static final long serialVersionUID = 3434060152387200042L;
public GreetServiceImpl() throws RemoteException {
super();
}
@Override
public String sayHello(String name) throws RemoteException {
return "Hello " + name;
}
}
3:生成 Stub 和 Skeleton;
4: 執行 rmiregistry 命令註冊服務
5:啟動服務
LocateRegistry.createRegistry(1098);
Naming.bind("rmi://10.108.1.138:1098/GreetService", new GreetServiceImpl());
6.客戶端呼叫
GreetService greetService = (GreetService)
Naming.lookup("rmi://10.108.1.138:1098/GreetService");
System.out.println(greetService.sayHello("Jobs"));
8.1.5. Protoclol Buffer
protocol buffer 是 google 的一個開源專案,它是用於結構化資料序列化的靈活、高效、自動的方法,
例如 XML, 不過它比 xml 更小、更快、也更簡單。你可以定義自己的資料結構,然後使用程式碼生成器
生成的程式碼來讀寫這個資料結構。你甚至可以在無需重新部署程式的情況下更新資料結構。13/04/2018 Page 157 of 283
8.1.5.1. 特點
Protocol Buffer 的序列化 & 反序列化簡單 & 速度快的原因是:
1. 編碼 / 解碼 方式簡單(只需要簡單的數學運算 = 位移等等)
2. 採用 Protocol Buffer 自身的框架程式碼 和 編譯器 共同完成
Protocol Buffer 的資料壓縮效果好(即序列化後的資料量體積小)的原因是:
1. a. 採用了獨特的編碼方式, 如 Varint、 Zigzag 編碼方式等等
2. b. 採用 T - L - V 的資料儲存方式:減少了分隔符的使用 & 資料儲存得緊湊
8.1.6. Thrift
Apache Thrift 是 Facebook 實現的一種高效的、支援多種程式語言的遠端服務呼叫的框架。本文將從
Java 開發人員角度詳細介紹 Apache Thrift 的架構、開發和部署,並且針對不同的傳輸協議和服務類
型給出相應的 Java 例項,同時詳細介紹 Thrift 非同步客戶端的實現,最後提出使用 Thrift 需要注意的事
項。
目前流行的服務呼叫方式有很多種,例如基於 SOAP 訊息格式的 Web Service,基於 JSON 訊息格式
的 RESTful 服務等。其中所用到的資料傳輸方式包括 XML, JSON 等,然而 XML 相對體積太大,傳輸
效率低, JSON 體積較小,新穎,但還不夠完善。本文將介紹由 Facebook 開發的遠端服務呼叫框架
Apache Thrift, 它採用介面描述語言定義並建立服務,支援可擴充套件的跨語言服務開發,所包含的程式碼
生成引擎可以在多種語言中,如 C++, Java, Python, PHP, Ruby, Erlang, Perl, Haskell, C#, Cocoa,
Smalltalk 等建立高效的、無縫的服務,其傳輸資料採用二進位制格式, 相對 XML 和 JSON 體積更小,
對於高併發、大資料量和多語言的環境更有優勢。本文將詳細介紹 Thrift 的使用,並且提供豐富的例項
程式碼加以解釋說明,幫助使用者快速構建服務。
為什麼要 Thrift:
1、多語言開發的需要 2、效能問題13/04/2018 Page 158 of 28313/04/2018 Page 159 of 283
9. 網路
9.1.1. 網路 7 層架構
7 層模型主要包括:
1. 物理層:主要定義物理裝置標準,如網線的介面型別、光纖的介面型別、各種傳輸介質的傳輸速率
等。 它的主要作用是傳輸位元流(就是由 1、 0 轉化為電流強弱來進行傳輸,到達目的地後在轉化為
1、 0,也就是我們常說的模數轉換與數模轉換)。這一層的資料叫做位元。
2. 資料鏈路層:主要將從物理層接收的資料進行 MAC 地址(網絡卡的地址)的封裝與解封裝。常把這
一層的資料叫做幀。在這一層工作的裝置是交換機,資料通過交換機來傳輸。
3. 網路層:主要將從下層接收到的資料進行 IP 地址(例 192.168.0.1)的封裝與解封裝。在這一層工
作的裝置是路由器,常把這一層的資料叫做資料包。
4. 傳輸層:定義了一些傳輸資料的協議和埠號(WWW 埠 80 等),如: TCP(傳輸控制協議,
傳輸效率低,可靠性強,用於傳輸可靠性要求高,資料量大的資料), UDP(使用者資料報協議,
與 TCP 特性恰恰相反,用於傳輸可靠性要求不高,資料量小的資料,如 QQ 聊天資料就是通過這
種方式傳輸的)。 主要是將從下層接收的資料進行分段進行傳輸,到達目的地址後在進行重組。
常常把這一層資料叫做段。
5. 會話層:通過傳輸層(埠號:傳輸埠與接收埠) 建立資料傳輸的通路。主要在你的系統之間
發起會話或或者接受會話請求(裝置之間需要互相認識可以是 IP 也可以是 MAC 或者是主機名)
6. 表示層:主要是進行對接收的資料進行解釋、加密與解密、壓縮與解壓縮等(也就是把計算機能夠
識別的東西轉換成人能夠能識別的東西(如圖片、聲音等))
7. 應用層 主要是一些終端的應用,比如說FTP(各種檔案下載), WEB(IE瀏覽), QQ之類的(你
就把它理解成我們在電腦螢幕上可以看到的東西.就 是終端應用)。13/04/2018 Page 160 of 283
9.1.2. TCP/IP 原理
TCP/IP 協議不是 TCP 和 IP 這兩個協議的合稱,而是指因特網整個 TCP/IP 協議族。從協議分層
模型方面來講, TCP/IP 由四個層次組成:網路介面層、網路層、傳輸層、應用層。
9.1.2.1. 網路訪問層(Network Access Layer)
1. 網路訪問層(Network Access Layer)在 TCP/IP 參考模型中並沒有詳細描述, 只是指出主機
必須使用某種協議與網路相連。
9.1.2.2. 網路層(Internet Layer)
2. 網路層(Internet Layer)是整個體系結構的關鍵部分,其功能是使主機可以把分組發往任何網
絡,並使分組獨立地傳向目標。這些分組可能經由不同的網路,到達的順序和傳送的順序也
可能不同。高層如果需要順序收發,那麼就必須自行處理對分組的排序。 網際網路層使用因特
網協議(IP, Internet Protocol)。
9.1.2.3. 傳輸層(Tramsport Layer-TCP/UDP)
3. 傳輸層(Tramsport Layer)使源端和目的端機器上的對等實體可以進行會話。 在這一層定義了
兩個端到端的協議:傳輸控制協議(TCP, Transmission Control Protocol)和使用者資料報協
議(UDP, User Datagram Protocol)。 TCP 是面向連線的協議,它提供可靠的報文傳輸和對
上層應用的連線服務。為此,除了基本的資料傳輸外,它還有可靠性保證、流量控制、多路
複用、優先權和安全性控制等功能。 UDP 是面向無連線的不可靠傳輸的協議,主要用於不需
要 TCP 的排序和流量控制等功能的應用程式。
9.1.2.4. 應用層(Application Layer)
4. 應用層(Application Layer)包含所有的高層協議,包括: 虛擬終端協議(TELNET,
TELecommunications NETwork)、檔案傳輸協議(FTP, File Transfer Protocol)、電子郵件
傳輸協議(SMTP, Simple Mail Transfer Protocol)、域名服務(DNS, Domain Name13/04/2018 Page 161 of 283
Service)、網上新聞傳輸協議(NNTP, Net News Transfer Protocol)和超文字傳送協議
(HTTP, HyperText Transfer Protocol)等。
9.1.3. TCP 三次握手/四次揮手
TCP 在傳輸之前會進行三次溝通,一般稱為“三次握手”,傳完資料斷開的時候要進行四次溝通,一般
稱為“四次揮手”。
9.1.3.1. 資料包說明
1. 源埠號( 16 位):它(連同源主機 IP 地址)標識源主機的一個應用程序。
2. 目的埠號( 16 位):它(連同目的主機 IP 地址)標識目的主機的一個應用程序。這兩個值
加上 IP 報頭中的源主機 IP 地址和目的主機 IP 地址唯一確定一個 TCP 連線。
3. 順序號 seq( 32 位): 用來標識從 TCP 源端向 TCP 目的端傳送的資料位元組流,它表示在這個
報文段中的第一個資料位元組的順序號。如果將位元組流看作在兩個應用程式間的單向流動,則
TCP 用順序號對每個位元組進行計數。序號是 32bit 的無符號數, 序號到達 2 的 32 次方 - 1 後
又從 0 開始。 當建立一個新的連線時, SYN 標誌變 1 ,順序號欄位包含由這個主機選擇的該
連線的初始順序號 ISN ( Initial Sequence Number )。
4. 確認號 ack( 32 位): 包含傳送確認的一端所期望收到的下一個順序號。因此,確認序號應當
是上次已成功收到資料位元組順序號加 1 。 只有 ACK 標誌為 1 時確認序號欄位才有效。 TCP 為
應用層提供全雙工服務,這意味資料能在兩個方向上獨立地進行傳輸。因此,連線的每一端必
須保持每個方向上的傳輸資料順序號。
5. TCP 報頭長度( 4 位):給出報頭中 32bit 字的數目, 它實際上指明資料從哪裡開始。 需要這
個值是因為任選欄位的長度是可變的。這個欄位佔 4bit ,因此 TCP 最多有 60 位元組的首部。然
而,沒有任選欄位,正常的長度是 20 位元組。
6. 保留位( 6 位):保留給將來使用,目前必須置為 0 。
7. 控制位( control flags , 6 位):在 TCP 報頭中有 6 個標誌位元,它們中的多個可同時被設
置為 1 。依次為:
URG :為 1 表示緊急指標有效,為 0 則忽略緊急指標值。
ACK :為 1 表示確認號有效,為 0 表示報文中不包含確認資訊,忽略確認號欄位。
PSH :為 1 表示是帶有 PUSH 標誌的資料, 指示接收方應該儘快將這個報文段交給應用層
而不用等待緩衝區裝滿。
RST : 用於復位由於主機崩潰或其他原因而出現錯誤的連線。它還可以用於拒絕非法的報
文段和拒絕連線請求。一般情況下,如果收到一個 RST 為 1 的報文,那麼一定發生了某些
問題。
SYN :同步序號, 為 1 表示連線請求,用於建立連線和使順序號同步( synchronize )。
FIN : 用於釋放連線,為 1 表示傳送方已經沒有資料傳送了,即關閉本方資料流。
8. 視窗大小( 16 位):資料位元組數,表示從確認號開始,本報文的源方可以接收的位元組數,即源
方接收視窗大小。視窗大小是一個 16bit 欄位,因而視窗大小最大為 65535 位元組。
9. 校驗和( 16 位):此校驗和是對整個的 TCP 報文段, 包括 TCP 頭部和 TCP 資料,以 16 位字
進行計算所得。這是一個強制性的欄位,一定是由傳送端計算和儲存, 並由接收端進行驗證。
10. 緊急指標( 16 位):只有當 URG 標誌置 1 時緊急指標才有效。 TCP 的緊急方式是傳送端向另
一端傳送緊急資料的一種方式。13/04/2018 Page 162 of 283
11. 選項:最常見的可選欄位是最長報文大小,又稱為 MSS(Maximum Segment Size) 。每個連
接方通常都在通訊的第一個報文段(為建立連線而設定 SYN 標誌的那個段)中指明這個選項,
它指明本端所能接收的最大長度的報文段。選項長度不一定是 32 位字的整數倍,所以要加填充
位,使得報頭長度成為整字數。
12. 資料: TCP 報文段中的資料部分是可選的。在一個連線建立和一個連線終止時,雙方交換的報
文段僅有 TCP 首部。如果一方沒有資料要傳送,也使用沒有任何資料的首部來確認收到的數
據。在處理超時的許多情況中,也會發送不帶任何資料的報文段。
9.1.3.2. 三次握手
第一次握手:主機 A 傳送位碼為 syn= 1,隨機產生 seq number=1234567 的資料包到伺服器,主機 B
由 SYN=1 知道, A 要求建立聯機;
第 二 次 握 手 : 主 機 B 收 到 請 求 後 要 確 認 聯 機 信 息 , 向 A 發 送 ack number=( 主 機 A 的
seq+1),syn=1,ack=1,隨機產生 seq=7654321 的包
第三次握手: 主機 A 收到後檢查 ack number 是否正確,即第一次傳送的 seq number+1,以及位碼
ack 是否為 1,若正確, 主機 A 會再發送 ack number=(主機 B 的 seq+1),ack=1,主機 B 收到後確認13/04/2018 Page 163 of 283
seq 值與 ack=1 則連線建立成功。
9.1.3.3. 四次揮手
TCP 建立連線要進行三次握手,而斷開連線要進行四次。這是由於 TCP 的半關閉造成的。因為 TCP 連
接是全雙工的(即資料可在兩個方向上同時傳遞)所以進行關閉時每個方向上都要單獨進行關閉。這個單
方向的關閉就叫半關閉。當一方完成它的資料傳送任務,就傳送一個 FIN 來向另一方通告將要終止這個
方向的連線。
1) 關閉客戶端到伺服器的連線:首先客戶端 A 傳送一個 FIN,用來關閉客戶到伺服器的資料傳送,
然後等待伺服器的確認。其中終止標誌位 FIN=1,序列號 seq=u
2) 伺服器收到這個 FIN,它發回一個 ACK,確認號 ack 為收到的序號加 1。
3) 關閉伺服器到客戶端的連線:也是傳送一個 FIN 給客戶端。
4) 客戶段收到 FIN 後,併發回一個 ACK 報文確認,並將確認序號 seq 設定為收到序號加 1。
首先進行關閉的一方將執行主動關閉,而另一方執行被動關閉。13/04/2018 Page 164 of 283
主機 A 傳送 FIN 後,進入終止等待狀態, 伺服器 B 收到主機 A 連線釋放報文段後,就立即
給主機 A 傳送確認,然後伺服器 B 就進入 close-wait 狀態,此時 TCP 伺服器程序就通知高
層應用程序,因而從 A 到 B 的連線就釋放了。此時是“半關閉”狀態。即 A 不可以傳送給
B,但是 B 可以傳送給 A。此時,若 B 沒有資料報要傳送給 A 了,其應用程序就通知 TCP 釋
放連線,然後傳送給 A 連線釋放報文段,並等待確認。 A 傳送確認後,進入 time-wait,注
意,此時 TCP 連線還沒有釋放掉,然後經過時間等待計時器設定的 2MSL 後, A 才進入到
close 狀態。
9.1.4. HTTP 原理
HTTP 是一個無狀態的協議。無狀態是指客戶機(Web 瀏覽器)和伺服器之間不需要建立持久的連線,
這意味著當一個客戶端向伺服器端發出請求,然後伺服器返回響應(response),連線就被關閉了,在服
務器端不保留連線的有關資訊.HTTP 遵循請求(Request)/應答(Response)模型。客戶機(瀏覽器)向
伺服器傳送請求,伺服器處理請求並返回適當的應答。所有 HTTP 連線都被構造成一套請求和應答。
9.1.4.1. 傳輸流程
1:地址解析
如用客戶端瀏覽器請求這個頁面: http://localhost.com:8080/index.htm 從中分解出協議名、主機名、
埠、物件路徑等部分,對於我們的這個地址,解析得到的結果如下:
協議名: http
主機名: localhost.com
埠: 8080
物件路徑: /index.htm13/04/2018 Page 165 of 283
在這一步,需要域名系統 DNS 解析域名 localhost.com,得主機的 IP 地址。
2: 封裝 HTTP 請求資料包
把以上部分結合本機自己的資訊,封裝成一個 HTTP 請求資料包
3: 封裝成 TCP 包並建立連線
封裝成 TCP 包,建立 TCP 連線(TCP 的三次握手)
4: 客戶機發送請求命
4)客戶機發送請求命令: 建立連線後,客戶機發送一個請求給伺服器,請求方式的格式為:統一資
源識別符號(URL)、協議版本號,後邊是 MIME 資訊包括請求修飾符、客戶機資訊和可內容。
5: 伺服器響應
伺服器接到請求後,給予相應的響應資訊, 其格式為一個狀態行,包括資訊的協議版本號、一個成功或
錯誤的程式碼,後邊是 MIME 資訊包括伺服器資訊、實體資訊和可能的內容。
6: 伺服器關閉 TCP 連線
伺服器關閉 TCP 連線: 一般情況下,一旦 Web 伺服器向瀏覽器傳送了請求資料,它就要關閉 TCP 連
接,然後如果瀏覽器或者伺服器在其頭資訊加入了這行程式碼 Connection:keep-alive, TCP 連線在傳送
後將仍然保持開啟狀態,於是,瀏覽器可以繼續通過相同的連線傳送請求。保持連線節省了為每個請求
建立新連線所需的時間,還節約了網路頻寬。
9.1.4.2. HTTP 狀態
狀態碼 原因短語
訊息響應
100 Continue(繼續)
101 Switching Protocol(切換協議)13/04/2018 Page 166 of 283
成功響應
200 OK(成功)
201 Created(已建立)
202 Accepted(已建立)
203 Non-Authoritative Information(未授權資訊)
204 No Content(無內容)
205 Reset Content(重置內容)
206 Partial Content(部分內容)
重定向
300 Multiple Choice(多種選擇)
301 Moved Permanently(永久移動)
302 Found(臨時移動)
303 See Other(檢視其他位置)
304 Not Modified(未修改)
305 Use Proxy(使用代理)
306 unused(未使用)
307 Temporary Redirect(臨時重定向)
308 Permanent Redirect(永久重定向)
客戶端錯誤
400 Bad Request(錯誤請求)
401 Unauthorized(未授權)
402 Payment Required(需要付款)
403 Forbidden(禁止訪問)
404 Not Found(未找到)
405 Method Not Allowed(不允許使用該方法)
406 Not Acceptable(無法接受)
407 Proxy Authentication Required(要求代理身份驗證)
408 Request Timeout(請求超時)
409 Conflict(衝突)
410 Gone(已失效)
411 Length Required(需要內容長度頭)
412 Precondition Failed(預處理失敗)
413 Request Entity Too Large(請求實體過長)
414 Request-URI Too Long(請求網址過長)
415 Unsupported Media Type(媒體型別不支援)
416 Requested Range Not Satisfiable(請求範圍不合要求)
417 Expectation Failed(預期結果失敗)
伺服器端錯誤
500 Internal Server Error(內部伺服器錯誤)
501 Implemented(未實現)
502 Bad Gateway(閘道器錯誤)
503 Service Unavailable(服務不可用)
504 Gateway Timeout (閘道器超時)
505 HTTP Version Not Supported(HTTP 版本不受支援)
9.1.4.3. HTTPS
HTTPS(全稱: Hypertext Transfer Protocol over Secure Socket Layer),是以安全為目標的
HTTP 通道,簡單講是 HTTP 的安全版。即 HTTP 下加入 SSL 層, HTTPS 的安全基礎是 SSL。其所用
的埠號是 443。 過程大致如下:13/04/2018 Page 167 of 283
建立連接獲取證書
1) SSL 客戶端通過 TCP 和伺服器建立連線之後(443 埠),並且在一般的 tcp 連線協商(握
手)過程中請求證書。即客戶端發出一個訊息給伺服器,這個訊息裡面包含了自己可實現的算
法列表和其它一些需要的訊息, SSL 的伺服器端會迴應一個數據包,這裡面確定了這次通訊所
需要的演算法,然後伺服器向客戶端返回證書。(證書裡面包含了伺服器資訊:域名。申請證書
的公司,公共祕鑰)。
證書驗證
2) Client 在收到伺服器返回的證書後,判斷簽發這個證書的公共簽發機構,並使用這個機構的公
共祕鑰確認簽名是否有效,客戶端還會確保證書中列出的域名就是它正在連線的域名。
資料加密和傳輸
3) 如果確認證書有效,那麼生成對稱祕鑰並使用伺服器的公共祕鑰進行加密。然後傳送給服務
器,伺服器使用它的私鑰對它進行解密,這樣兩臺計算機可以開始進行對稱加密進行通訊。
9.1.5. CDN 原理
CND 一般包含分發服務系統、負載均衡系統和管理系統
9.1.5.1. 分發服務系統
其基本的工作單元就是各個 Cache 伺服器。負責直接響應使用者請求,將內容快速分發到使用者;同時還
負責內容更新,保證和源站內容的同步。13/04/2018 Page 168 of 283
根據內容型別和服務種類的不同,分發服務系統分為多個子服務系統,如: 網頁加速服務、流媒體加速
服務、應用加速服務等。每個子服務系統都是一個分散式的服務叢集,由功能類似、地域接近的分佈部
署的 Cache 叢集組成。
在承擔內容同步、更新和響應使用者請求之外,分發服務系統還需要向上層的管理排程系統反饋各個
Cache 裝置的健康狀況、響應情況、內容快取狀況等,以便管理排程系統能夠根據設定的策略決定由
哪個 Cache 裝置來響應使用者的請求。
9.1.5.2. 負載均衡系統:
負載均衡系統是整個 CDN 系統的中樞。負責對所有的使用者請求進行排程,確定提供給使用者的最終訪問
地址。
使用分級實現。最基本的兩極排程體系包括全域性負載均衡(GSLB)和本地負載均衡(SLB)。
GSLB 根據使用者地址和使用者請求的內容, 主要根據就近性原則,確定向用戶服務的節點。一般通過 DNS
解析或者應用層重定向(Http 3XX 重定向)的方式實現。
SLB 主要負責節點內部的負載均衡。當用戶請求從 GSLB 排程到 SLB 時, SLB 會根據節點內各個
Cache 裝置的工作狀況和內容分佈情況等對使用者請求重定向。 SLB 的實現有四層排程(LVS)、七層調
度(Nginx)和鏈路負載排程等。
9.1.5.3. 管理系統:
分為運營管理和網路管理子系統。
網路管理系統實現對 CDN 系統的裝置管理、拓撲管理、鏈路監控和故障管理,為管理員提供對全網資
源的視覺化的集中管理,通常用 web 方式實現。
運營管理是對 CDN 系統的業務管理,負責處理業務層面的與外界系統互動所必須的一些收集、整理、
交付工作。 包括使用者管理、產品管理、計費管理、統計分析等。13/04/2018 Page 169 of 283
10. 日誌
10.1.1. Slf4j
slf4j 的全稱是 Simple Loging Facade For Java,即它僅僅是一個為 Java 程式提供日誌輸出的統一接
口,並不是一個具體的日誌實現方案,就比如 JDBC 一樣,只是一種規則而已。所以單獨的 slf4j 是不
能工作的,必須搭配其他具體的日誌實現方案,比如 apache 的 org.apache.log4j.Logger, jdk 自帶
的 java.util.logging.Logger 等。
10.1.2. Log4j
Log4j 是 Apache 的一個開源專案,通過使用 Log4j,我們可以控制日誌資訊輸送的目的地是控制檯、
檔案、 GUI 元件,甚至是套介面伺服器、 NT 的事件記錄器、 UNIX Syslog 守護程序等;我們也可以控
制每一條日誌的輸出格式;通過定義每一條日誌資訊的級別,我們能夠更加細緻地控制日誌的生成過程。
Log4j 由三個重要的組成構成:日誌記錄器(Loggers),輸出端(Appenders)和日誌格式化器(Layout)。
1.Logger:控制要啟用或禁用哪些日誌記錄語句,並對日誌資訊進行級別限制
2.Appenders : 指定了日誌將列印到控制檯還是檔案中
3.Layout : 控制日誌資訊的顯示格式
Log4j 中將要輸出的 Log 資訊定義了 5 種級別,依次為 DEBUG、 INFO、 WARN、 ERROR 和 FATAL,
當輸出時,只有級別高過配置中規定的 級別的資訊才能真正的輸出,這樣就很方便的來配置不同情況
下要輸出的內容,而不需要更改程式碼。
10.1.3. LogBack
簡單地說, Logback 是一個 Java 領域的日誌框架。它被認為是 Log4J 的繼承人。
Logback 主要由三個模組組成: logback-core, logback-classic。 logback-access
logback-core 是其它模組的基礎設施,其它模組基於它構建,顯然, logback-core 提供了一些關鍵的
通用機制。
logback-classic 的地位和作用等同於 Log4J,它也被認為是 Log4J 的一個改進版,並且它實現了簡單
日誌門面 SLF4J;
logback-access 主要作為一個與 Servlet 容器互動的模組,比如說 tomcat 或者 jetty,提供一些與
HTTP 訪問相關的功能。
10.1.3.1. Logback 優點
同樣的程式碼路徑, Logback 執行更快
更充分的測試
原生實現了 SLF4J API(Log4J 還需要有一箇中間轉換層)
內容更豐富的文件
支援 XML 或者 Groovy 方式配置
配置檔案自動熱載入13/04/2018 Page 170 of 283
從 IO 錯誤中優雅恢復
自動刪除日誌歸檔
自動壓縮日誌成為歸檔檔案
支援 Prudent 模式,使多個 JVM 程序能記錄同一個日誌檔案
支援配置檔案中加入條件判斷來適應不同的環境
更強大的過濾器
支援 SiftingAppender(可篩選 Appender)
異常棧資訊帶有包資訊
10.1.4. ELK
ELK 是軟體集合 Elasticsearch、 Logstash、 Kibana 的簡稱,由這三個軟體及其相關的元件可以打
造大規模日誌實時處理系統。
Elasticsearch 是一個基於 Lucene 的、支援全文索引的分散式儲存和索引引擎,主要負責將
日誌索引並存儲起來,方便業務方檢索查詢。
Logstash 是一個日誌收集、過濾、轉發的中介軟體,主要負責將各條業務線的各類日誌統一收
集、過濾後,轉發給 Elasticsearch 進行下一步處理。
Kibana 是一個視覺化工具,主要負責查詢 Elasticsearch 的資料並以視覺化的方式展現給業
務方,比如各類餅圖、直方圖、區域圖等。13/04/2018 Page 171 of 283
11. Zookeeper
11.1.1. Zookeeper 概念
Zookeeper 是一個分散式協調服務,可用於服務發現,分散式鎖,分散式領導選舉,配置管理等。
Zookeeper 提供了一個類似於 Linux 檔案系統的樹形結構(可認為是輕量級的記憶體檔案系統,但
只適合存少量資訊,完全不適合儲存大量檔案或者大檔案),同時提供了對於每個節點的監控與
通知機制。
11.1.1. Zookeeper 角色
Zookeeper 叢集是一個基於主從複製的高可用叢集,每個伺服器承擔如下三種角色中的一種
11.1.1.1. Leader
1. 一個 Zookeeper 叢集同一時間只會有一個實際工作的 Leader,它會發起並維護與各 Follwer
及 Observer 間的心跳。
2. 所有的寫操作必須要通過 Leader 完成再由 Leader 將寫操作廣播給其它伺服器。 只要有超過
半數節點(不包括 observeer 節點) 寫入成功,該寫請求就會被提交(類 2PC 協議)。
11.1.1.2. Follower
1. 一個 Zookeeper 叢集可能同時存在多個 Follower,它會響應 Leader 的心跳,
2. Follower 可直接處理並返回客戶端的讀請求,同時會將寫請求轉發給 Leader 處理,
3. 並且負責在 Leader 處理寫請求時對請求進行投票。
11.1.1.3. Observer
角色與 Follower 類似,但是無投票權。 Zookeeper 需保證高可用和強一致性,為了支援更多的客
戶端,需要增加更多 Server; Server 增多,投票階段延遲增大,影響效能; 引入 Observer,
Observer 不參與投票; Observers 接受客戶端的連線,並將寫請求轉發給 leader 節點; 加入更
多 Observer 節點,提高伸縮性,同時不影響吞吐率。13/04/2018 Page 172 of 283
11.1.1.1. ZAB 協議
事務編號 Zxid(事務請求計數器+ epoch)
在 ZAB ( ZooKeeper Atomic Broadcast , ZooKeeper 原子訊息廣播協議) 協議的事務編號 Zxid
設計中, Zxid 是一個 64 位的數字,其中低 32 位是一個簡單的單調遞增的計數器, 針對客戶端每
一個事務請求,計數器加 1;而高 32 位則代表 Leader 週期 epoch 的編號, 每個當選產生一個新
的 Leader 伺服器,就會從這個 Leader 伺服器上取出其本地日誌中最大事務的 ZXID,並從中讀取
epoch 值,然後加 1,以此作為新的 epoch,並將低 32 位從 0 開始計數。
Zxid(Transaction id) 類似於 RDBMS 中的事務 ID,用於標識一次更新操作的 Proposal(提議)
ID。為了保證順序性,該 zkid 必須單調遞增。
epoch
epoch:可以理解為當前叢集所處的年代或者週期,每個 leader 就像皇帝,都有自己的年號,所
以每次改朝換代, leader 變更之後,都會在前一個年代的基礎上加 1。這樣就算舊的 leader 崩潰
恢復之後,也沒有人聽他的了,因為 follower 只聽從當前年代的 leader 的命令。
Zab 協議有兩種模式-恢復模式(選主)、廣播模式(同步)
Zab 協議有兩種模式,它們分別是恢復模式(選主)和廣播模式(同步) 。當服務啟動或者在領導
者崩潰後, Zab 就進入了恢復模式,當領導者被選舉出來,且大多數 Server 完成了和 leader 的狀
態同步以後,恢復模式就結束了。狀態同步保證了 leader 和 Server 具有相同的系統狀態。
ZAB 協議 4 階段
Leader election(選舉階段-選出準 Leader)
1. Leader election(選舉階段) : 節點在一開始都處於選舉階段,只要有一個節點得到超半數
節點的票數,它就可以當選準 leader。只有到達 廣播階段(broadcast) 準 leader 才會成
為真正的 leader。這一階段的目的是就是為了選出一個準 leader,然後進入下一個階段。13/04/2018 Page 173 of 283
Discovery(發現階段-接受提議、生成 epoch、接受 epoch)
2. Discovery(發現階段) : 在這個階段, followers 跟準 leader 進行通訊,同步 followers
最近接收的事務提議。這個一階段的主要目的是發現當前大多數節點接收的最新提議,並且
準 leader 生成新的 epoch,讓 followers 接受,更新它們的 accepted Epoch
一個 follower 只會連線一個 leader, 如果有一個節點 f 認為另一個 follower p 是 leader, f
在嘗試連線 p 時會被拒絕, f 被拒絕之後,就會進入重新選舉階段。
Synchronization(同步階段-同步 follower 副本)
3. Synchronization(同步階段) : 同步階段主要是利用 leader 前一階段獲得的最新提議歷史,
同步叢集中所有的副本。 只有當 大多數節點都同步完成,準 leader 才會成為真正的 leader。
follower 只會接收 zxid 比自己的 lastZxid 大的提議。
Broadcast(廣播階段-leader 訊息廣播)
4. Broadcast(廣播階段) : 到了這個階段, Zookeeper 叢集才能正式對外提供事務服務,
並且 leader 可以進行訊息廣播。同時如果有新的節點加入,還需要對新節點進行同步。
ZAB 提交事務並不像 2PC 一樣需要全部 follower 都 ACK, 只需要得到超過半數的節點的 ACK 就
可以了。
ZAB 協議 JAVA 實現(FLE-發現階段和同步合併為 Recovery Phase(恢復階段) )
協議的 Java 版本實現跟上面的定義有些不同,選舉階段使用的是 Fast Leader Election(FLE),
它包含了 選舉的發現職責。因為 FLE 會選舉擁有最新提議歷史的節點作為 leader,這樣就省去了
發現最新提議的步驟。實際的實現將 發現階段 和 同步合併為 Recovery Phase(恢復階段)。所
以, ZAB 的實現只有三個階段: Fast Leader Election; Recovery Phase; Broadcast Phase。
11.1.1.2. 投票機制
每個 sever 首先給自己投票, 然後用自己的選票和其他 sever 選票對比, 權重大的勝出,使用權
重較大的更新自身選票箱。 具體選舉過程如下:
1. 每個 Server 啟動以後都詢問其它的 Server 它要投票給誰。對於其他 server 的詢問,
server 每次根據自己的狀態都回復自己推薦的 leader 的 id 和上一次處理事務的 zxid(系
統啟動時每個 server 都會推薦自己)
2. 收到所有 Server 回覆以後,就計算出 zxid 最大的哪個 Server,並將這個 Server 相關信
息設定成下一次要投票的 Server。
3. 計算這過程中獲得票數最多的的 sever 為獲勝者,如果獲勝者的票數超過半數,則改
server 被選為 leader。否則,繼續這個過程,直到 leader 被選舉出來
4. leader 就會開始等待 server 連線
5. Follower 連線 leader,將最大的 zxid 傳送給 leader
6. Leader 根據 follower 的 zxid 確定同步點,至此選舉階段完成。
7. 選舉階段完成 Leader 同步後通知 follower 已經成為 uptodate 狀態
8. Follower 收到 uptodate 訊息後,又可以重新接受 client 的請求進行服務了13/04/2018 Page 174 of 283
目前有 5 臺伺服器,每臺伺服器均沒有資料,它們的編號分別是 1,2,3,4,5,按編號依次啟動,它們
的選擇舉過程如下:
1. 伺服器 1 啟動,給自己投票,然後發投票資訊,由於其它機器還沒有啟動所以它收不到反
饋資訊,伺服器 1 的狀態一直屬於 Looking。
2. 伺服器 2 啟動,給自己投票,同時與之前啟動的伺服器 1 交換結果,由於伺服器 2 的編號
大所以伺服器 2 勝出,但此時投票數沒有大於半數,所以兩個伺服器的狀態依然是
LOOKING。
3. 伺服器 3 啟動,給自己投票,同時與之前啟動的伺服器 1,2 交換資訊,由於伺服器 3 的編
號最大所以伺服器 3 勝出,此時投票數正好大於半數,所以伺服器 3 成為領導者,伺服器
1,2 成為小弟。
4. 伺服器 4 啟動,給自己投票,同時與之前啟動的伺服器 1,2,3 交換資訊,儘管伺服器 4 的
編號大,但之前伺服器 3 已經勝出,所以伺服器 4 只能成為小弟。
5. 伺服器 5 啟動,後面的邏輯同伺服器 4 成為小弟。
11.1.2. Zookeeper 工作原理(原子廣播)
1. Zookeeper 的核心是原子廣播,這個機制保證了各個 server 之間的同步。實現這個機制
的協議叫做 Zab 協議。 Zab 協議有兩種模式,它們分別是恢復模式和廣播模式。
2. 當服務啟動或者在領導者崩潰後, Zab 就進入了恢復模式,當領導者被選舉出來,且大多
數 server 的完成了和 leader 的狀態同步以後,恢復模式就結束了。
3. 狀態同步保證了 leader 和 server 具有相同的系統狀態
4. 一旦 leader 已經和多數的 follower 進行了狀態同步後,他就可以開始廣播訊息了,即進
入廣播狀態。這時候當一個 server 加入 zookeeper 服務中,它會在恢復模式下啟動,發
現 leader,並和 leader 進行狀態同步。待到同步結束,它也參與訊息廣播。 Zookeeper
服務一直維持在 Broadcast 狀態,直到 leader 崩潰了或者 leader 失去了大部分的
followers 支援。
5. 廣播模式需要保證 proposal 被按順序處理,因此 zk 採用了遞增的事務 id 號(zxid)來保
證。所有的提議(proposal)都在被提出的時候加上了 zxid。
6. 實現中 zxid 是一個 64 為的數字,它高 32 位是 epoch 用來標識 leader 關係是否改變,
每次一個 leader 被選出來,它都會有一個新的 epoch。低 32 位是個遞增計數。
7. 當 leader 崩潰或者 leader 失去大多數的 follower,這時候 zk 進入恢復模式,恢復模式
需要重新選舉出一個新的 leader,讓所有的 server 都恢復到一個正確的狀態。
11.1.3. Znode 有四種形式的目錄節點
1. PERSISTENT:持久的節點。
2. EPHEMERAL: 暫時的節點。
3. PERSISTENT_SEQUENTIAL:持久化順序編號目錄節點。
4. EPHEMERAL_SEQUENTIAL:暫時化順序編號目錄節點。13/04/2018 Page 175 of 283
12. Kafka
12.1.1. Kafka 概念
Kafka 是一種高吞吐量、分散式、基於釋出/訂閱的訊息系統,最初由 LinkedIn 公司開發,使用
Scala 語言編寫,目前是 Apache 的開源專案。
1. broker: Kafka 伺服器,負責訊息儲存和轉發
2. topic:訊息類別, Kafka 按照 topic 來分類訊息
3. partition: topic 的分割槽,一個 topic 可以包含多個 partition, topic 訊息儲存在各個
partition 上
4. offset:訊息在日誌中的位置,可以理解是訊息在 partition 上的偏移量,也是代表該訊息的
唯一序號
5. Producer:訊息生產者
6. Consumer:訊息消費者
7. Consumer Group:消費者分組,每個 Consumer 必須屬於一個 group
8. Zookeeper:儲存著叢集 broker、 topic、 partition 等 meta 資料;另外,還負責 broker 故
障發現, partition leader 選舉,負載均衡等功能
12.1.2. Kafka 資料儲存設計
12.1.2.1. partition 的資料檔案(offset, MessageSize, data)
partition 中的每條 Message 包含了以下三個屬性: offset, MessageSize, data, 其中 offset 表
示 Message 在這個 partition 中的偏移量, offset 不是該 Message 在 partition 資料檔案中的實13/04/2018 Page 176 of 283
際儲存位置,而是邏輯上一個值,它唯一確定了 partition 中的一條 Message,可以認為 offset 是
partition 中 Message 的 id; MessageSize 表示訊息內容 data 的大小; data 為 Message 的具
體內容。
12.1.2.2. 資料檔案分段 segment(順序讀寫、分段命令、二分查詢)
partition 物理上由多個 segment 檔案組成,每個 segment 大小相等,順序讀寫。每個 segment
資料檔案以該段中最小的 offset 命名,副檔名為.log。這樣在查詢指定 offset 的 Message 的
時候,用二分查詢就可以定位到該 Message 在哪個 segment 資料檔案中。
12.1.2.3. 資料檔案索引(分段索引、 稀疏儲存)
Kafka 為每個分段後的資料檔案建立了索引檔案,檔名與資料檔案的名字是一樣的,只是檔案擴
展名為.index。 index 檔案中並沒有為資料檔案中的每條 Message 建立索引,而是採用了稀疏存
儲的方式,每隔一定位元組的資料建立一條索引。這樣避免了索引檔案佔用過多的空間,從而可以
將索引檔案保留在記憶體中。
12.1.3. 生產者設計
12.1.3.1. 負載均衡(partition 會均衡分佈到不同 broker 上)
由於訊息 topic 由多個 partition 組成, 且 partition 會均衡分佈到不同 broker 上,因此,為了有
效利用 broker 叢集的效能,提高訊息的吞吐量, producer 可以通過隨機或者 hash 等方式,將消
息平均傳送到多個 partition 上,以實現負載均衡。13/04/2018 Page 177 of 283
12.1.3.2. 批量傳送
是提高訊息吞吐量重要的方式, Producer 端可以在記憶體中合併多條訊息後, 以一次請求的方式發
送了批量的訊息給 broker,從而大大減少 broker 儲存訊息的 IO 操作次數。但也一定程度上影響
了訊息的實時性,相當於以時延代價,換取更好的吞吐量。
12.1.3.3. 壓縮(GZIP 或 Snappy)
Producer 端可以通過 GZIP 或 Snappy 格式對訊息集合進行壓縮。 Producer 端進行壓縮之後,在
Consumer 端需進行解壓。壓縮的好處就是減少傳輸的資料量,減輕對網路傳輸的壓力,在對大
資料處理上,瓶頸往往體現在網路上而不是 CPU(壓縮和解壓會耗掉部分 CPU 資源)。
12.1.1. 消費者設計13/04/2018 Page 178 of 283
12.1.1.1. Consumer Group
同一 Consumer Group 中的多個 Consumer 例項,不同時消費同一個 partition,等效於佇列模
式。 partition 內訊息是有序的, Consumer 通過 pull 方式消費訊息。 Kafka 不刪除已消費的訊息
對於 partition,順序讀寫磁碟資料,以時間複雜度 O(1)方式提供訊息持久化能力。13/04/2018 Page 179 of 283
13. RabbitMQ
13.1.1. 概念
RabbitMQ 是一個由 Erlang 語言開發的 AMQP 的開源實現。
AMQP : Advanced Message Queue,高階訊息佇列協議。它是應用層協議的一個開放標準,為
面向訊息的中介軟體設計,基於此協議的客戶端與訊息中介軟體可傳遞訊息,並不受產品、開發語言
等條件的限制。
RabbitMQ 最初起源於金融系統,用於在分散式系統中儲存轉發訊息,在易用性、擴充套件性、高可
用性等方面表現不俗。具體特點包括:
1. 可靠性(Reliability) : RabbitMQ 使用一些機制來保證可靠性,如持久化、傳輸確認、釋出
確認。
2. 靈活的路由(Flexible Routing) : 在訊息進入佇列之前,通過 Exchange 來路由訊息的。對
於典型的路由功能, RabbitMQ 已經提供了一些內建的 Exchange 來實現。針對更復雜的路
由功能,可以將多個 Exchange 繫結在一起,也通過外掛機制實現自己的 Exchange 。
3. 訊息叢集(Clustering) : 多個 RabbitMQ 伺服器可以組成一個叢集,形成一個邏輯 Broker 。
4. 高可用(Highly Available Queues) : 佇列可以在叢集中的機器上進行映象, 使得在部分節
點出問題的情況下佇列仍然可用。
5. 多種協議(Multi-protocol) : RabbitMQ 支援多種訊息佇列協議,比如 STOMP、 MQTT
等等。
6. 多語言客戶端(Many Clients) : RabbitMQ 幾乎支援所有常用語言,比如 Java、 .NET、
Ruby 等等。
7. 管理介面(Management UI) :RabbitMQ 提供了一個易用的使用者介面,使得使用者可以監控
和管理訊息 Broker 的許多方面。
8. 跟蹤機制(Tracing) :如果訊息異常, RabbitMQ 提供了訊息跟蹤機制,使用者可以找出發生
了什麼。
9. 外掛機制(Plugin System) :RabbitMQ 提供了許多外掛,來從多方面進行擴充套件,也可以編
寫自己的外掛。
13.1.2. RabbitMQ 架構13/04/2018 Page 180 of 283
13.1.2.1. Message
訊息,訊息是不具名的,它由訊息頭和訊息體組成。訊息體是不透明的,而訊息頭則由一系
列的可選屬性組成,這些屬性包括 routing-key(路由鍵)、 priority(相對於其他訊息的優
先權)、 delivery-mode(指出該訊息可能需要永續性儲存)等。
13.1.2.2. Publisher
1. 訊息的生產者,也是一個向交換器釋出訊息的客戶端應用程式。
13.1.2.3. Exchange(將訊息路由給佇列 )
2. 交換器,用來接收生產者傳送的訊息並將這些訊息路由給伺服器中的佇列。
13.1.2.4. Binding(訊息佇列和交換器之間的關聯)
3. 繫結,用於訊息佇列和交換器之間的關聯。一個繫結就是基於路由鍵將交換器和訊息佇列連
接起來的路由規則,所以可以將交換器理解成一個由繫結構成的路由表。
13.1.2.5. Queue
4. 訊息佇列,用來儲存訊息直到傳送給消費者。它是訊息的容器,也是訊息的終點。 一個訊息
可投入一個或多個佇列。訊息一直在佇列裡面,等待消費者連線到這個佇列將其取走。
13.1.2.6. Connection
5. 網路連線,比如一個 TCP 連線。
13.1.2.7. Channel
6. 通道, 多路複用連線中的一條獨立的雙向資料流通道。通道是建立在真實的 TCP 連線內地虛
擬連線, AMQP 命令都是通過通道發出去的,不管是釋出訊息、訂閱佇列還是接收訊息,這
些動作都是通過通道完成。因為對於作業系統來說建立和銷燬 TCP 都是非常昂貴的開銷,所
以引入了通道的概念,以複用一條 TCP 連線。
13.1.2.8. Consumer
7. 訊息的消費者,表示一個從訊息佇列中取得訊息的客戶端應用程式。
13.1.2.9. Virtual Host
8. 虛擬主機,表示一批交換器、訊息佇列和相關物件。虛擬主機是共享相同的身份認證和加密
環境的獨立伺服器域。13/04/2018 Page 181 of 283
13.1.2.10.Broker
9. 表示訊息佇列伺服器實體。
13.1.3. Exchange 型別
Exchange 分發訊息時根據型別的不同分發策略有區別, 目前共四種類型: direct、 fanout、
topic、 headers 。 headers 匹配 AMQP 訊息的 header 而不是路由鍵,此外 headers 交換器和
direct 交換器完全一致,但效能差很多,目前幾乎用不到了,所以直接看另外三種類型:
13.1.3.1. Direct 鍵(routing key)分佈:
1. Direct: 訊息中的路由鍵(routing key)如果和 Binding 中的 binding key 一致,
交換器就將訊息發到對應的佇列中。它是完全匹配、單播的模式。
13.1.3.2. Fanout(廣播分發)
2. Fanout: 每個發到 fanout 型別交換器的訊息都會分到所有繫結的佇列上去。很像子
網廣播,每臺子網內的主機都獲得了一份複製的訊息。 fanout 型別轉發訊息是最快
的。13/04/2018 Page 182 of 283
13.1.3.3. topic 交換器(模式匹配)
3. topic 交換器: topic 交換器通過模式匹配分配訊息的路由鍵屬性,將路由鍵和某個模
式進行匹配,此時佇列需要繫結到一個模式上。它將路由鍵和繫結鍵的字串切分成
單詞,這些單詞之間用點隔開。它同樣也會識別兩個萬用字元:符號“#” 和符號
“” 。 #匹配 0 個或多個單詞,匹配不多不少一個單詞。13/04/2018 Page 183 of 283
14. Hbase
14.1.1. 概念
base 是分散式、面向列的開源資料庫(其實準確的說是面向列族)。 HDFS 為 Hbase 提供可靠的
底層資料儲存服務, MapReduce 為 Hbase 提供高效能的計算能力, Zookeeper 為 Hbase 提供
穩定服務和 Failover 機制,因此我們說 Hbase 是一個通過大量廉價的機器解決海量資料的高速存
儲和讀取的分散式資料庫解決方案。
14.1.2. 列式儲存
列方式所帶來的重要好處之一就是,由於查詢中的選擇規則是通過列來定義的,因此整個資料庫
是自動索引化的。
這裡的列式儲存其實說的是列族儲存, Hbase 是根據列族來儲存資料的。列族下面可以有非常多
的列,列族在建立表的時候就必須指定。為了加深對 Hbase 列族的理解,下面是一個簡單的關係
型資料庫的表和 Hbase 資料庫的表:13/04/2018 Page 184 of 283
14.1.3. Hbase 核心概念
14.1.3.1. Column Family 列族
Column Family 又叫列族, Hbase 通過列族劃分資料的儲存,列族下面可以包含任意多的列,實
現靈活的資料存取。 Hbase 表的建立的時候就必須指定列族。就像關係型資料庫建立的時候必須
指定具體的列是一樣的。 Hbase 的列族不是越多越好,官方推薦的是列族最好小於或者等於 3。我
們使用的場景一般是 1 個列族。
14.1.3.2. Rowkey(Rowkey 查詢, Rowkey 範圍掃描,全表掃描)
Rowkey 的概念和 mysql 中的主鍵是完全一樣的, Hbase 使用 Rowkey 來唯一的區分某一行的數
據。 Hbase 只支援 3 中查詢方式:基於 Rowkey 的單行查詢,基於 Rowkey 的範圍掃描,全表掃
描。
14.1.3.3. Region 分割槽
Region: Region 的概念和關係型資料庫的分割槽或者分片差不多。 Hbase 會將一個大表的數
據基於 Rowkey 的不同範圍分配到不通的 Region 中,每個 Region 負責一定範圍的資料訪問
和儲存。這樣即使是一張巨大的表,由於被切割到不通的 region,訪問起來的時延也很低。
14.1.3.4. TimeStamp 多版本
TimeStamp 是實現 Hbase 多版本的關鍵。在 Hbase 中使用不同的 timestame 來標識相同
rowkey 行對應的不通版本的資料。在寫入資料的時候,如果使用者沒有指定對應的
timestamp, Hbase 會自動新增一個 timestamp, timestamp 和伺服器時間保持一致。 在
Hbase 中,相同 rowkey 的資料按照 timestamp 倒序排列。預設查詢的是最新的版本,使用者
可同指定 timestamp 的值來讀取舊版本的資料。
14.1.4. Hbase 核心架構
Hbase 是由 Client、 Zookeeper、 Master、 HRegionServer、 HDFS 等幾個組建組成。13/04/2018 Page 185 of 283
14.1.4.1. Client:
Client 包含了訪問 Hbase 的介面,另外 Client 還維護了對應的 cache 來加速 Hbase 的
訪問,比如 cache 的.META.元資料的資訊。
14.1.4.2. Zookeeper:
Hbase 通過 Zookeeper 來做 master 的高可用、 RegionServer 的監控、元資料的入口
以及叢集配置的維護等工作。具體工作如下:
1. 通過 Zoopkeeper 來保證叢集中只有 1 個 master 在執行,如果 master 異
常,會通過競爭機制產生新的 master 提供服務
2. 通過 Zoopkeeper 來監控 RegionServer 的狀態,當 RegionSevrer 有異常的
時候,通過回撥的形式通知 Master RegionServer 上下限的資訊
3. 通過 Zoopkeeper 儲存元資料的統一入口地址。
14.1.4.3. Hmaster
master 節點的主要職責如下:
1. 為 RegionServer 分配 Region
2. 維護整個叢集的負載均衡
3. 維護叢集的元資料資訊發現失效的 Region,並將失效的 Region 分配到正常
RegionServer 上當 RegionSever 失效的時候,協調對應 Hlog 的拆分
14.1.4.4. HregionServer
HregionServer 直接對接使用者的讀寫請求,是真正的“幹活”的節點。它的功能概括如
下:
1. 管理 master 為其分配的 Region13/04/2018 Page 186 of 283
2. 處理來自客戶端的讀寫請求
3. 負責和底層 HDFS 的互動,儲存資料到 HDFS
4. 負責 Region 變大以後的拆分
5. 負責 Storefile 的合併工作
14.1.4.5. Region 定址方式(通過 zookeeper .META)
第 1 步: Client 請求 ZK 獲取.META.所在的 RegionServer 的地址。
第 2 步: Client 請求.META.所在的 RegionServer 獲取訪問資料所在的 RegionServer 地
址, client 會將.META.的相關資訊 cache 下來,以便下一次快速訪問。
第 3 步: Client 請求資料所在的 RegionServer,獲取所需要的資料。
14.1.4.6. HDFS
HDFS 為 Hbase 提供最終的底層資料儲存服務,同時為 Hbase 提供高可用(Hlog 儲存在
HDFS)的支援。13/04/2018 Page 187 of 283
14.1.5. Hbase 的寫邏輯
14.1.5.1. Hbase 的寫入流程
從上圖可以看出氛圍 3 步驟:
獲取 RegionServer
第 1 步: Client 獲取資料寫入的 Region 所在的 RegionServer
請求寫 Hlog
第 2 步:請求寫 Hlog, Hlog 儲存在 HDFS,當 RegionServer 出現異常,需要使用 Hlog 來
恢復資料。
請求寫 MemStore
第 3 步:請求寫 MemStore,只有當寫 Hlog 和寫 MemStore 都成功了才算請求寫入完成。
MemStore 後續會逐漸刷到 HDFS 中。
14.1.5.2. MemStore 刷盤
為了提高 Hbase 的寫入效能,當寫請求寫入 MemStore 後,不會立即刷盤。而是會等到一
定的時候進行刷盤的操作。具體是哪些場景會觸發刷盤的操作呢?總結成如下的幾個場景:13/04/2018 Page 188 of 283
全域性記憶體控制
1. 這個全域性的引數是控制記憶體整體的使用情況,當所有 memstore 佔整個 heap 的最大比
例的時候,會觸發刷盤的操作。這個引數是
hbase.regionserver.global.memstore.upperLimit,預設為整個 heap 記憶體的 40%。
但這並不意味著全域性記憶體觸發的刷盤操作會將所有的 MemStore 都進行輸盤,而是通過
另外一個引數 hbase.regionserver.global.memstore.lowerLimit 來控制,預設是整個
heap 記憶體的 35%。當 flush 到所有 memstore 佔整個 heap 記憶體的比率為 35%的時
候,就停止刷盤。這麼做主要是為了減少刷盤對業務帶來的影響,實現平滑系統負載的
目的。
MemStore 達到上限
2. 當 MemStore 的大小達到 hbase.hregion.memstore.flush.size 大小的時候會觸發刷
盤,預設 128M 大小
RegionServer 的 Hlog 數量達到上限
3. 前面說到 Hlog 為了保證 Hbase 資料的一致性,那麼如果 Hlog 太多的話,會導致故障
恢復的時間太長,因此 Hbase 會對 Hlog 的最大個數做限制。當達到 Hlog 的最大個數
的時候,會強制刷盤。這個引數是 hase.regionserver.max.logs,預設是 32 個。
手工觸發
4. 可以通過 hbase shell 或者 java api 手工觸發 flush 的操作。
關閉 RegionServer 觸發
5. 在正常關閉 RegionServer 會觸發刷盤的操作,全部資料刷盤後就不需要再使用 Hlog 恢
複數據。
Region 使用 HLOG 恢復完資料後觸發
6. : 當 RegionServer 出現故障的時候,其上面的 Region 會遷移到其他正常的
RegionServer 上,在恢復完 Region 的資料後,會觸發刷盤,當刷盤完成後才會提供給
業務訪問。
14.1.6. HBase vs Cassandra
HBase Cassandra
語言 Java Java
出發點 BigTable BigTable and Dynamo
License Apache Apache
Protocol HTTP/REST (also Thrift) Custom, binary (Thrift)
資料分佈 表劃分為多個 region 存在不同 region
server 上
改進的一致性雜湊(虛擬節點)
儲存目標 大檔案 小檔案
一致性 強一致性 最終一致性, Quorum NRW 策略
架構 master/slave p2p
高可用性 NameNode 是 HDFS 的單點故障點 P2P 和去中心化設計,不會出現單點故障
伸縮性 Region Server 擴容,通過將自身釋出到
Master, Master 均勻分佈 Region
擴容需在 Hash Ring 上多個節點間調整資料分佈13/04/2018 Page 189 of 283
讀寫效能 資料讀寫定位可能要通過最多 6 次的網
絡 RPC,效能較低。
資料讀寫定位非常快
資料衝突處理 樂觀併發控制(optimistic concurrency
control)
向量時鐘
臨時故障處理 Region Server 宕機,重做 HLog 資料回傳機制:某節點宕機, hash 到該節點的新資料自
動路由到下一節點做 hinted handoff,源節點恢復後,推
送回源節點。
永久故障恢復 Region Server 恢復, master 重新給其
分配 region
Merkle 雜湊樹,通過 Gossip 協議同步 Merkle Tree,維
護叢集節點間的資料一致性
成員通訊及錯誤檢測 Zookeeper 基於 Gossip
CAP 1,強一致性, 0 資料丟失。 2,可用性
低。 3,擴容方便。
1,弱一致性,資料可能丟失。 2,可用性高。 3,擴容方
便。13/04/2018 Page 190 of 283
15. MongoDB
15.1.1. 概念
MongoDB 是由 C++語言編寫的,是一個基於分散式檔案儲存的開源資料庫系統。 在高負載的情
況下,新增更多的節點,可以保證伺服器效能。 MongoDB 旨在為 WEB 應用提供可擴充套件的高效能
資料儲存解決方案。
MongoDB 將資料儲存為一個文件,資料結構由鍵值(key=>value)對組成。 MongoDB 文件類似
於 JSON 物件。欄位值可以包含其他文件,陣列及文件陣列。
15.1.2. 特點
MongoDB 是一個面向文件儲存的資料庫,操作起來比較簡單和容易。
你可以在 MongoDB 記錄中設定任何屬性的索引 (如: FirstName="Sameer",Address="8 Ga
ndhi Road")來實現更快的排序。
你可以通過本地或者網路建立資料映象,這使得 MongoDB 有更強的擴充套件性。
如果負載的增加(需要更多的儲存空間和更強的處理能力) ,它可以分佈在計算機網路中的其
他節點上這就是所謂的分片。
Mongo 支援豐富的查詢表示式。查詢指令使用 JSON 形式的標記,可輕易查詢文件中內嵌的
物件及陣列。
MongoDb 使用 update()命令可以實現替換完成的文件(資料)或者一些指定的資料欄位 。
Mongodb 中的 Map/reduce 主要是用來對資料進行批量處理和聚合操作。
Map 和 Reduce。 Map 函式呼叫 emit(key,value)遍歷集合中所有的記錄,將 key 與 value 傳
給 Reduce 函式進行處理。
Map 函式和 Reduce 函式是使用 Javascript 編寫的,並可以通過 db.runCommand 或 mapre
duce 命令來執行 MapReduce 操作。13/04/2018 Page 191 of 283
GridFS 是 MongoDB 中的一個內建功能,可以用於存放大量小檔案。
MongoDB 允許在服務端執行指令碼, 可以用 Javascript 編寫某個函式,直接在服務端執行,也
可以把函式的定義儲存在服務端,下次直接呼叫即可。13/04/2018 Page 192 of 283
16. Cassandra
16.1.1. 概念
Apache Cassandra 是高度可擴充套件的,高效能的分散式 NoSQL 資料庫。 Cassandra 旨在處理許
多商品伺服器上的大量資料,提供高可用性而無需擔心單點故障。
Cassandra 具有能夠處理大量資料的分散式架構。 資料放置在具有多個複製因子的不同機器上,
以獲得高可用性,而無需擔心單點故障。
16.1.2. 資料模型
Key Space(對應 SQL 資料庫中的 database)
1. 一個 Key Space 中可包含若干個 CF,如同 SQL 資料庫中一個 database 可包含多個 table
Key(對應 SQL 資料庫中的主鍵)
2. 在 Cassandra 中,每一行資料記錄是以 key/value 的形式儲存的,其中 key 是唯一標識。
column(對應 SQL 資料庫中的列)
3. Cassandra 中每個 key/value 對中的 value 又稱為 column,它是一個三元組,即: name,
value 和 timestamp,其中 name 需要是唯一的。
super column(SQL 資料庫不支援)
4. cassandra 允許 key/value 中的 value 是一個 map(key/value_list),即某個 column 有多個
子列。
Standard Column Family(相對應 SQL 資料庫中的 table)
5. 每個 CF 由一系列 row 組成,每個 row 包含一個 key 以及其對應的若干 column。
Super Column Family(SQL 資料庫不支援)
6. 每個 SCF 由一系列 row 組成,每個 row 包含一個 key 以及其對應的若干 super column。
16.1.3. Cassandra 一致 Hash 和虛擬節點
一致性 Hash(多米諾 down 機)
為每個節點分配一個 token,根據這個 token 值來決定節點在叢集中的位置以及這個節點所儲存
的資料範圍。13/04/2018 Page 193 of 283
虛擬節點(down 機多節點託管)
由於這種方式會造成資料分佈不均的問題, 在 Cassandra1.2 以後採用了虛擬節點的思想:不需要
為每個節點分配 token,把圓環分成更多部分,讓每個節點負責多個部分的資料,這樣一個節點移
除後,它所負責的多個 token 會託管給多個節點處理,這種思想解決了資料分佈不均的問題。
如圖所示, 上面部分是標準一致性雜湊,每個節點負責圓環中連續的一段,如果 Node2 突然
down 掉, Node2 負責的資料託管給 Node1,即 Node1 負責 EFAB 四段,如果 Node1 裡面有
很多熱點使用者產生的資料導致 Node1 已經有點撐不住了,恰巧 B 也是熱點使用者產生的資料,這樣
一來 Node1 可能會接著 down 機, Node1down 機, Node6 還 hold 住嗎?
下面部分是虛擬節點實現, 每個節點不再負責連續部分,且圓環被分為更多的部分。 如果 Node2
突然 down 掉, Node2 負責的資料不全是託管給 Node1,而是託管給多個節點。而且也保持了一
致性雜湊的特點。
16.1.4. Gossip 協議
Gossip 演算法如其名,靈感來自辦公室八卦,只要一個人八卦一下,在有限的時間內所有的人都
會知道該八卦的資訊,這種方式也與病毒傳播類似,因此 Gossip 有眾多的別名“閒話演算法” 、
“疫情傳播演算法” 、 “病毒感染演算法” 、 “謠言傳播演算法” 。 Gossip 的特點:在一個有界網路中,
每個節點都隨機地與其他節點通訊,經過一番雜亂無章的通訊,最終所有節點的狀態都會達成一
致。因為 Gossip 不要求節點知道所有其他節點, 因此又具有去中心化的特點,節點之間完全對等,
不需要任何的中心節點。實際上 Gossip 可以用於眾多能接受“最終一致性” 的領域:失敗檢測、
路由同步、 Pub/Sub、動態負載均衡。13/04/2018 Page 194 of 283
Gossip 節點的通訊方式及收斂性
Gossip 兩個節點(A、 B)之間存在三種通訊方式(push、 pull、 push&pull)
1. push: A 節點將資料(key,value,version)及對應的版本號推送給 B 節點, B 節點更新 A 中比自
己新的資料。
2. pull: A 僅將資料 key,version 推送給 B, B 將本地比 A 新的資料(Key,value,version)推送
給 A, A 更新本地。
3. push/pull:與 pull 類似,只是多了一步, A 再將本地比 B 新的資料推送給 B, B 更新本地。
如果把兩個節點資料同步一次定義為一個週期,則在一個週期內, push 需通訊 1 次, pull 需 2 次,
push/pull 則需 3 次,從效果上來講, push/pull 最好,理論上一個週期內可以使兩個節點完全一
致。直觀上也感覺, push/pull 的收斂速度是最快的。
gossip 的協議和 seed list(防止叢集分列)
cassandra 使用稱為 gossip 的協議來發現加入 C 叢集中的其他節點的位置和狀態資訊。 gossip 進
程每秒都在進行,並與至多三個節點交換狀態資訊。節點交換他們自己和所知道的資訊,於是所
有的節點很快就能學習到整個叢集中的其他節點的資訊。 gossip 資訊有一個相關的版本號,於是
在一次 gossip 資訊交換中,舊的資訊會被新的資訊覆蓋重寫。要阻止分割槽進行 gossip 交流,那麼
在叢集中的所有節點中使用相同的 seed list, 種子節點的指定除了啟動起 gossip 程序外,沒有其
他的目的。種子節點不是一個單點故障,他們在叢集操作中也沒有其他的特殊目的,除了引導節
點以外
16.1.5. 資料複製
Partitioners(計算 primary key token 的 hash 函式)
在 Cassandra 中, table 的每行由唯一的 primarykey 標識, partitioner 實際上為一 hash 函式用
以計算 primary key 的 token。 Cassandra 依據這個 token 值在叢集中放置對應的行
兩種可用的複製策略:
SimpleStrategy: 僅用於單資料中心,
將第一個 replica 放在由 partitioner 確定的節點中,其餘的 replicas 放在上述節點順時針方向的
後續節點中。
NetworkTopologyStrategy:可用於較複雜的多資料中心。
可以指定在每個資料中心分別儲存多少份 replicas。
複製策略在建立 keyspace 時指定,如
CREATE KEYSPACE Excelsior WITH REPLICATION = { 'class' :
'SimpleStrategy','replication_factor' : 3 };
CREATE KEYSPACE Excalibur WITH REPLICATION = {'class' :'NetworkTopologyStrategy',
'dc1' : 3, 'dc2' : 2};13/04/2018 Page 195 of 283
16.1.6. 資料寫請求和協調者
協調者(coordinator)
協調者(coordinator)將 write 請求傳送到擁有對應 row 的所有 replica 節點,只要節點可用便獲取
並執行寫請求。 寫一致性級別(write consistency level)確定要有多少個 replica 節點必須返回成功
的確認資訊。 成功意味著資料被正確寫入了 commit log 和 memtable。
其中 dc1、 dc2 這些資料中心名稱要與 snitch 中配置的名稱一致.上面的拓撲策略表示在 dc1 配置
3 個副本,在 dc2 配置 2 個副本
16.1.7. 資料讀請求和後臺修復
1. 協調者首先與一致性級別確定的所有 replica 聯絡,被聯絡的節點返回請求的資料。
2. 若多個節點被聯絡, 則來自各 replica 的 row 會在記憶體中作比較,若不一致,則協調者使用含
最新資料的 replica 向 client 返回結果。 那麼比較操作過程中只需要傳遞時間戳就可以,因為要
比較的只是哪個副本資料是最新的。
3. 協調者在後臺聯系和比較來自其餘擁有對應 row 的 replica 的資料,若不一致,會向過時的
replica 發寫請求用最新的資料進行更新 read repair。13/04/2018 Page 196 of 283
16.1.8. 資料儲存(CommitLog、 MemTable、 SSTable)
寫請求分別到 CommitLog 和 MemTable, 並且 MemTable 的資料會刷寫到磁碟 SSTable 上. 除
了寫資料,還有索引也會儲存到磁碟上.
先將資料寫到磁碟中的 commitlog,同時追加到中記憶體中的資料結構 memtable 。這個時候就會
返 回 客 戶 端 狀 態 , memtable 內 容 超 出 指 定 容 量 後 會 被 放 進 將 被 刷 入 磁 盤 的 隊 列
(memtable_flush_queue_size 配置佇列長度)。若將被刷入磁碟的資料超出了佇列長度,將記憶體
資料刷進磁碟中的 SSTable,之後 commit log 被清空。
SSTable 檔案構成(BloomFilter、 index、 data、 static)
SSTable 檔案有 fileer(判斷資料 key 是否存在,這裡使用了 BloomFilter 提高效率), index(尋
找對應 column 值所在 data 檔案位置)檔案, data(儲存真實資料)檔案, static(儲存和統計
column 和 row 大小)檔案。
16.1.9. 二級索引(對要索引的 value 摘要,生成 RowKey)
在 Cassandra 中,資料都是以 Key-value 的形式儲存的。13/04/2018 Page 197 of 283
KeysIndex 所建立的二級索引也被儲存在一張 ColumnFamily 中。 在插入資料時,對需要進行索
引的 value進行摘要,生成獨一無二的key,將其作為 RowKey 儲存在索引的 ColumnFamily 中;
同時在 RowKey 上新增一個 Column,將插入資料的 RowKey 作為 name 域的值, value 域則賦
空值, timestamp 域則賦為插入資料的時間戳。
如果有相同的 value 被索引了,則會在索引 ColumnFamily 中相同的 RowKey 後再新增新的
Column。如果有新的 value 被索引,則會在索引 ColumnFamily 中新增新的 RowKey 以及對應
新的 Column。
當對 value 進行查詢時,只需計算該 value 的 RowKey,在索引 ColumnFamily 中的查詢該
RowKey,對其 Columns 進行遍歷就能得到該 value 所有資料的 RowKey。
16.1.10. 資料讀寫
資料寫入和更新(資料追加)
Cassandra 的設計思路與這些系統不同,無論是 insert 還是 remove 操作, 都是在已有的資料後
面進行追加,而不修改已有的資料。這種設計稱為 Log structured 儲存,顧名思義就是系統中的
資料是以日誌的形式存在的,所以只會將新的資料追加到已有資料的後面。 Log structured 儲存
系統有兩個主要優點:
資料的寫和刪除效率極高
傳統的儲存系統需要更新元資訊和資料, 因此磁碟的磁頭需要反覆移動,這是一個比較耗時
的操作,而 Log structured 的系統則是順序寫,可以充分利用檔案系統的 cache,所以效率
很高。
錯誤恢復簡單
由於資料本身就是以日誌形式儲存,老的資料不會被覆蓋,所以在設計 journal 的時候不需
要考慮 undo,簡化了錯誤恢復。
讀的複雜度高
但是, Log structured 的儲存系統也引入了一個重要的問題:讀的複雜度和效能。 理論上
說,讀操作需要從後往前掃描資料,以找到某個記錄的最新版本。相比傳統的儲存系統,這
是比較耗時的。
參考: https://blog.csdn.net/fs1360472174/article/details/55005335
資料刪除(column 的墓碑)
如果一次刪除操作在一個節點上失敗了(總共 3 個節點,副本為 3, RF=3).整個刪除操作仍然被
認為成功的(因為有兩個節點應答成功,使用 CL.QUORUM 一致性)。接下來如果讀發生在該節
點上就會變的不明確,因為結果返回是空,還是返回資料,沒有辦法確定哪一種是正確的。13/04/2018 Page 198 of 283
Cassandra 總是認為返回資料是對的,那就會發生刪除的資料又出現了的事情,這些資料可以叫”
殭屍”,並且他們的表現是不可預見的。
墓碑
刪除一個 column 其實只是插入一個關於這個 column 的墓碑(tombstone),並不直接刪除原
有的 column。該墓碑被作為對該 CF 的一次修改記錄在 Memtable 和 SSTable 中。墓碑的內容
是刪除請求被執行的時間,該時間是接受客戶端請求的儲存節點在執行該請求時的本地時間
(local delete time),稱為本地刪除時間。需要注意區分本地刪除時間和時間戳,每個 CF 修改
記錄都有一個時間戳,這個時間戳可以理解為該 column 的修改時間,是由客戶端給定的。
垃圾回收 compaction
由於被刪除的 column 並不會立即被從磁碟中刪除,所以系統佔用的磁碟空間會越來越大,這就
需要有一種垃圾回收的機制,定期刪除被標記了墓碑的 column。垃圾回收是在 compaction 的過
程中完成的。
資料讀取(memtable+SStables)
為了滿足讀 cassandra 讀取的資料是 memtable 中的資料和 SStables 中資料的合併結果。讀取
SSTables 中的資料就是查詢到具體的哪些的 SSTables 以及資料在這些 SSTables 中的偏移量
(SSTables 是按主鍵排序後的資料塊)。首先如果 row cache enable 了話,會檢測快取。快取命中
直接返回資料, 沒有則查詢 Bloom filter,查詢可能的 SSTable。然後有一層 Partition key cache,
找 partition key 的位置。如果有根據找到的 partition 去壓縮偏移量對映表找具體的資料塊。如果
快取沒有,則要經過 Partition summary,Partition index 去找 partition key。然後經過壓縮偏移
量對映表找具體的資料塊。
1. 檢查 memtable
2. 如果 enabled 了,檢查 row cache
3. 檢查 Bloom filter
4. 如果 enable 了,檢查 partition key 快取
5. 如果在 partition key 快取中找到了 partition key,直接去 compression offset 命中,如果沒
有,檢查 partition summary
6. 根據 compression offset map 找到資料位置
7. 從磁碟的 SSTable 中取出資料13/04/2018 Page 199 of 283
行快取和鍵快取請求流程圖
MemTable: 如果 memtable 有目標分割槽資料,這個資料會被讀出來並且和從 SSTables 中讀出
來的資料進行合併。 SSTable 的資料訪問如下面所示的步驟。
Row Cache(SSTables 中頻繁被訪問的資料)
在 Cassandra2.2+,它們被儲存在堆外記憶體,使用全新的實現避免造成垃圾回收對 JVM 造成壓力。
存在在 row cache 的子集資料可以在特定的一段時間內配置一定大小的記憶體。 row cache 使用
LRU(least-recently-userd)進行回收在申請記憶體。 儲存在 row cache 中的資料是 SSTables 中頻繁
被訪問的資料。 儲存到row cache中後,資料就可以被後續的查詢訪問。 row cache不是寫更新。
如果寫某行了,這行的快取就會失效,並且不會被繼續快取,直到這行被讀到。 類似的,如果一
個partition更新了,整個partition的cache都會被移除,但目標的資料在row cache中找不到,
就會去檢查 Bloom filter。13/04/2018 Page 200 of 283
Bloom Filter( 查詢資料可能對應的 SSTable)
首先, Cassandra 檢查 Bloom filter 去發現哪個 SSTables 中有可能有請求的分割槽資料。 Bloom
filter 是儲存在堆外記憶體。 每個 SSTable 都有一個關聯的 Bloom filter。一個 Bloom filter 可以建
立一個 SSTable 沒有包含的特定的分割槽資料。同樣也可以找到分割槽資料存在 SSTable 中的可能性。
它可以加速查詢 partition key 的查詢過程。然而,因為 Bloom filter 是一個概率函式,所以可能
會得到錯誤的結果,並不是所有的 SSTables 都可以被 Bloom filter 識別出是否有資料。 如果
Bloom filter 不能夠查詢到 SSTable, Cassandra 會檢查 partition key cache。 Bloom filter 大小
增長很適宜,每 10 億資料 1~2GB。在極端情況下,可以一個分割槽一行。都可以很輕鬆的將數十
億的 entries 儲存在單個機器上。 Bloom filter 是可以調節的,如果你願意用記憶體來換取效能。
Partition Key Cache( 查詢資料可能對應的 Partition key)
partition key 快取如果開啟了,將 partition index 儲存在堆外記憶體。 key cache 使用一小塊可配
置大小的記憶體。在讀的過程中, 每個”hit” 儲存一個檢索。如果在 key cache 中找到了 partition
key。就直接到 compression offset map 中招對應的塊。 partition key cache 熱啟動後工作的更
好,相比較冷啟動,有很大的效能提升。如果一個節點上的記憶體非常受限制,可能的話,需要限
制儲存在 key cache 中的 partition key 數目。 如果一個在 key cache 中沒有找到 partition key。
就會去partition summary中去找。 partition key cache 大小是可以配置的,意義就是儲存在key
cache 中的 partition keys 數目。
Partition Summary( 記憶體中儲存一些 partition index 的樣本)
partition summary 是儲存在堆外記憶體的結構, 儲存一些 partition index 的樣本。如果一個
partition index 包含所有的 partition keys。鑑於一個 partition summary 從每 X 個 keys 中取
樣,然後將每 X 個 key map 到 index 檔案中。例如,如果一個 partition summary 設定了 20keys
進行取樣。它就會儲存 SSTable file 開始的一個 key,20th 個 key,以此類推。儘管並不知道
partition key 的具體位置, partition summary 可以縮短找到 partition 資料位置。當找到了
partition key 值可能的範圍後,就會去找 partition index。通過配置取樣頻率,你可以用記憶體來
換取效能,當 partition summary 包含的資料越多,使用的記憶體越多。可以通過表定義的 index
interval 屬性來改變樣本頻率。固定大小的記憶體可以通過 index_summary_capacity_in_mb 屬性
來設定,預設是堆大小的 5%。
Partition Index(磁碟中)
partition index 駐紮在磁碟中,索引所有 partition keys 和偏移量的對映。如果 partition
summary 已經查到 partition keys 的範圍,現在的檢索就是根據這個範圍值來檢索目標 partition
key。需要進行單次檢索和順序讀。根據找到的資訊。然後去 compression offset map 中去找磁
盤中有這個資料的塊。如果 partition index 必須要被檢索,則需要檢索兩次磁碟去找到目標資料。
Compression offset map(磁碟中)
compression offset map 儲存磁碟資料準確位置的指標。儲存在堆外記憶體,可以被 partition key
cache 或者 partition index 訪問。一旦 compression offset map 識別出來磁碟中的資料位置,
就會從正確的 SStable(s)中取出資料。查詢就會收到結果集。13/04/2018 Page 201 of 283
17. 設計模式
17.1.1. 設計原則
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. 備忘錄模式13/04/2018 Page 202 of 283
17.1.21. 狀態模式
17.1.22. 訪問者模式
17.1.23. 中介者模式
17.1.24. 直譯器模式13/04/2018 Page 203 of 283
18. 負載均衡
負載均衡 建立在現有網路結構之上,它提供了一種廉價有效透明的方法擴充套件網路裝置和伺服器的帶
寬、增加吞吐量、加強網路資料處理能力、提高網路的靈活性和可用性。
18.1.1. 四層負載均衡 vs 七層負載均衡
18.1.1.1. 四層負載均衡(目標地址和埠交換)
主要通過報文中的目標地址和埠,再加上負載均衡裝置設定的伺服器選擇方式,決定最終選擇
的內部伺服器。
以常見的 TCP 為例, 負載均衡裝置在接收到第一個來自客戶端的 SYN 請求時,即通過上述方式選
擇一個最佳的伺服器,並對報文中目標 IP 地址進行修改(改為後端伺服器 IP),直接轉發給該服務
器。 TCP 的連線建立, 即三次握手是客戶端和伺服器直接建立的,負載均衡裝置只是起到一個類
似路由器的轉發動作。在某些部署情況下,為保證伺服器回包可以正確返回給負載均衡裝置,在
轉發報文的同時可能還會對報文原來的源地址進行修改。 實現四層負載均衡的軟體有:
F5:硬體負載均衡器,功能很好,但是成本很高。
lvs:重量級的四層負載軟體。
nginx:輕量級的四層負載軟體,帶快取功能,正則表示式較靈活。
haproxy:模擬四層轉發,較靈活。
18.1.1.2. 七層負載均衡(內容交換)
所謂七層負載均衡,也稱為“內容交換”,也就是主要通過報文中的真正有意義的應用層內容,
再加上負載均衡裝置設定的伺服器選擇方式,決定最終選擇的內部伺服器。13/04/2018 Page 204 of 283
七層應用負載的好處,是使得整個網路更智慧化。例如訪問一個網站的使用者流量,可以通過七層
的方式,將對圖片類的請求轉發到特定的圖片伺服器並可以使用快取技術;將對文字類的請求可
以轉發到特定的文字伺服器並可以使用壓縮技術。 實現七層負載均衡的軟體有:
haproxy:天生負載均衡技能,全面支援七層代理,會話保持,標記,路徑轉移;
nginx:只在 http 協議和 mail 協議上功能比較好,效能與 haproxy 差不多;
apache:功能較差
Mysql proxy:功能尚可。
18.1.2. 負載均衡演算法/策略
18.1.2.1. 輪循均衡(Round Robin)
每一次來自網路的請求輪流分配給內部中的伺服器,從 1 至 N 然後重新開始。此種均衡演算法適合
於伺服器組中的所有伺服器都有相同的軟硬體配置並且平均服務請求相對均衡的情況。
18.1.2.2. 權重輪循均衡(Weighted Round Robin)
根據伺服器的不同處理能力,給每個伺服器分配不同的權值,使其能夠接受相應權值數的服務請
求。例如:伺服器 A 的權值被設計成 1, B 的權值是 3, C 的權值是 6,則伺服器 A、 B、 C 將分
別接受到 10%、 30%、 60%的服務請求。此種均衡演算法能確保高效能的伺服器得到更多的使用
率,避免低效能的伺服器負載過重。
18.1.2.3. 隨機均衡(Random)
把來自網路的請求隨機分配給內部中的多個伺服器。
18.1.2.4. 權重隨機均衡(Weighted Random)
此種均衡演算法類似於權重輪循演算法,不過在處理請求分擔時是個隨機選擇的過程。
18.1.2.5. 響應速度均衡(Response Time 探測時間)
負載均衡裝置對內部各伺服器發出一個探測請求(例如 Ping),然後根據內部中各伺服器對探測
請求的最快響應時間來決定哪一臺伺服器來響應客戶端的服務請求。此種均衡演算法能較好的反映
伺服器的當前執行狀態,但這最快響應時間僅僅指的是負載均衡裝置與伺服器間的最快響應時
間,而不是客戶端與伺服器間的最快響應時間。13/04/2018 Page 205 of 283
18.1.2.6. 最少連線數均衡(Least Connection)
最少連線數均衡演算法對內部中需負載的每一臺伺服器都有一個數據記錄,記錄當前該伺服器正在
處理的連線數量,當有新的服務連線請求時,將把當前請求分配給連線數最少的伺服器,使均衡
更加符合實際情況,負載更加均衡。此種均衡演算法適合長時處理的請求服務,如 FTP。
18.1.2.7. 處理能力均衡(CPU、記憶體)
此種均衡演算法將把服務請求分配給內部中處理負荷(根據伺服器 CPU 型號、 CPU 數量、記憶體大小
及當前連線數等換算而成)最輕的伺服器,由於考慮到了內部伺服器的處理能力及當前網路執行
狀況,所以此種均衡演算法相對來說更加精確,尤其適合運用到第七層(應用層)負載均衡的情況
下。
18.1.2.8. DNS 響應均衡(Flash DNS)
在此均衡演算法下,分處在不同地理位置的負載均衡裝置收到同一個客戶端的域名解析請求,並在
同一時間內把此域名解析成各自相對應伺服器的 IP 地址並返回給客戶端, 則客戶端將以最先收到
的域名解析 IP 地址來繼續請求服務,而忽略其它的 IP 地址響應。在種均衡策略適合應用在全域性負
載均衡的情況下,對本地負載均衡是沒有意義的。
18.1.2.9. 雜湊演算法
一致性雜湊一致性 Hash,相同引數的請求總是發到同一提供者。當某一臺提供者掛時,原本發往
該提供者的請求,基於虛擬節點,平攤到其它提供者,不會引起劇烈變動。
18.1.2.10. IP 地址雜湊(保證客戶端伺服器對應關係穩定)
通過管理髮送方 IP 和目的地 IP 地址的雜湊,將來自同一傳送方的分組(或傳送至同一目的地的分
組)統一轉發到相同伺服器的演算法。當客戶端有一系列業務需要處理而必須和一個伺服器反覆通訊
時,該演算法能夠以流(會話)為單位, 保證來自相同客戶端的通訊能夠一直在同一伺服器中進行處
理。
18.1.2.11.URL 雜湊
通過管理客戶端請求 URL 資訊的雜湊,將傳送至相同 URL 的請求轉發至同一伺服器的演算法。13/04/2018 Page 206 of 283
18.1.3. LVS
18.1.3.1. LVS 原理
IPVS
LVS 的 IP 負載均衡技術是通過 IPVS 模組來實現的, IPVS 是 LVS 集群系統的核心軟體,它的主要
作用是:安裝在 Director Server 上,同時在 Director Server 上虛擬出一個 IP 地址,使用者必須通
過這個虛擬的 IP 地址訪問伺服器。這個虛擬 IP 一般稱為 LVS 的 VIP,即 Virtual IP。訪問的請求
首先經過 VIP 到達負載排程器,然後由負載排程器從 Real Server 列表中選取一個服務節點響應用
戶的請求。 在使用者的請求到達負載排程器後,排程器如何將請求傳送到提供服務的 Real Server 節
點,而 Real Server 節點如何返回資料給使用者,是 IPVS 實現的重點技術。
ipvs : 工作於核心空間,主要用於使使用者定義的策略生效
ipvsadm : 工作於使用者空間,主要用於使用者定義和管理叢集服務的工具
ipvs 工作於核心空間的 INPUT 鏈上,當收到使用者請求某叢集服務時,經過 PREROUTING 鏈,經
檢查本機路由表,送往 INPUT 鏈;在進入 netfilter 的 INPUT 鏈時, ipvs 強行將請求報文通過
ipvsadm 定義的叢集服務策略的路徑改為 FORWORD 鏈,將報文轉發至後端真實提供服務的主機。13/04/2018 Page 207 of 283
18.1.3.1. LVS NAT 模式
①.客戶端將請求發往前端的負載均衡器,請求報文源地址是 CIP(客戶端 IP),後面統稱為 CIP),目
標地址為 VIP(負載均衡器前端地址,後面統稱為 VIP)。
②.負載均衡器收到報文後,發現請求的是在規則裡面存在的地址,那麼它將客戶端請求報文的目
標地址改為了後端伺服器的 RIP 地址並將報文根據演算法傳送出去。
③.報文送到 Real Server 後,由於報文的目標地址是自己,所以會響應該請求, 並將響應報文返還
給 LVS。
④.然後 lvs 將此報文的源地址修改為本機併發送給客戶端。
注意:在 NAT 模式中, Real Server 的閘道器必須指向 LVS,否則報文無法送達客戶端
特點:
1、 NAT 技術將請求的報文和響應的報文都需要通過 LB 進行地址改寫,因此網站訪問量比較大的
時候 LB 負載均衡排程器有比較大的瓶頸,一般要求最多之能 10-20 臺節點
2、 只需要在 LB 上配置一個公網 IP 地址就可以了。
3、每臺內部的 realserver 伺服器的閘道器地址必須是排程器 LB 的內網地址。
4、 NAT 模式支援對 IP 地址和埠進行轉換。 即使用者請求的埠和真實伺服器的埠可以不一致。
優點:
叢集中的物理伺服器可以使用任何支援 TCP/IP 作業系統,只有負載均衡器需要一個合法的 IP 地
址。
缺點:13/04/2018 Page 208 of 283
擴充套件性有限。當伺服器節點(普通 PC 伺服器)增長過多時,負載均衡器將成為整個系統的瓶頸,因
為所有的請求包和應答包的流向都經過負載均衡器。當伺服器節點過多時,大量的資料包都交匯
在負載均衡器那,速度就會變慢!
18.1.3.2. LVS DR 模式(區域網改寫 mac 地址)
①.客戶端將請求發往前端的負載均衡器,請求報文源地址是 CIP,目標地址為 VIP。
②.負載均衡器收到報文後,發現請求的是在規則裡面存在的地址,那麼它將客戶端請求報文的源
MAC 地址改為自己 DIP 的 MAC 地址, 目標 MAC 改為了 RIP 的 MAC 地址,並將此包傳送給 RS。
③.RS 發現請求報文中的目的 MAC 是自己,就會將次報文接收下來, 處理完請求報文後,將響應
報文通過 lo 介面送給 eth0 網絡卡直接傳送給客戶端。
注意: 需要設定 lo 介面的 VIP 不能響應本地網路內的 arp 請求。
總結:
1、通過在排程器 LB 上修改資料包的目的 MAC 地址實現轉發。注意源地址仍然是 CIP,目的地址
仍然是 VIP 地址。
2、 請求的報文經過排程器,而 RS 響應處理後的報文無需經過排程器 LB,因此併發訪問量大時使
用效率很高(和 NAT 模式比)
3、因為 DR 模式是通過 MAC 地址改寫機制實現轉發,因此所有 RS 節點和排程器 LB 只能在一個
局域網裡面
4、 RS 主機需要繫結 VIP 地址在 LO 介面(掩碼 32 位)上,並且需要配置 ARP 抑制。
5、 RS 節點的預設閘道器不需要配置成 LB,而是直接配置為上級路由的閘道器,能讓 RS 直接出網就
可以。13/04/2018 Page 209 of 283
6、由於 DR 模式的排程器僅做 MAC 地址的改寫,所以排程器 LB 就不能改寫目標埠,那麼 RS
伺服器就得使用和 VIP 相同的埠提供服務。
7、直接對外的業務比如 WEB 等, RS 的 IP 最好是使用公網 IP。對外的服務,比如資料庫等最好
使用內網 IP。
優點:
和 TUN(隧道模式)一樣,負載均衡器也只是分發請求,應答包通過單獨的路由方法返回給客戶
端。與 VS-TUN 相比, VS-DR 這種實現方式不需要隧道結構,因此可以使用大多數作業系統做為
物理伺服器。
DR 模式的效率很高,但是配置稍微複雜一點,因此對於訪問量不是特別大的公司可以用
haproxy/nginx 取代。日1000-2000W PV 或者併發請求1 萬一下都可以考慮用haproxy/nginx。
缺點:
所有 RS 節點和排程器 LB 只能在一個局域網裡面
18.1.3.3. LVS TUN 模式(IP 封裝、 跨網段)
①.客戶端將請求發往前端的負載均衡器,請求報文源地址是 CIP,目標地址為 VIP。
②.負載均衡器收到報文後,發現請求的是在規則裡面存在的地址,那麼它將在客戶端請求報文的
首部再封裝一層 IP 報文,將源地址改為 DIP,目標地址改為 RIP,並將此包傳送給 RS。
③.RS 收到請求報文後,會首先拆開第一層封裝,然後發現裡面還有一層 IP 首部的目標地址是自己
lo 介面上的 VIP,所以會處理次請求報文,並將響應報文通過 lo 介面送給 eth0 網絡卡直接傳送給客
戶端。
注意:需要設定 lo 介面的 VIP 不能在共網上出現。13/04/2018 Page 210 of 283
總結:
1.TUNNEL 模式必須在所有的 realserver 機器上面繫結 VIP 的 IP 地址
2.TUNNEL 模式的 vip ------>realserver 的包通訊通過 TUNNEL 模式, 不管是內網和外網都能通
信, 所以不需要 lvs vip 跟 realserver 在同一個網段內。
3.TUNNEL 模式 realserver 會把 packet 直接發給 client 不會給 lvs 了
4.TUNNEL 模式走的隧道模式,所以運維起來比較難,所以一般不用。
優點:
負載均衡器只負責將請求包分發給後端節點伺服器,而 RS 將應答包直接發給使用者。所以,減少了
負載均衡器的大量資料流動,負載均衡器不再是系統的瓶頸,就能處理很巨大的請求量,這種方
式,一臺負載均衡器能夠為很多 RS 進行分發。而且跑在公網上就能進行不同地域的分發。
缺點:
隧道模式的 RS 節點需要合法 IP,這種方式需要所有的伺服器支援 ” IP Tunneling” (IP
Encapsulation)協議,伺服器可能只侷限在部分 Linux 系統上。
18.1.3.4. LVS FULLNAT 模式
無論是 DR 還是 NAT 模式,不可避免的都有一個問題: LVS 和 RS 必須在同一個 VLAN 下,否則
LVS 無法作為 RS 的閘道器。這引發的兩個問題是:
1、同一個 VLAN 的限制導致運維不方便,跨 VLAN 的 RS 無法接入。
2、 LVS 的水平擴充套件受到制約。當 RS 水平擴容時,總有一天其上的單點 LVS 會成為瓶頸。
Full-NAT 由此而生,解決的是 LVS 和 RS 跨 VLAN 的問題,而跨 VLAN 問題解決後, LVS 和 RS
不再存在 VLAN 上的從屬關係,可以做到多個 LVS 對應多個 RS,解決水平擴容的問題。
Full-NAT 相比 NAT 的主要改進是,在 SNAT/DNAT 的基礎上,加上另一種轉換,轉換過程如下:13/04/2018 Page 211 of 283
1. 在包從 LVS 轉到 RS 的過程中, 源地址從客戶端 IP 被替換成了 LVS 的內網 IP。 內網 IP 之間
可以通過多個交換機跨 VLAN 通訊。 目標地址從 VIP 修改為 RS IP.
2. 當 RS 處理完接受到的包, 處理完成後返回時, 將目標地址修改為 LVS ip,原地址修改為 RS
IP, 最終將這個包返回給 LVS 的內網 IP,這一步也不受限於 VLAN。
3. LVS 收到包後, 在 NAT 模式修改源地址的基礎上,再把 RS 發來的包中的目標地址從 LVS 內
網 IP 改為客戶端的 IP,並將原地址修改為 VIP。
Full-NAT 主要的思想是把閘道器和其下機器的通訊,改為了普通的網路通訊,從而解決了跨 VLAN
的問題。採用這種方式, LVS 和 RS 的部署在 VLAN 上將不再有任何限制,大大提高了運維部署的
便利性。
總結
1. FULL NAT 模式不需要 LBIP 和 realserver ip 在同一個網段;
2. full nat 因為要更新 sorce ip 所以效能正常比 nat 模式下降 10%
18.1.4. Keepalive
keepalive 起初是為 LVS 設計的,專門用來監控 lvs 各個服務節點的狀態,後來加入了 vrrp 的功
能,因此除了 lvs,也可以作為其他服務(nginx, haproxy)的高可用軟體。 VRRP 是 virtual
router redundancy protocal(虛擬路由器冗餘協議)的縮寫。 VRRP 的出現就是為了解決靜態路
由出現的單點故障,它能夠保證網路可以不間斷的穩定的執行。所以 keepalive 一方面具有 LVS
cluster node healthcheck 功能,另一方面也具有 LVS director failover。
18.1.5. Nginx 反向代理負載均衡
普通的負載均衡軟體,如 LVS, 其實現的功能只是對請求資料包的轉發、傳遞,從負載均衡下的節
點伺服器來看,接收到的請求還是來自訪問負載均衡器的客戶端的真實使用者;而反向代理就不一13/04/2018 Page 212 of 283
樣了, 反向代理伺服器在接收訪問使用者請求後,會代理使用者 重新發起請求代理下的節點伺服器,
最後把資料返回給客戶端使用者。在節點伺服器看來,訪問的節點伺服器的客戶端使用者就是反向代
理伺服器,而非真實的網站訪問使用者。
18.1.5.1. upstream_module 和健康檢測
ngx_http_upstream_module 是負載均衡模組,可以實現網站的負載均衡功能即節點的健康檢
查, upstream 模組允許 Nginx 定義一組或多組節點伺服器組,使用時可通過 proxy_pass 代理方
式把網站的請求傳送到事先定義好的對應 Upstream 組 的名字上。
upstream 模組
內參數
引數說明
weight 伺服器權重
max_fails Nginx 嘗試連線後端主機失敗的此時,這是值是配合 proxy_next_upstream、
fastcgi_next_upstream 和 memcached_next_upstream 這三個引數來使用的。當 Nginx
接收後端伺服器返回這三個引數定義的狀態碼時,會將這個請求轉發給正常工作的的後端服
務器。如 404、 503、 503,max_files=1
fail_timeout max_fails 和 fail_timeout 一般會關聯使用,如果某臺 server 在 fail_timeout 時間內出現了
max_fails 次連線失敗,那麼 Nginx 會認為其已經掛掉,從而在 fail_timeout 時間內不再去
請求它, fail_timeout 預設是 10s, max_fails 預設是 1,即預設情況只要是發生錯誤就認為
伺服器掛了,如果將 max_fails 設定為 0,則表示取消這項檢查
backup 表示當前 server 是備用伺服器,只有其它非 backup 後端伺服器都掛掉了或很忙才會分配請
求給它
down 標誌伺服器永遠不可用,可配合 ip_hash 使用
upstream lvsServer{
server 191.168.1.11 weight=5 ;
server 191.168.1.22:82;
server example.com:8080 max_fails=2 fail_timeout=10s backup;
#域名的話需要解析的哦,內網記得 hosts
}
18.1.5.1. proxy_pass 請求轉發
proxy_pass 指令屬於 ngx_http_proxy_module 模組,此模組可以將請求轉發到另一臺伺服器,
在實際的反向代理工作中, 會通過 location 功能匹配指定的 URI,然後把接收到服務匹配 URI 的
請求通過 proyx_pass 拋給定義好的 upstream 節點池。
location /download/ {
proxy_pass http://download/vedio/;
} #
這是前端代理節點的設定13/04/2018 Page 213 of 283
#交給後端 upstream 為 download 的節點
proxy 模組引數 說明
proxy_next_upstream 什麼情況下將請求傳遞到下一個 upstream
proxy_limite_rate 限制從後端伺服器讀取響應的速率
proyx_set_header 設定 http 請求 header 傳給後端伺服器節點,如:可實現讓代
理後端的伺服器節點獲取訪問客戶端的這是 ip
client_body_buffer_size 客戶端請求主體緩衝區大小
proxy_connect_timeout 代理與後端節點伺服器連線的超時時間
proxy_send_timeout 後端節點資料回傳的超時時間
proxy_read_timeout 設定 Nginx 從代理的後端伺服器獲取資訊的時間,表示連線成
功建立後, Nginx 等待後端伺服器的響應時間
proxy_buffer_size 設定緩衝區大小
proxy_buffers 設定緩衝區的數量和大小
proyx_busy_buffers_size 用於設定系統很忙時可以使用的 proxy_buffers 大小,推薦為
proxy_buffers*2
proxy_temp_file_write_size 指定 proxy 快取臨時檔案的大小
18.1.6. HAProxy13/04/2018 Page 214 of 283
19. 資料庫
19.1.1. 儲存引擎
19.1.1.1. 概念
資料庫儲存引擎是資料庫底層軟體組織,資料庫管理系統(DBMS)使用資料引擎進行建立、查詢、
更新和刪除資料。不同的儲存引擎提供不同的儲存機制、索引技巧、鎖定水平等功能,使用不同
的儲存引擎,還可以 獲得特定的功能。現在許多不同的資料庫管理系統都支援多種不同的資料引
擎。儲存引擎主要有: 1. MyIsam , 2. InnoDB, 3. Memory, 4. Archive, 5. Federated 。
19.1.1.2. InnoDB(B+樹)
InnoDB 底層儲存結構為B+樹, B樹的每個節點對應innodb的一個page, page大小是固定的,
一般設為 16k。其中非葉子節點只有鍵值,葉子節點包含完成資料。
適用場景:
1)經常更新的表,適合處理多重併發的更新請求。
2)支援事務。
3)可以從災難中恢復(通過 bin-log 日誌等)。
4)外來鍵約束。只有他支援外來鍵。
5)支援自動增加列屬性 auto_increment。13/04/2018 Page 215 of 283
19.1.1.3. TokuDB( Fractal Tree-節點帶資料)
TokuDB 底層儲存結構為 Fractal Tree,Fractal Tree 的結構與 B+樹有些類似, 在 Fractal Tree
中, 每一個 child 指標除了需要指向一個 child 節點外,還會帶有一個 Message Buffer ,這個
Message Buffer 是一個 FIFO 的佇列,用來快取更新操作。
例如,一次插入操作只需要落在某節點的 Message Buffer 就可以馬上返回了,並不需要搜尋到葉
子節點。這些快取的更新會在查詢時或後臺非同步合併應用到對應的節點中。
TokuDB 線上新增索引,不影響讀寫操作, 非常快的寫入效能, Fractal-tree 在事務實現上有優
勢。 他主要適用於訪問頻率不高的資料或歷史資料歸檔。
19.1.1.4. MyIASM
MyIASM是 MySQL預設的引擎,但是它沒有提供對資料庫事務的支援,也不支援行級鎖和外來鍵,
因此當 INSERT(插入)或 UPDATE(更新)資料時即寫操作需要鎖定整個表,效率便會低一些。
ISAM 執行讀取操作的速度很快,而且不佔用大量的記憶體和儲存資源。在設計之初就預想資料組織
成有固定長度的記錄,按順序儲存的。 ---ISAM 是一種靜態索引結構。
缺點是它不 支援事務處理。
19.1.1.5. Memory
Memory(也叫 HEAP)堆記憶體:使用存在記憶體中的內容來建立表。每個 MEMORY 表只實際對應
一個磁碟檔案。 MEMORY 型別的表訪問非常得快,因為它的資料是放在記憶體中的,並且預設使用
HASH 索引。但是一旦服務關閉,表中的資料就會丟失掉。 Memory 同時支援雜湊索引和 B 樹索
引, B樹索引可以使用部分查詢和通配查詢,也可以使用<,>和>=等操作符方便資料探勘,雜湊索
引相等的比較快但是對於範圍的比較慢很多。
19.1.2. 索引
索引(Index)是幫助 MySQL 高效獲取資料的資料結構。 常見的查詢演算法,順序查詢,二分查詢,二
叉排序樹查詢,雜湊雜湊法,分塊查詢,平衡多路搜尋樹 B 樹(B-tree)13/04/2018 Page 216 of 283
19.1.2.1. 常見索引原則有
1.選擇唯一性索引
1. 唯一性索引的值是唯一的,可以更快速的通過該索引來確定某條記錄。
2.為經常需要排序、分組和聯合操作的欄位建立索引:
3.為常作為查詢條件的欄位建立索引。
4.限制索引的數目:
越多的索引,會使更新表變得很浪費時間。
儘量使用資料量少的索引
6. 如果索引的值很長,那麼查詢的速度會受到影響。
儘量使用字首來索引
7. 如果索引欄位的值很長,最好使用值的字首來索引。
7.刪除不再使用或者很少使用的索引
8 . 最左字首匹配原則,非常重要的原則。
10 . 儘量選擇區分度高的列作為索引
區分度的公式是表示欄位不重複的比例
11 .索引列不能參與計算,保持列“乾淨”:帶函式的查詢不參與索引。
12 .儘量的擴充套件索引,不要新建索引。
19.1.3. 資料庫三正規化
正規化是具有最小冗餘的表結構。 3 正規化具體如下:
19.1.3.1. 第一正規化(1st NF - 列都是不可再分)
第一正規化的目標是確保每列的原子性:如果每列都是不可再分的最小資料單元(也稱為最小的原子
單元),則滿足第一正規化(1NF)
19.1.3.2. 第二正規化(2nd NF- 每個表只描述一件事情)13/04/2018 Page 217 of 283
首先滿足第一正規化,並且表中非主鍵列不存在對主鍵的部分依賴。 第二正規化要求每個表只描述一
件事情。
19.1.3.3. 第三正規化(3rd NF- 不存在對非主鍵列的傳遞依賴)
第三正規化定義是,滿足第二正規化,並且表中的列不存在對非主鍵列的傳遞依賴。 除了主鍵訂單編
號外,顧客姓名依賴於非主鍵顧客編號。
19.1.4. 資料庫是事務
事務(TRANSACTION)是作為單個邏輯工作單元執行的一系列操作, 這些操作作為一個整體一起向
系統提交,要麼都執行、要麼都不執行 。 事務是一個不可分割的工作邏輯單元
事務必須具備以下四個屬性,簡稱 ACID 屬性:
原子性(Atomicity)
1. 事務是一個完整的操作。事務的各步操作是不可分的(原子的);要麼都執行,要麼都不執
行。
一致性(Consistency)
2. 當事務完成時,資料必須處於一致狀態。13/04/2018 Page 218 of 283
隔離性(Isolation)
3. 對資料進行修改的所有併發事務是彼此隔離的, 這表明事務必須是獨立的,它不應以任何方
式依賴於或影響其他事務。
永久性(Durability)
4. 事務完成後,它對資料庫的修改被永久保持,事務日誌能夠保持事務的永久性。
19.1.5. 儲存過程(特定功能的 SQL 語句集)
一組為了完成特定功能的 SQL 語句集,儲存在資料庫中,經過第一次編譯後再次呼叫不需要再次
編譯,使用者通過指定儲存過程的名字並給出引數(如果該儲存過程帶有引數)來執行它。儲存過
程是資料庫中的一個重要物件。
儲存過程優化思路:
1. 儘量利用一些 sql 語句來替代一些小迴圈,例如聚合函式,求平均函式等。
2. 中間結果存放於臨時表,加索引。
3. 少使用遊標。 sql 是個集合語言,對於集合運算具有較高效能。而 cursors 是過程運算。比如
對一個 100 萬行的資料進行查詢。遊標需要讀表 100 萬次,而不使用遊標則只需要少量幾次
讀取。
4. 事務越短越好。 sqlserver 支援併發操作。如果事務過多過長,或者隔離級別過高,都會造成
併發操作的阻塞,死鎖。導致查詢極慢, cpu 佔用率極地。
5. 使用 try-catch 處理錯誤異常。
6. 查詢語句儘量不要放在迴圈內。
19.1.6. 觸發器(一段能自動執行的程式)
觸發器是一段能自動執行的程式,是一種特殊的儲存過程, 觸發器和普通的儲存過程的區別是:
觸發器是當對某一個表進行操作時觸發。 諸如: update、 insert、 delete 這些操作的時候,系統
會自動呼叫執行該表上對應的觸發器。 SQL Server 2005 中觸發器可以分為兩類: DML 觸發器和
DDL 觸發器,其中 DDL 觸發器它們會影響多種資料定義語言語句而激發,這些語句有 create、
alter、 drop 語句。
19.1.7. 資料庫併發策略
併發控制一般採用三種方法,分別是樂觀鎖和悲觀鎖以及時間戳。
19.1.7.1. 樂觀鎖
樂觀鎖認為一個使用者讀資料的時候,別人不會去寫自己所讀的資料;悲觀鎖就剛好相反,覺得自
己讀資料庫的時候,別人可能剛好在寫自己剛讀的資料,其實就是持一種比較保守的態度;時間
戳就是不加鎖,通過時間戳來控制併發出現的問題。13/04/2018 Page 219 of 283
19.1.7.2. 悲觀鎖
悲觀鎖就是在讀取資料的時候,為了不讓別人修改自己讀取的資料,就會先對自己讀取的資料加
鎖,只有自己把資料讀完了,才允許別人修改那部分資料,或者反過來說,就是自己修改某條數
據的時候,不允許別人讀取該資料,只有等自己的整個事務提交了,才釋放自己加上的鎖,才允
許其他使用者訪問那部分資料。
19.1.7.3. 時間戳
時間戳就是在資料庫表中單獨加一列時間戳,比如“TimeStamp”, 每次讀出來的時候,把該字
段也讀出來,當寫回去的時候,把該欄位加1,提交之前 ,跟資料庫的該欄位比較一次,如果比數
據庫的值大的話,就允許儲存,否則不允許儲存,這種處理方法雖然不使用資料庫系統提供的鎖
機制,但是這種方法可以大大提高資料庫處理的併發量,
以上悲觀鎖所說的加“鎖”,其實分為幾種鎖,分別是: 排它鎖(寫鎖) 和共享鎖(讀鎖) 。
19.1.8. 資料庫鎖
19.1.8.1. 行級鎖
行級鎖是一種排他鎖,防止其他事務修改此行;在使用以下語句時, Oracle 會自動應用行級鎖:
1. INSERT、 UPDATE、 DELETE、 SELECT … FOR UPDATE [OF columns] [WAIT n | NOWAIT];
2. SELECT … FOR UPDATE 語句允許使用者一次鎖定多條記錄進行更新
3. 使用 COMMIT 或 ROLLBACK 語句釋放鎖。
19.1.8.2. 表級鎖
表示對當前操作的整張表加鎖,它實現簡單,資源消耗較少,被大部分 MySQL 引擎支援。最常使
用的 MYISAM 與 INNODB 都支援表級鎖定。表級鎖定分為表共享讀鎖(共享鎖)與表獨佔寫鎖
(排他鎖)。
19.1.8.1. 頁級鎖
頁級鎖是 MySQL 中鎖定粒度介於行級鎖和表級鎖中間的一種鎖。表級鎖速度快,但衝突多,行級
衝突少,但速度慢。所以取了折衷的頁級,一次鎖定相鄰的一組記錄。 BDB 支援頁級鎖
19.1.9. 基於 Redis 分散式鎖
1. 獲取鎖的時候,使用 setnx(SETNX key val:當且僅當 key 不存在時, set 一個 key
為 val 的字串,返回 1;若 key 存在,則什麼都不做,返回 0)加鎖,鎖的 value
值為一個隨機生成的 UUID,在釋放鎖的時候進行判斷。 並使用 expire 命令為鎖添
加一個超時時間,超過該時間則自動釋放鎖。13/04/2018 Page 220 of 283
2. 獲取鎖的時候呼叫 setnx, 如果返回 0,則該鎖正在被別人使用,返回 1 則成功獲取
鎖。 還設定一個獲取的超時時間,若超過這個時間則放棄獲取鎖。
3. 釋放鎖的時候,通過 UUID 判斷是不是該鎖,若是該鎖, 則執行 delete 進行鎖釋放。
19.1.10. 分割槽分表
分庫分表有垂直切分和水平切分兩種。
垂直切分(按照功能模組)
將表按照功能模組、關係密切程度劃分出來, 部署到不同的庫上。例如,我們會建立定義數
據庫 workDB、商品資料庫 payDB、使用者資料庫 userDB、日誌資料庫 logDB 等,分別用於
儲存專案資料定義表、商品定義表、使用者資料表、日誌資料表等。
水平切分(按照規則劃分儲存)
當一個表中的資料量過大時,我們可以把該表的資料按照某種規則,例如 userID 雜湊,進行
劃分,然後儲存到多個結構相同的表,和不同的庫上。
19.1.11. 兩階段提交協議
分散式事務是指會涉及到操作多個數據庫的事務,在分散式系統中,各個節點之間在物理上相互獨
立,通過網路進行溝通和協調。13/04/2018 Page 221 of 283
XA 就是 X/Open DTP 定義的交易中介軟體與資料庫之間的介面規範(即介面函式),交易中介軟體
用它來通知資料庫事務的開始、結束以及提交、回滾等。 XA 介面函式由資料庫廠商提供。
二階段提交(Two-phaseCommit)是指,在計算機網路以及資料庫領域內, 為了使基於分散式系統
架構下的所有節點在進行事務提交時保持一致性而設計的一種演算法(Algorithm)。通常,二階段提
交也被稱為是一種協議(Protocol))。在分散式系統中,每個節點雖然可以知曉自己的操作時成功
或者失敗,卻無法知道其他節點的操作的成功或失敗。當一個事務跨越多個節點時,為了保持事
務的 ACID 特性, 需要引入一個作為協調者的元件來統一掌控所有節點(稱作參與者)的操作結果並
最終指示這些節點是否要把操作結果進行真正的提交(比如將更新後的資料寫入磁碟等等)。 因此,
二階段提交的演算法思路可以概括為:參與者將操作成敗通知協調者,再由協調者根據所有參與者
的反饋情報決定各參與者是否要提交操作還是中止操作。
19.1.11.1. 準備階段
事務協調者(事務管理器)給每個參與者(資源管理器)傳送 Prepare 訊息,每個參與者要麼直接返回
失敗(如許可權驗證失敗),要麼在本地執行事務, 寫本地的 redo 和 undo 日誌,但不提交,到達一
種“萬事俱備,只欠東風”的狀態。
19.1.11.2. 提交階段
如果協調者收到了參與者的失敗訊息或者超時,直接給每個參與者傳送回滾(Rollback)訊息;否則,
傳送提交(Commit)訊息;參與者根據協調者的指令執行提交或者回滾操作,釋放所有事務處理過
程中使用的鎖資源。 (注意:必須在最後階段釋放鎖資源)
19.1.11.3. 缺點
同步阻塞問題
1、 執行過程中,所有參與節點都是事務阻塞型的。
單點故障
2、 由於協調者的重要性,一旦協調者發生故障。參與者會一直阻塞下去。
資料不一致(腦裂問題)
3、 在二階段提交的階段二中,當協調者向參與者傳送 commit 請求之後,發生了局部網路異
常或者在傳送 commit 請求過程中協調者發生了故障,導致只有一部分參與者接受到了
commit 請求。於是整個分散式系統便出現了資料部一致性的現象(腦裂現象)。
二階段無法解決的問題(資料狀態不確定)
4、 協調者再發出 commit 訊息之後宕機, 而唯一接收到這條訊息的參與者同時也宕機了。那
麼即使協調者通過選舉協議產生了新的協調者, 這條事務的狀態也是不確定的,沒人知道
事務是否被已經提交。13/04/2018 Page 222 of 283
19.1.12. 三階段提交協議
三 階 段 提 交 ( Three-phase commit ) , 也 叫 三 階 段 提 交 協 議 ( Three-phase commit
protocol),是二階段提交(2PC)的改進版本。
與兩階段提交不同的是,三階段提交有兩個改動點。
1、引入超時機制。同時在協調者和參與者中都引入超時機制。
2、在第一階段和第二階段中插入一個準備階段。保證了在最後提交階段之前各參與節點的狀態是
一致的。也就是說,除了引入超時機制之外, 3PC 把 2PC 的準備階段再次一分為二,這樣三階段
提交就有 CanCommit、 PreCommit、 DoCommit 三個階段。
19.1.12.1. CanCommit 階段
協調者向參與者傳送 commit 請求,參與者如果可以提交就返回 Yes 響應,否則返回 No 響應。
19.1.12.2. PreCommit 階段
協調者根據參與者的反應情況來決定是否可以繼續進行,有以下兩種可能。假如協調者從所有的
參與者獲得的反饋都是 Yes 響應,那麼就會執行事務的預執行假如有任何一個參與者向協調者傳送
了 No 響應,或者等待超時之後,協調者都沒有接到參與者的響應,那麼就執行事務的中斷。
19.1.12.3. doCommit 階段
該階段進行真正的事務提交, 主要包含 1.協調這傳送提交請求 2.參與者提交事務 3.參與者響應反
饋( 事務提交完之後,向協調者傳送 Ack 響應。) 4.協調者確定完成事務。
19.1.13. 柔性事務
19.1.13.1. 柔性事務
在電商領域等網際網路場景下,傳統的事務在資料庫效能和處理能力上都暴露出了瓶頸。在分散式
領域基於 CAP 理論以及 BASE 理論,有人就提出了 柔性事務 的概念。 CAP(一致性、可用性、分
區容忍性)理論大家都理解很多次了,這裡不再敘述。說一下 BASE 理論,它是在 CAP 理論的基
礎之上的延伸。包括 基本可用(Basically Available)、柔性狀態(Soft State)、最終一致性
(Eventual Consistency)。
通常所說的柔性事務分為: 兩階段型、補償型、非同步確保型、最大努力通知型幾種。
兩階段型
1、 就是分散式事務兩階段提交,對應技術上的 XA、 JTA/JTS。這是分散式環境下事務處理的
典型模式。
補償型
2、 TCC 型事務(Try/Confirm/Cancel)可以歸為補償型。13/04/2018 Page 223 of 283
WS-BusinessActivity 提供了一種基於補償的 long-running 的事務處理模型。 伺服器 A 發起事務,
伺服器 B 參與事務, 伺服器 A 的事務如果執行順利,那麼事務 A 就先行提交,如果事務 B 也執行
順利,則事務 B 也提交,整個事務就算完成。但是如果事務 B 執行失敗,事務 B 本身回滾,這時
事務 A 已經被提交,所以需要執行一個補償操作,將已經提交的事務 A 執行的操作作反操作,恢
復到未執行前事務 A 的狀態。這樣的 SAGA 事務模型,是犧牲了一定的隔離性和一致性的,但是
提高了 long-running 事務的可用性。
非同步確保型
3、 通過將一系列同步的事務操作變為基於訊息執行的非同步操作, 避免了分散式事務中的同步
阻塞操作的影響。
最大努力通知型(多次嘗試)
4、 這是分散式事務中要求最低的一種, 也可以通過訊息中介軟體實現, 與前面非同步確保型操作不
同的一點是, 在訊息由 MQ Server 投遞到消費者之後, 允許在達到最大重試次數之後正常
結束事務。13/04/2018 Page 224 of 283
19.1.14. CAP
CAP 原則又稱 CAP 定理,指的是在一個分散式系統中, Consistency(一致性)、 Availability
(可用性)、 Partition tolerance(分割槽容錯性),三者不可得兼。
一致性(C):
1. 在分散式系統中的所有資料備份,在同一時刻是否同樣的值。(等同於所有節點訪問同一份
最新的資料副本)
可用性( A):
2. 在叢集中一部分節點故障後,叢集整體是否還能響應客戶端的讀寫請求。(對資料更新具備
高可用性)
分割槽容忍性(P) :
3. 以實際效果而言,分割槽相當於對通訊的時限要求。系統如果不能在時限內達成資料一致性,
就意味著發生了分割槽的情況,必須就當前操作在 C 和 A 之間做出選擇。13/04/2018 Page 225 of 283
20. 一致性演算法
20.1.1. Paxos
Paxos 演算法解決的問題是一個分散式系統如何就某個值(決議)達成一致。一個典型的場景是,
在一個分散式資料庫系統中,如果各節點的初始狀態一致,每個節點執行相同的操作序列,那麼
他們最後能得到一個一致的狀態。為保證每個節點執行相同的命令序列,需要在每一條指令上執
行一個“一致性演算法”以保證每個節點看到的指令一致。 zookeeper 使用的 zab 演算法是該演算法的
一個實現。 在 Paxos 演算法中,有三種角色: Proposer, Acceptor, Learners
Paxos 三種角色: Proposer, Acceptor, Learners
Proposer:
只要 Proposer 發的提案被半數以上 Acceptor 接受, Proposer 就認為該提案裡的 value 被選定
了。
Acceptor:
只要 Acceptor 接受了某個提案, Acceptor 就認為該提案裡的 value 被選定了。
Learner:
Acceptor 告訴 Learner 哪個 value 被選定, Learner 就認為那個 value 被選定。
Paxos 演算法分為兩個階段。具體如下:
階段一(準 leader 確定 ) :
(a) Proposer 選擇一個提案編號 N,然後向半數以上的 Acceptor 傳送編號為 N 的 Prepare 請求。
(b) 如果一個 Acceptor 收到一個編號為 N 的 Prepare 請求,且 N 大於該 Acceptor 已經響應過的
所有 Prepare 請求的編號,那麼它就會將它已經接受過的編號最大的提案(如果有的話)作為響
應反饋給 Proposer,同時該 Acceptor 承諾不再接受任何編號小於 N 的提案。
階段二(leader 確認) :
(a) 如果 Proposer 收到半數以上 Acceptor 對其發出的編號為 N 的 Prepare 請求的響應,那麼它
就會發送一個針對[N,V]提案的 Accept 請求給半數以上的 Acceptor。注意: V 就是收到的響應中
編號最大的提案的 value,如果響應中不包含任何提案,那麼 V 就由 Proposer 自己決定。
(b) 如果 Acceptor 收到一個針對編號為 N 的提案的 Accept 請求,只要該 Acceptor 沒有對編號
大於 N 的 Prepare 請求做出過響應,它就接受該提案。
20.1.2. Zab
ZAB( ZooKeeper Atomic Broadcast , ZooKeeper 原子訊息廣播協議)協議包括兩種基本的模
式:崩潰恢復和訊息廣播13/04/2018 Page 226 of 283
1. 當整個服務框架在啟動過程中,或是當 Leader 伺服器出現網路中斷崩潰退出與重啟等異常情
況時, ZAB 就會進入恢復模式並選舉產生新的 Leader 伺服器。
2. 當選舉產生了新的 Leader 伺服器,同時叢集中已經有過半的機器與該 Leader 伺服器完成了
狀態同步之後, ZAB 協議就會退出崩潰恢復模式,進入訊息廣播模式。
3. 當有新的伺服器加入到叢集中去,如果此時叢集中已經存在一個 Leader 伺服器在負責進行消
息廣播,那麼新加入的伺服器會自動進入資料恢復模式,找到 Leader 伺服器,並與其進行數
據同步,然後一起參與到訊息廣播流程中去。
以上其實大致經歷了三個步驟:
1.崩潰恢復:主要就是 Leader 選舉過程
2.資料同步: Leader 伺服器與其他伺服器進行資料同步
3.訊息廣播: Leader 伺服器將資料傳送給其他伺服器
說明: zookeeper 章節對該協議有詳細描述。
20.1.3. Raft
與 Paxos 不同 Raft 強調的是易懂(Understandability), Raft 和 Paxos 一樣只要保證 n/2+1 節
點正常就能夠提供服務; raft 把演算法流程分為三個子問題:選舉(Leader election)、日誌複製
(Log replication)、安全性(Safety)三個子問題。
20.1.3.1. 角色
Raft 把叢集中的節點分為三種狀態: Leader、 Follower 、 Candidate,理所當然每種狀態負
責的任務也是不一樣的, Raft 執行時提供服務的時候只存在 Leader 與 Follower 兩種狀態;
Leader(領導者-日誌管理)
負責日誌的同步管理,處理來自客戶端的請求,與 Follower 保持這 heartBeat 的聯絡;
Follower(追隨者-日誌同步)
剛啟動時所有節點為Follower狀態,響應Leader的日誌同步請求,響應Candidate的請求,
把請求到 Follower 的事務轉發給 Leader;
Candidate(候選者-負責選票)
負責選舉投票, Raft 剛啟動時由一個節點從 Follower 轉為 Candidate 發起選舉,選舉出
Leader 後從 Candidate 轉為 Leader 狀態;
20.1.3.2. Term(任期)
在 Raft 中使用了一個可以理解為週期(第幾屆、任期)的概念,用 Term 作為一個週期,每
個 Term 都是一個連續遞增的編號,每一輪選舉都是一個 Term 週期,在一個 Term 中只能產生一
個 Leader;當某節點收到的請求中 Term 比當前 Term 小時則拒絕該請求。13/04/2018 Page 227 of 283
20.1.3.3. 選舉(Election)
選舉定時器
Raft 的選舉由定時器來觸發, 每個節點的選舉定時器時間都是不一樣的,開始時狀態都為
Follower 某個節點定時器觸發選舉後 Term 遞增,狀態由 Follower 轉為 Candidate,向其他節點
發起 RequestVote RPC 請求,這時候有三種可能的情況發生:
1:該 RequestVote 請求接收到 n/2+1(過半數)個節點的投票,從 Candidate 轉為 Leader,
向其他節點發送 heartBeat 以保持 Leader 的正常運轉。
2:在此期間如果收到其他節點發送過來的 AppendEntries RPC 請求,如該節點的 Term 大
則當前節點轉為 Follower,否則保持 Candidate 拒絕該請求。
3: Election timeout 發生則 Term 遞增,重新發起選舉
在一個 Term 期間每個節點只能投票一次, 所以當有多個 Candidate 存在時就會出現每個
Candidate 發起的選舉都存在接收到的投票數都不過半的問題,這時每個 Candidate 都將 Term
遞增、重啟定時器並重新發起選舉,由於每個節點中定時器的時間都是隨機的,所以就不會多次
存在有多個 Candidate 同時發起投票的問題。
在 Raft 中當接收到客戶端的日誌(事務請求)後先把該日誌追加到本地的 Log 中,然後通過
heartbeat 把該 Entry 同步給其他 Follower, Follower 接收到日誌後記錄日誌然後向 Leader 傳送
ACK,當 Leader 收到大多數(n/2+1) Follower 的 ACK 資訊後將該日誌設定為已提交併追加到
本地磁碟中,通知客戶端並在下個 heartbeat 中 Leader 將通知所有的 Follower 將該日誌儲存在
自己的本地磁碟中。
20.1.3.4. 安全性(Safety)
安全性是用於保證每個節點都執行相同序列的安全機制如當某個 Follower 在當前 Leader commit
Log 時變得不可用了,稍後可能該 Follower 又會倍選舉為 Leader,這時新 Leader 可能會用新的
Log 覆蓋先前已 committed 的 Log,這就是導致節點執行不同序列; Safety 就是用於保證選舉出
來的 Leader 一定包含先前 commited Log 的機制;
選舉安全性(Election Safety) : 每個 Term 只能選舉出一個 Leader
Leader 完整性(Leader Completeness) : 這裡所說的完整性是指 Leader 日誌的完整性,
Raft 在選舉階段就使用 Term 的判斷用於保證完整性:當請求投票的該 Candidate 的 Term 較大
或 Term 相同 Index 更大則投票,該節點將容易變成 leader。
20.1.3.5. raft 協議和 zab 協議區別
相同點
採用 quorum 來確定整個系統的一致性,這個 quorum 一般實現是叢集中半數以上的伺服器,
zookeeper 裡還提供了帶權重的 quorum 實現.
都由 leader 來發起寫操作.
都採用心跳檢測存活性13/04/2018 Page 228 of 283
leader election 都採用先到先得的投票方式
不同點
zab 用的是 epoch 和 count 的組合來唯一表示一個值, 而 raft 用的是 term 和 index
zab 的 follower 在投票給一個 leader 之前必須和 leader 的日誌達成一致,而 raft 的 follower
則簡單地說是誰的 term 高就投票給誰
raft 協議的心跳是從 leader 到 follower, 而 zab 協議則相反
raft 協議資料只有單向地從 leader 到 follower(成為 leader 的條件之一就是擁有最新的 log),
而 zab 協議在 discovery 階段, 一個 prospective leader 需要將自己的 log 更新為 quorum 裡面
最新的 log,然後才好在 synchronization 階段將 quorum 裡的其他機器的 log 都同步到一致.
20.1.4. NWR
N:在分散式儲存系統中,有多少份備份資料
W:代表一次成功的更新操作要求至少有 w 份資料寫入成功
R: 代表一次成功的讀資料操作要求至少有 R 份資料成功讀取
NWR值的不同組合會產生不同的一致性效果,當W+R>N 的時候,整個系統對於客戶端來講能保
證強一致性。 而如果 R+W<=N,則無法保證資料的強一致性。 以常見的 N=3、 W=2、 R=2 為例:
N=3,表示,任何一個物件都必須有三個副本( Replica), W=2 表示,對資料的修改操作
(Write)只需要在 3 個 Replica 中的 2 個上面完成就返回, R=2 表示,從三個物件中要讀取到 2
個數據物件, 才能返回。
20.1.5. Gossip
Gossip 演算法又被稱為反熵(Anti-Entropy),熵是物理學上的一個概念, 代表雜亂無章,而反熵
就是在雜亂無章中尋求一致,這充分說明了 Gossip 的特點:在一個有界網路中,每個節點都隨機13/04/2018 Page 229 of 283
地與其他節點通訊,經過一番雜亂無章的通訊,最終所有節點的狀態都會達成一致。每個節點可
能知道所有其他節點,也可能僅知道幾個鄰居節點,只要這些節可以通過網路連通,最終他們的
狀態都是一致的,當然這也是疫情傳播的特點。
20.1.6. 一致性 Hash
一致性雜湊演算法(Consistent Hashing Algorithm)是一種分散式演算法,常用於負載均衡。
Memcached client 也選擇這種演算法,解決將 key-value 均勻分配到眾多 Memcached server 上
的問題。它可以取代傳統的取模操作,解決了取模操作無法應對增刪 Memcached Server 的問題
(增刪 server 會導致同一個 key,在 get 操作時分配不到資料真正儲存的 server,命中率會急劇下
降)。
20.1.6.1. 一致性 Hash 特性
平衡性(Balance): 平衡性是指雜湊的結果能夠儘可能分佈到所有的緩衝中去,這樣可以使得
所有的緩衝空間都得到利用。
單調性(Monotonicity): 單調性是指如果已經有一些內容通過雜湊分派到了相應的緩衝中,
又有新的緩衝加入到系統中。雜湊的結果應能夠保證原有已分配的內容可以被對映到新的緩
衝中去,而不會被對映到舊的緩衝集合中的其他緩衝區。容易看到,上面的簡單求餘演算法
hash(object)%N 難以滿足單調性要求。
平滑性(Smoothness): 平滑性是指快取伺服器的數目平滑改變和快取物件的平滑改變是一致
的。
20.1.6.2. 一致性 Hash 原理
1.建構環形 hash 空間:
1. 考慮通常的 hash 演算法都是將 value 對映到一個 32 為的 key 值,也即是 0~2^32-1 次方的
數值空間;我們可以將這個空間想象成一個首( 0 )尾( 2^32-1 )相接的圓環。
2.把需要快取的內容(物件)對映到 hash 空間
2. 接下來考慮 4 個物件 object1~object4 ,通過 hash 函式計算出的 hash 值 key 在環上的分
布
3.把伺服器(節點)對映到 hash 空間
3. Consistent hashing 的基本思想就是將物件和 cache 都對映到同一個 hash 數值空間中,並
且使用相同的 hash 演算法。 一般的方法可以使用 伺服器(節點) 機器的 IP 地址或者機器名作為
hash 輸入。
4.把物件對映到服務節點
4. 現在服務節點和物件都已經通過同一個 hash 演算法對映到 hash 數值空間中了, 首先確定物件
hash 值在環上的位置,從此位置沿環順時針“行走”,第一臺遇到的伺服器就是其應該定位
到的伺服器。13/04/2018 Page 230 of 283
考察 cache 的變動
5. 通過 hash 然後求餘的方法帶來的最大問題就在於不能滿足單調性,當 cache 有所變動時,
cache 會失效。
5.1 移除 cache: 考慮假設 cache B 掛掉了,根據上面講到的對映方法,這時受影響的將僅是
那些沿 cache B 逆時針遍歷直到下一個 cache ( cache C )之間的物件。
5.2 新增 cache: 再考慮新增一臺新的 cache D 的情況,這時受影響的將僅是那些沿 cache
D 逆時針遍歷直到下一個 cache 之間的物件,將這些物件重新對映到 cache D 上即可。
虛擬節點
hash 演算法並不是保證絕對的平衡,如果 cache 較少的話,物件並不能被均勻的對映到 cache 上,
為了解決這種情況, consistent hashing 引入了“虛擬節點”的概念, 它可以如下定義:
虛擬節點( virtual node )是實際節點在 hash 空間的複製品( replica ) ,一實際個節點對應了
若干個“虛擬節點”,這個對應個數也成為“複製個數”,“虛擬節點”在 hash 空間中以 hash
值排列。
仍以僅部署 cache A 和 cache C 的情況為例。現在我們引入虛擬節點,並設定“複製個數”為 2 ,
這就意味著一共會存在 4 個“虛擬節點”, cache A1, cache A2 代表了 cache A; cache C1,
cache C2 代表了 cache C 。 此時,物件到“虛擬節點”的對映關係為:
objec1->cache A2 ; objec2->cache A1 ; objec3->cache C1 ; objec4->cache C2 ;
因此物件 object1 和 object2 都被對映到了 cache A 上,而 object3 和 object4 對映到了 cache
C 上;平衡性有了很大提高。
引入“虛擬節點”後,對映關係就從 { 物件 -> 節點 } 轉換到了 { 物件 -> 虛擬節點 } 。查詢物體所
在 cache 時的對映關係如下圖 所示。13/04/2018 Page 231 of 28313/04/2018 Page 232 of 283
21. JAVA 演算法
21.1.1. 二分查詢
又叫折半查詢,要求待查詢的序列有序。每次取中間位置的值與待查關鍵字比較,如果中間位置
的值比待查關鍵字大,則在前半部分迴圈這個查詢的過程,如果中間位置的值比待查關鍵字小,
則在後半部分迴圈這個查詢的過程。直到查詢到了為止,否則序列中沒有待查的關鍵字。
public static int biSearch(int []array,int a){
int lo=0;
int hi=array.length-1;
int mid;
while(lo<=hi){
mid=(lo+hi)/2;//中間位置
if(array[mid]==a){
return mid+1;
}else if(array[mid]<a){ //向右查詢
lo=mid+1;
}else{ //向左查詢
hi=mid-1;
}
}
return -1;
}
21.1.2. 氣泡排序演算法
(1)比較前後相鄰的二個數據,如果前面資料大於後面的資料,就將這二個數據交換。
(2)這樣對陣列的第 0 個數據到 N-1 個數據進行一次遍歷後,最大的一個數據就“沉” 到陣列第
N-1 個位置。
(3) N=N-1,如果 N 不為 0 就重複前面二步,否則排序完成。
public static void bubbleSort1(int [] a, int n){
int i, j;13/04/2018 Page 233 of 283
for(i=0; i<n; i++){//表示 n 次排序過程。
for(j=1; j<n-i; j++){
if(a[j-1] > a[j]){//前面的數字大於後面的數字就交換
//交換 a[j-1]和 a[j]
int temp;
temp = a[j-1];
a[j-1] = a[j];
a[j]=temp;
}
}
}
}
21.1.3. 插入排序演算法
通過構建有序序列,對於未排序資料,在已排序序列中從後向前掃描,找到相應的位置並插入。
插入排序非常類似於整撲克牌。在開始摸牌時,左手是空的,牌面朝下放在桌上。接著, 一次從
桌上摸起一張牌,並將它插入到左手一把牌中的正確位置上。 為了找到這張牌的正確位置,要將
它與手中已有的牌從右到左地進行比較。無論什麼時候,左手中的牌都是排好序的。
如果輸入陣列已經是排好序的話,插入排序出現最佳情況,其執行時間是輸入規模的一個線性函
數。如果輸入陣列是逆序排列的,將出現最壞情況。平均情況與最壞情況一樣,其時間代價是(n2)。
public void sort(int arr[])13/04/2018 Page 234 of 283
{
for(int i =1; i<arr.length;i++)
{
//插入的數
int insertVal = arr[i];
//被插入的位置(準備和前一個數比較)
int index = i-1;
//如果插入的數比被插入的數小
while(index>=0&&insertVal<arr[index])
{
//將把 arr[index] 向後移動
arr[index+1]=arr[index];
//讓 index 向前移動
index--;
}
//把插入的數放入合適位置
arr[index+1]=insertVal;
}
}
21.1.4. 快速排序演算法
快速排序的原理:選擇一個關鍵值作為基準值。比基準值小的都在左邊序列(一般是無序的),
比基準值大的都在右邊(一般是無序的)。 一般選擇序列的第一個元素。
一次迴圈: 從後往前比較,用基準值和最後一個值比較,如果比基準值小的交換位置,如果沒有
繼續比較下一個,直到找到第一個比基準值小的值才交換。 找到這個值之後,又從前往後開始比
較,如果有比基準值大的,交換位置,如果沒有繼續比較下一個,直到找到第一個比基準值大的
值才交換。直到從前往後的比較索引>從後往前比較的索引,結束第一次迴圈,此時,對於基準值
來說,左右兩邊就是有序的了。
public void sort(int[] a,int low,int high){
int start = low;
int end = high;13/04/2018 Page 235 of 283
int key = a[low];
while(end>start){
//從後往前比較
while(end>start&&a[end]>=key)
//如果沒有比關鍵值小的,比較下一個,直到有比關鍵值小的交換位置,然後又從前往後比較
end--;
if(a[end]<=key){
int temp = a[end];
a[end] = a[start];
a[start] = temp;
}
//從前往後比較
while(end>start&&a[start]<=key)
//如果沒有比關鍵值大的,比較下一個,直到有比關鍵值大的交換位置
start++;
if(a[start]>=key){
int temp = a[start];
a[start] = a[end];
a[end] = temp;
}
//此時第一次迴圈比較結束,關鍵值的位置已經確定了。左邊的值都比關鍵值小,右邊的
值都比關鍵值大,但是兩邊的順序還有可能是不一樣的,進行下面的遞迴呼叫
}
//遞迴
if(start>low) sort(a,low,start-1);//左邊序列。第一個索引位置到關鍵值索引-1
if(end<high) sort(a,end+1,high);//右邊序列。從關鍵值索引+1 到最後一個
}
}13/04/2018 Page 236 of 283
21.1.1. 希爾排序演算法
基本思想:先將整個待排序的記錄序列分割成為若干子序列分別進行直接插入排序,待整個序列
中的記錄“基本有序” 時,再對全體記錄進行依次直接插入排序。
1. 操作方法:
選擇一個增量序列 t1, t2, …, tk,其中 ti>tj, tk=1;
2. 按增量序列個數 k,對序列進行 k 趟排序;
3. 每趟排序,根據對應的增量 ti,將待排序列分割成若干長度為 m 的子序列,分別對各子表進
行直接插入排序。僅增量因子為1 時,整個序列作為一個表來處理,表長度即為整個序列的長
度。13/04/2018 Page 237 of 283
private void shellSort(int[] a) {
int dk = a.length/2;
while( dk >= 1 ){
ShellInsertSort(a, dk);
dk = dk/2;
}
}
private void ShellInsertSort(int[] a, int dk) {
//類似插入排序,只是插入排序增量是 1,這裡增量是 dk,把 1 換成 dk 就可以了
for(int i=dk;i<a.length;i++){
if(a[i]<a[i-dk]){
int j;
int x=a[i];//x 為待插入元素
a[i]=a[i-dk];
for(j=i-dk; j>=0 && x<a[j];j=j-dk){
//通過迴圈,逐個後移一位找到要插入的位置。
a[j+dk]=a[j];
}
a[j+dk]=x;//插入
}
}
}
21.1.2. 歸併排序演算法
歸併(Merge)排序法是將兩個(或兩個以上)有序表合併成一個新的有序表,即把待排序序列
分為若干個子序列,每個子序列是有序的。然後再把有序子序列合併為整體有序序列。13/04/2018 Page 238 of 283
public class MergeSortTest {
public static void main(String[] args) {
int[] data = new int[] { 5, 3, 6, 2, 1, 9, 4, 8, 7 };
print(data);
mergeSort(data);
System.out.println("排序後的陣列: ");
print(data);
}
public static void mergeSort(int[] data) {
sort(data, 0, data.length - 1);
}
public static void sort(int[] data, int left, int right) {
if (left >= right)
return;
// 找出中間索引
int center = (left + right) / 2;
// 對左邊陣列進行遞迴
sort(data, left, center);
// 對右邊陣列進行遞迴
sort(data, center + 1, right);
// 合併
merge(data, left, center, right);
print(data);
}
/**
* 將兩個陣列進行歸併,歸併前面 2 個數組已有序,歸併後依然有序13/04/2018 Page 239 of 283
*
* @param data
* 陣列物件
* @param left
* 左陣列的第一個元素的索引
* @param center
* 左陣列的最後一個元素的索引, center+1 是右陣列第一個元素的索引
* @param right
* 右陣列最後一個元素的索引
*/
public static void merge(int[] data, int left, int center, int right) {
// 臨時陣列
int[] tmpArr = new int[data.length];
// 右陣列第一個元素索引
int mid = center + 1;
// third 記錄臨時陣列的索引
int third = left;
// 快取左陣列第一個元素的索引
int tmp = left;
while (left <= center && mid <= right) {
// 從兩個陣列中取出最小的放入臨時陣列
if (data[left] <= data[mid]) {
tmpArr[third++] = data[left++];
} else {
tmpArr[third++] = data[mid++];
}
}
// 剩餘部分依次放入臨時陣列(實際上兩個 while 只會執行其中一個)
while (mid <= right) {
tmpArr[third++] = data[mid++];13/04/2018 Page 240 of 283
}
while (left <= center) {
tmpArr[third++] = data[left++];
}
// 將臨時陣列中的內容拷貝回原陣列中
// (原 left-right 範圍的內容被複制回原陣列)
while (tmp <= right) {
data[tmp] = tmpArr[tmp++];
}
}
public static void print(int[] data) {
for (int i = 0; i < data.length; i++) {
System.out.print(data[i] + "\t");
}
System.out.println();
}
}
21.1.3. 桶排序演算法
桶排序的基本思想是: 把陣列 arr 劃分為 n 個大小相同子區間(桶),每個子區間各自排序,最
後合併 。計數排序是桶排序的一種特殊情況,可以把計數排序當成每個桶裡只有一個元素的情況。
1.找出待排序陣列中的最大值 max、最小值 min
2.我們使用 動態陣列 ArrayList 作為桶,桶裡放的元素也用 ArrayList 儲存。桶的數量為(maxmin)/arr.length+1
3.遍歷陣列 arr,計算每個元素 arr[i] 放的桶
4.每個桶各自排序
public static void bucketSort(int[] arr){
int max = Integer.MIN_VALUE;
int min = Integer.MAX_VALUE;
for(int i = 0; i < arr.length; i++){13/04/2018 Page 241 of 283
max = Math.max(max, arr[i]);
min = Math.min(min, arr[i]);
}
//建立桶
int bucketNum = (max - min) / arr.length + 1;
ArrayList<ArrayList<Integer>> bucketArr = new ArrayList<>(bucketNum);
for(int i = 0; i < bucketNum; i++){
bucketArr.add(new ArrayList<Integer>());
}
//將每個元素放入桶
for(int i = 0; i < arr.length; i++){
int num = (arr[i] - min) / (arr.length);
bucketArr.get(num).add(arr[i]);
}
//對每個桶進行排序
for(int i = 0; i < bucketArr.size(); i++){
Collections.sort(bucketArr.get(i));
}
}
21.1.4. 基數排序演算法
將所有待比較數值(正整數)統一為同樣的數位長度,數位較短的數前面補零。然後,從最低位
開始,依次進行一次排序。這樣從最低位排序一直到最高位排序完成以後,數列就變成一個有序序
列。
public class radixSort {
inta[]={49,38,65,97,76,13,27,49,78,34,12,64,5,4,62,99,98,54,101,56,17,18,23,34,15,35,2
5,53,51};
public radixSort(){
sort(a);
for(inti=0;i<a.length;i++){
System.out.println(a[i]);
}
}
public void sort(int[] array){
//首先確定排序的趟數;
int max=array[0];
for(inti=1;i<array.length;i++){
if(array[i]>max){13/04/2018 Page 242 of 283
max=array[i];
}
}
int time=0;
//判斷位數;
while(max>0){
max/=10;
time++;
}
//建立 10 個佇列;
List<ArrayList> queue=newArrayList<ArrayList>();
for(int i=0;i<10;i++){
ArrayList<Integer>queue1=new ArrayList<Integer>();
queue.add(queue1);
}
//進行 time 次分配和收集;
for(int i=0;i<time;i++){
//分配陣列元素;
for(intj=0;j<array.length;j++){
//得到數字的第 time+1 位數;
int x=array[j]%(int)Math.pow(10,i+1)/(int)Math.pow(10, i);
ArrayList<Integer>queue2=queue.get(x);
queue2.add(array[j]);
queue.set(x, queue2);
}
int count=0;//元素計數器;
//收集佇列元素;
for(int k=0;k<10;k++){
while(queue.get(k).size()>0){
ArrayList<Integer>queue3=queue.get(k);
array[count]=queue3.get(0);
queue3.remove(0);
count++;
}
}
}
}
}13/04/2018 Page 243 of 283
21.1.5. 剪枝演算法
在搜尋演算法中優化中,剪枝,就是通過某種判斷,避免一些不必要的遍歷過程,形象的說,就是
剪去了搜尋樹中的某些“枝條”,故稱剪枝。應用剪枝優化的核心問題是設計剪枝判斷方法,即
確定哪些枝條應當捨棄,哪些枝條應當保留的方法。
21.1.6. 回溯演算法
回溯演算法實際上一個類似列舉的搜尋嘗試過程,主要是在搜尋嘗試過程中尋找問題的解,當發現
已不滿足求解條件時,就“回溯”返回,嘗試別的路徑。
21.1.7. 最短路徑演算法
從某頂點出發,沿圖的邊到達另一頂點所經過的路徑中,各邊上權值之和最小的一條路徑叫做最
短路徑。解決最短路的問題有以下演算法, Dijkstra 演算法, Bellman-Ford 演算法, Floyd 演算法和 SPFA
演算法等。
21.1.8. 最大子陣列演算法
21.1.9. 最長公共子序演算法
21.1.10. 最小生成樹演算法
現在假設有一個很實際的問題:我們要在 n 個城市中建立一個通訊網路,則連通這 n 個城市需要
佈置 n-1 一條通訊線路,這個時候我們需要考慮如何在成本最低的情況下建立這個通訊網?
於是我們就可以引入連通圖來解決我們遇到的問題, n 個城市就是圖上的 n 個頂點,然後,邊表示
兩個城市的通訊線路,每條邊上的權重就是我們搭建這條線路所需要的成本,所以現在我們有 n 個
頂點的連通網可以建立不同的生成樹,每一顆生成樹都可以作為一個通訊網,當我們構造這個連
通網所花的成本最小時,搭建該連通網的生成樹,就稱為最小生成樹。13/04/2018 Page 244 of 283
構造最小生成樹有很多演算法,但是他們都是利用了最小生成樹的同一種性質: MST 性質(假設
N=(V,{E})是一個連通網, U 是頂點集 V 的一個非空子集,如果(u, v)是一條具有最小權值的邊,
其中 u 屬於 U, v 屬於 V-U,則必定存在一顆包含邊(u, v)的最小生成樹),下面就介紹兩種使
用 MST 性質生成最小生成樹的演算法:普里姆演算法和克魯斯卡爾演算法。13/04/2018 Page 245 of 283
22. 資料結構
22.1.1. 棧(stack)
棧( stack)是限制插入和刪除只能在一個位置上進行的表,該位置是表的末端,叫做棧頂
(top)。它是後進先出(LIFO)的。對棧的基本操作只有 push(進棧)和 pop(出棧)兩種,
前者相當於插入,後者相當於刪除最後的元素。
22.1.2. 佇列(queue)
佇列是一種特殊的線性表,特殊之處在於它只允許在表的前端(front)進行刪除操作,而在表的
後端(rear)進行插入操作,和棧一樣,佇列是一種操作受限制的線性表。進行插入操作的端稱為
隊尾,進行刪除操作的端稱為隊頭。
22.1.3. 連結串列(Link)
連結串列是一種資料結構,和陣列同級。比如, Java 中我們使用的 ArrayList,其實現原理是陣列。而
LinkedList 的實現原理就是連結串列了。連結串列在進行迴圈遍歷時效率不高,但是插入和刪除時優勢明顯。13/04/2018 Page 246 of 283
22.1.4. 散列表(Hash Table)
散列表(Hash table,也叫雜湊表)是一種查詢演算法,與連結串列、樹等演算法不同的是,散列表演算法
在查詢時不需要進行一系列和關鍵字(關鍵字是資料元素中某個資料項的值,用以標識一個數據
元素)的比較操作。
散列表演算法希望能儘量做到不經過任何比較,通過一次存取就能得到所查詢的資料元素,因而必
須要在資料元素的儲存位置和它的關鍵字(可用 key 表示)之間建立一個確定的對應關係,使每個
關鍵字和散列表中一個唯一的儲存位置相對應。因此在查詢時,只要根據這個對應關係找到給定
關鍵字在散列表中的位置即可。這種對應關係被稱為雜湊函式(可用 h(key)表示)。
用的構造雜湊函式的方法有:
(1) 直接定址法: 取關鍵字或關鍵字的某個線性函式值為雜湊地址。
即: h(key) = key 或 h(key) = a * key + b, 其中 a 和 b 為常數。
(2) 數字分析法
(3) 平方取值法: 取關鍵字平方後的中間幾位為雜湊地址。
(4) 摺疊法: 將關鍵字分割成位數相同的幾部分,然後取這幾部分的疊加和作為雜湊地址。
(5) 除留餘數法: 取關鍵字被某個不大於散列表表長 m 的數 p 除後所得的餘數為雜湊地址,
即: h(key) = key MOD p p ≤ m
(6) 隨機數法: 選擇一個隨機函式,取關鍵字的隨機函式值為它的雜湊地址,
即: h(key) = random(key)
22.1.5. 排序二叉樹
首先如果普通二叉樹每個節點滿足:左子樹所有節點值小於它的根節點值,且右子樹所有節點值
大於它的根節點值,則這樣的二叉樹就是排序二叉樹。
22.1.5.1. 插入操作
首先要從根節點開始往下找到自己要插入的位置(即新節點的父節點);具體流程是:新節點與
當前節點比較,如果相同則表示已經存在且不能再重複插入;如果小於當前節點,則到左子樹中13/04/2018 Page 247 of 283
尋找,如果左子樹為空則當前節點為要找的父節點,新節點插入到當前節點的左子樹即可;如果
大於當前節點,則到右子樹中尋找,如果右子樹為空則當前節點為要找的父節點,新節點插入到
當前節點的右子樹即可。
22.1.5.2. 刪除操作
刪除操作主要分為三種情況, 即要刪除的節點無子節點,要刪除的節點只有一個子節點,要刪除
的節點有兩個子節點。
1. 對於要刪除的節點無子節點可以直接刪除,即讓其父節點將該子節點置空即可。
2. 對於要刪除的節點只有一個子節點,則替換要刪除的節點為其子節點。
3. 對於要刪除的節點有兩個子節點, 則首先找該節點的替換節點(即右子樹中最小的節點),
接著替換要刪除的節點為替換節點,然後刪除替換節點。13/04/2018 Page 248 of 283
22.1.5.3. 查詢操作
查詢操作的主要流程為:先和根節點比較,如果相同就返回, 如果小於根節點則到左子樹中
遞迴查詢,如果大於根節點則到右子樹中遞迴查詢。因此在排序二叉樹中可以很容易獲取最
大(最右最深子節點)和最小(最左最深子節點)值。
22.1.6. 紅黑樹
R-B Tree,全稱是 Red-Black Tree,又稱為“紅黑樹”,它一種特殊的二叉查詢樹。紅黑樹的每
個節點上都有儲存位表示節點的顏色,可以是紅(Red)或黑(Black)。
22.1.6.1. 紅黑樹的特性
(1)每個節點或者是黑色,或者是紅色。
(2)根節點是黑色。
(3)每個葉子節點(NIL)是黑色。 [注意:這裡葉子節點,是指為空(NIL 或NULL)的葉子節點! ]
(4)如果一個節點是紅色的,則它的子節點必須是黑色的。
(5)從一個節點到該節點的子孫節點的所有路徑上包含相同數目的黑節點。
22.1.6.1. 左旋
對 x 進行左旋,意味著,將“x 的右孩子”設為“x 的父親節點”;即,將 x 變成了一個左節點(x
成了為 z 的左孩子)!。 因此,左旋中的“左”,意味著“被旋轉的節點將變成一個左節點”。13/04/2018 Page 249 of 283
LEFT-ROTATE(T, x)
y ← right[x] // 前提:這裡假設 x 的右孩子為 y。下面開始正式操作
right[x] ← left[y] // 將 “y 的左孩子” 設為 “x 的右孩子”,即 將β設為 x 的右孩子
p[left[y]] ← x // 將 “x” 設為 “y 的左孩子的父親”,即 將β的父親設為 x
p[y] ← p[x] // 將 “x 的父親” 設為 “y 的父親”
if p[x] = nil[T]
then root[T] ← y // 情況 1:如果 “x 的父親” 是空節點,則將 y 設為根節點
else if x = left[p[x]]
then left[p[x]] ← y // 情況 2:如果 x 是它父節點的左孩子,則將 y 設為“x 的父節點
的左孩子”
else right[p[x]] ← y // 情況 3: (x 是它父節點的右孩子) 將 y 設為“x 的父節點的右孩
子”
left[y] ← x // 將 “x” 設為 “y 的左孩子”
p[x] ← y // 將 “x 的父節點” 設為 “y”
22.1.6.1. 右旋
對 x 進行右旋,意味著,將“x 的左孩子”設為“x 的父親節點”;即,將 x 變成了一個右節點(x
成了為 y 的右孩子)! 因此,右旋中的“右”,意味著“被旋轉的節點將變成一個右節點”。13/04/2018 Page 250 of 283
RIGHT-ROTATE(T, y)
x ← left[y] // 前提:這裡假設 y 的左孩子為 x。下面開始正式操作
left[y] ← right[x] // 將 “x 的右孩子” 設為 “y 的左孩子”,即 將β設為 y 的左孩子
p[right[x]] ← y // 將 “y” 設為 “x 的右孩子的父親”,即 將β的父親設為 y
p[x] ← p[y] // 將 “y 的父親” 設為 “x 的父親”
if p[y] = nil[T]
then root[T] ← x // 情況 1:如果 “y 的父親” 是空節點,則將 x 設為根節點
else if y = right[p[y]]
then right[p[y]] ← x // 情況 2:如果 y 是它父節點的右孩子,則將 x 設為“y 的父節
點的左孩子”
else left[p[y]] ← x // 情況 3: (y 是它父節點的左孩子) 將 x 設為“y 的父節點的左孩
子”
right[x] ← y // 將 “y” 設為 “x 的右孩子”
p[y] ← x // 將 “y 的父節點” 設為 “x”
22.1.6.1. 新增
第一步: 將紅黑樹當作一顆二叉查詢樹,將節點插入。
第二步:將插入的節點著色為"紅色"。
根據被插入節點的父節點的情況,可以將"當節點 z 被著色為紅色節點,並插入二叉樹"劃分為三
種情況來處理。
① 情況說明:被插入的節點是根節點。
處理方法:直接把此節點塗為黑色。
② 情況說明:被插入的節點的父節點是黑色。
處理方法:什麼也不需要做。節點被插入後,仍然是紅黑樹。13/04/2018 Page 251 of 283
③ 情況說明:被插入的節點的父節點是紅色。這種情況下,被插入節點是一定存在非空祖父節點
的;進一步的講,被插入節點也一定存在叔叔節點(即使叔叔節點為空,我們也視之為存在,空節
點本身就是黑色節點)。理解這點之後,我們依據"叔叔節點的情況",將這種情況進一步劃分為 3
種情況(Case)。
第三步: 通過一系列的旋轉或著色等操作,使之重新成為一顆紅黑樹。
22.1.6.2. 刪除
第一步:將紅黑樹當作一顆二叉查詢樹, 將節點刪除。
這和"刪除常規二叉查詢樹中刪除節點的方法是一樣的"。分 3 種情況:
① 被刪除節點沒有兒子,即為葉節點。那麼,直接將該節點刪除就 OK 了。
② 被刪除節點只有一個兒子。那麼,直接刪除該節點,並用該節點的唯一子節點頂替它的位置。
③ 被刪除節點有兩個兒子。那麼,先找出它的後繼節點;然後把“它的後繼節點的內容”複製給
“該節點的內容”;之後,刪除“它的後繼節點”。
第二步:通過"旋轉和重新著色"等一系列來修正該樹,使之重新成為一棵紅黑樹。
因為"第一步"中刪除節點之後,可能會違背紅黑樹的特性。所以需要通過"旋轉和重新著色"來修正
該樹,使之重新成為一棵紅黑樹。
選擇重著色 3 種情況。
① 情況說明: x 是“紅+黑”節點。
處理方法:直接把 x 設為黑色,結束。此時紅黑樹性質全部恢復。
② 情況說明: x 是“黑+黑”節點,且 x 是根。
處理方法:什麼都不做,結束。此時紅黑樹性質全部恢復。
③ 情況說明: x 是“黑+黑”節點,且 x 不是根。
處理方法:這種情況又可以劃分為 4 種子情況。這 4 種子情況如下表所示:13/04/2018 Page 252 of 283
參考: https://www.jianshu.com/p/038585421b73
程式碼實現: https://www.cnblogs.com/skywang12345/p/3624343.html
22.1.7. B-TREE
B-tree 又叫平衡多路查詢樹。一棵 m 階的 B-tree (m 叉樹)的特性如下(其中 ceil(x)是一個取上限
的函式) :
1. 樹中每個結點至多有 m 個孩子;
2. 除根結點和葉子結點外,其它每個結點至少有有 ceil(m / 2)個孩子;
3. 若根結點不是葉子結點,則至少有 2 個孩子(特殊情況:沒有孩子的根結點,即根結點為葉子
結點,整棵樹只有一個根節點);
4. 所有葉子結點都出現在同一層,葉子結點不包含任何關鍵字資訊(可以看做是外部結點或查詢
失敗的結點,實際上這些結點不存在,指向這些結點的指標都為 null);
5. 每個非終端結點中包含有 n 個關鍵字資訊: (n, P0, K1, P1, K2, P2, ......, Kn, Pn)。其
中:
a) Ki (i=1...n)為關鍵字,且關鍵字按順序排序 K(i-1)< Ki。
b) Pi 為指向子樹根的接點,且指標 P(i-1)指向子樹種所有結點的關鍵字均小於 Ki,但都大於 K(i-
1)。
c) 關鍵字的個數 n 必須滿足: ceil(m / 2)-1 <= n <= m-1。13/04/2018 Page 253 of 283
一棵 m 階的 B+tree 和 m 階的 B-tree 的差異在於:
1.有 n 棵子樹的結點中含有 n 個關鍵字; (B-tree 是 n 棵子樹有 n-1 個關鍵字)
2.所有的葉子結點中包含了全部關鍵字的資訊,及指向含有這些關鍵字記錄的指標,且葉子結點本
身依關鍵字的大小自小而大的順序連結。 (B-tree 的葉子節點並沒有包括全部需要查詢的資訊)
3.所有的非終端結點可以看成是索引部分,結點中僅含有其子樹根結點中最大(或最小)關鍵字。
(B-tree 的非終節點也包含需要查詢的有效資訊)
參考: https://www.jianshu.com/p/1ed61b4cca1213/04/2018 Page 254 of 283
22.1.8. 點陣圖
點陣圖的原理就是用一個 bit 來標識一個數字是否存在,採用一個 bit 來儲存一個數據,所以這樣可
以大大的節省空間。 bitmap 是很常用的資料結構, 比如用於 Bloom Filter 中;用於無重複整數的
排序等等。 bitmap 通常基於陣列來實現,陣列中每個元素可以看成是一系列二進位制數,所有元素
組成更大的二進位制集合。
https://www.cnblogs.com/polly333/p/4760275.html13/04/2018 Page 255 of 283
23. 加密演算法
23.1.1. AES
高階加密標準(AES,Advanced Encryption Standard)為最常見的對稱加密演算法(微信小程式加密傳
輸就是用這個加密演算法的)。對稱加密演算法也就是加密和解密用相同的金鑰,具體的加密流程如下
圖:
23.1.2. RSA
RSA 加密演算法是一種典型的非對稱加密演算法,它基於大數的因式分解數學難題,它也是應用最廣
泛的非對稱加密演算法。
非對稱加密是通過兩個金鑰(公鑰-私鑰)來實現對資料的加密和解密的。公鑰用於加密,私鑰用
於解密。13/04/2018 Page 256 of 283
23.1.3. CRC
迴圈冗餘校驗(Cyclic Redundancy Check, CRC)是一種根據網路資料包或電腦檔案等資料產生簡
短固定位數校驗碼的一種雜湊函式,主要用來檢測或校驗資料傳輸或者儲存後可能出現的錯誤。
它是利用除法及餘數的原理來作錯誤偵測的。
23.1.4. MD5
MD5 常常作為檔案的簽名出現,我們在下載檔案的時候,常常會看到檔案頁面上附帶一個擴充套件
名為.MD5 的文字或者一行字元,這行字元就是就是把整個檔案當作原資料通過 MD5 計算後的值,
我們下載檔案後,可以用檢查檔案 MD5 資訊的軟體對下載到的檔案在進行一次計算。兩次結果對
比就可以確保下載到檔案的準確性。 另一種常見用途就是網站敏感資訊加密,比如使用者名稱密碼,
支付簽名等等。隨著 https 技術的普及,現在的網站廣泛採用前臺明文傳輸到後臺, MD5 加密
(使用偏移量)的方式保護敏感資料保護站點和資料安全。13/04/2018 Page 257 of 283
24. 分散式快取
24.1.1. 快取雪崩
快取雪崩我們可以簡單的理解為:由於原有快取失效,新快取未到期間所有原本應該訪問快取的請求都
去查詢資料庫了,而對資料庫 CPU 和記憶體造成巨大壓力,嚴重的會造成資料庫宕機。從而形成一系列
連鎖反應,造成整個系統崩潰。 一般有三種處理辦法:
1. 一般併發量不是特別多的時候,使用最多的解決方案是加鎖排隊。
2. 給每一個快取資料增加相應的快取標記,記錄快取的是否失效,如果快取標記失效,則更新資料緩
存。
3. 為 key 設定不同的快取失效時間。
24.1.2. 快取穿透
快取穿透是指使用者查詢資料,在資料庫沒有,自然在快取中也不會有。這樣就導致使用者查詢的時候,在
快取中找不到,每次都要去資料庫再查詢一遍,然後返回空(相當於進行了兩次無用的查詢)。這樣請
求就繞過快取直接查資料庫,這也是經常提的快取命中率問題。
有很多種方法可以有效地解決快取穿透問題,最常見的則是採用布隆過濾器,將所有可能存在的資料哈
希到一個足夠大的 bitmap 中,一個一定不存在的資料會被這個 bitmap 攔截掉,從而避免了對底層存
儲系統的查詢壓力。另外也有一個更為簡單粗暴的方法, 如果一個查詢返回的資料為空(不管是資料不
存在,還是系統故障),我們仍然把這個空結果進行快取,但它的過期時間會很短,最長不超過五分鐘。
通過這個直接設定的預設值存放到快取,這樣第二次到緩衝中獲取就有值了,而不會繼續訪問資料庫。
24.1.3. 快取預熱
快取預熱就是系統上線後,將相關的快取資料直接載入到快取系統。這樣就可以避免在使用者請求的時候,
先查詢資料庫,然後再將資料快取的問題!使用者直接查詢事先被預熱的快取資料!
24.1.4. 快取更新
快取更新除了快取伺服器自帶的快取失效策略之外(Redis 預設的有 6 中策略可供選擇),我們還可以
根據具體的業務需求進行自定義的快取淘汰,常見的策略有兩種:
(1)定時去清理過期的快取;
(2)當有使用者請求過來時,再判斷這個請求所用到的快取是否過期,過期的話就去底層系統得到新數
據並更新快取。
24.1.5. 快取降級
當訪問量劇增、服務出現問題(如響應時間慢或不響應)或非核心服務影響到核心流程的效能時,仍然
需要保證服務還是可用的,即使是有損服務。系統可以根據一些關鍵資料進行自動降級,也可以配置開
關實現人工降級。降級的最終目的是保證核心服務可用,即使是有損的。而且有些服務是無法降級的
(如加入購物車、結算)。13/04/2018 Page 258 of 28313/04/2018 Page 259 of 283
25. Hadoop
25.1.1. 概念
就是一個大資料解決方案。它提供了一套分散式系統基礎架構。 核心內容包含 hdfs 和
mapreduce。 hadoop2.0 以後引入 yarn.
hdfs 是提供資料儲存的, mapreduce 是方便資料計算的。
1. hdfs 又對應 namenode 和 datanode. namenode 負責儲存元資料的基本資訊,
datanode 直接存放資料本身;
2. mapreduce 對應 jobtracker 和 tasktracker. jobtracker 負責分發任務, tasktracker 負
責執行具體任務;
3. 對應到 master/slave 架構, namenode 和 jobtracker 就應該對應到 master, datanode
和 tasktracker 就應該對應到 slave.
25.1.2. HDFS
25.1.2.1. Client
Client(代表用 戶) 通過與 NameNode 和 DataNode 互動訪問 HDFS 中 的檔案。 Client 提供
了一個類似 POSIX 的檔案系統介面供使用者呼叫。
25.1.2.2. NameNode
整個 Hadoop 叢集中只有一個 NameNode。 它是整個系統的“ 總管”, 負責管理 HDFS 的目
錄樹和相關的檔案元資料資訊。 這些資訊是以“ fsimage”( HDFS 元資料映象檔案)和
“ editlog”(HDFS 檔案改動日誌)兩個檔案形式存放在本地磁碟,當 HDFS 重啟時重新構造出
來的。此外, NameNode 還負責監控各個 DataNode 的健康狀態, 一旦發現某個 DataNode 宕
掉,則將該 DataNode 移出 HDFS 並重新備份其上面的資料。
25.1.2.3. Secondary NameNode
Secondary NameNode 最重要的任務並不是為 NameNode 元資料進行熱備份, 而是定期合併
fsimage 和 edits 日誌, 並傳輸給 NameNode。 這裡需要注意的是,為了減小 NameNode 壓
力, NameNode 自己並不會合併 fsimage 和 edits, 並將檔案儲存到磁碟上, 而是交由
Secondary NameNode 完成。
25.1.2.4. DataNode
一般而言, 每個 Slave 節點上安裝一個 DataNode, 它負責實際的資料儲存, 並將資料資訊定期
彙報給 NameNode。 DataNode 以固定大小的 block 為基本單位組織檔案內容, 預設情況下
block 大小為 64MB。 當用戶上傳一個大的檔案到 HDFS 上時, 該檔案會被切分成若干個 block,
分別儲存到不同的 DataNode ; 同時,為了保證資料可靠, 會將同一個 block 以流水線方式寫到13/04/2018 Page 260 of 283
若干個(預設是 3,該引數可配置)不同的 DataNode 上。 這種檔案切割後儲存的過程是對使用者
透明的。
25.1.3. MapReduce
同 HDFS 一樣, Hadoop MapReduce 也採用了 Master/Slave(M/S)架構,具體如圖所示。它
主要由以下幾個元件組成: Client、 JobTracker、 TaskTracker 和 Task。 下面分別對這幾個元件
進行介紹。
25.1.3.1. Client
使用者編寫的 MapReduce 程式通過 Client 提交到 JobTracker 端; 同時, 使用者可通過 Client 提
供的一些介面檢視作業執行狀態。 在 Hadoop 內部用“作業”(Job) 表示 MapReduce 程式。
一個 MapReduce 程式可對應若干個作業,而每個作業會被分解成若干個 Map/Reduce 任務
(Task)。
25.1.3.2. JobTracker
JobTracker 主要負責資源監控和作業排程。 JobTracker 監控所有 TaskTracker 與作業的健康狀況,
一旦發現失敗情況後,其會將相應的任務轉移到其他節點;同時 JobTracker 會跟蹤任務的執行進
度、資源使用量等資訊,並將這些資訊告訴任務排程器,而排程器會在資源出現空閒時,選擇合
適的任務使用這些資源。在 Hadoop 中,任務排程器是一個可插拔的模組,使用者可以根據自己的
需要設計相應的排程器。13/04/2018 Page 261 of 283
25.1.3.3. TaskTracker
TaskTracker 會週期性地通過 Heartbeat 將本節點上資源的使用情況和任務的執行進度彙報給
JobTracker, 同時接收 JobTracker 傳送過來的命令並執行相應的操作(如啟動新任務、 殺死任
務等)。 TaskTracker 使用“slot” 等量劃分本節點上的資源量。“slot” 代表計算資源(CPU、
記憶體等)。一個 Task 獲取到一個 slot 後才有機會執行,而 Hadoop 排程器的作用就是將各個
TaskTracker 上的空閒 slot 分配給 Task 使用。 slot 分為 Map slot 和 Reduce slot 兩種,分別供
MapTask 和 Reduce Task 使用。 TaskTracker 通過 slot 數目(可配置引數)限定 Task 的併發
度。
25.1.3.4. Task
Task 分為 Map Task 和 Reduce Task 兩種, 均由 TaskTracker 啟動。 HDFS 以固定大小的 block
為基本單位儲存資料, 而對於 MapReduce 而言, 其處理單位是 split。 split 與 block 的對應關
系如圖所示。 split 是一個邏輯概念, 它只包含一些元資料資訊, 比如資料起始位置、資料長度、
資料所在節點等。它的劃分方法完全由使用者自己決定。 但需要注意的是, split 的多少決定了 Map
Task 的數目 ,因為每個 split 會交由一個 Map Task 處理。
Map Task 執行過程如圖所示。 由該圖可知, Map Task 先將對應的 split 迭代解析成一個個
key/value 對,依次呼叫使用者自定義的 map() 函式進行處理,最終將臨時結果存放到本地磁碟上,
其中臨時資料被分成若干個 partition,每個 partition 將被一個 Reduce Task 處理。
25.1.3.5. Reduce Task 執行過程
該過程分為三個階段
1. 從遠端節點上讀取 MapTask 中間結果(稱為“Shuffle 階段”);
2. 按照 key 對 key/value 對進行排序(稱為“ Sort 階段”);
3. 依次讀取<key, value list>,呼叫使用者自定義的 reduce() 函式處理,並將最終結果存到 HDFS
上(稱為“ Reduce 階段”)。13/04/2018 Page 262 of 283
25.1.4. Hadoop MapReduce 作業的生命週期
1.作業提交與初始化
1. 使用者提交作業後, 首先由 JobClient 例項將作業相關資訊, 比如將程式 jar 包、作業配置文
件、 分片元資訊檔案等上傳到分散式檔案系統( 一般為 HDFS)上,其中,分片元資訊檔案
記錄了每個輸入分片的邏輯位置資訊。 然後 JobClient 通過 RPC 通知 JobTracker。
JobTracker 收到新作業提交請求後, 由 作業排程模組對作業進行初始化:為作業建立一個
JobInProgress 物件以跟蹤作業執行狀況, 而 JobInProgress 則會為每個 Task 建立一個
TaskInProgress 物件以跟蹤每個任務的執行狀態, TaskInProgress 可能需要管理多個
“ Task 執行嘗試”( 稱為“ Task Attempt”)。
2.任務排程與監控。
2. 前面提到,任務排程和監控的功能均由 JobTracker 完成。 TaskTracker 週期性地通過
Heartbeat 向 JobTracker 彙報本節點的資源使用 情況, 一旦出 現空閒資源, JobTracker
會按照一定的策略選擇一個合適的任務使用該空閒資源, 這由任務排程器完成。 任務排程器
是一個可插拔的獨立模組, 且為雙層架構, 即首先選擇作業, 然後從該作業中選擇任務, 其
中,選擇任務時需要重點考慮資料本地性。 此外, JobTracker 跟蹤作業的整個執行過程,並
為作業的成功執行提供全方位的保障。 首先, 當 TaskTracker 或者 Task 失敗時, 轉移計算
任務 ; 其次, 當某個 Task 執行進度遠落後於同一作業的其他 Task 時,為之啟動一個相同
Task, 並選取計算快的 Task 結果作為最終結果。
3.任務執行環境準備
3. 執行環境準備包括 JVM 啟動和資源隔 離, 均由 TaskTracker 實現。 TaskTracker 為每個
Task 啟動一個獨立的 JVM 以避免不同 Task 在執行過程中相互影響 ; 同時, TaskTracker 使
用了作業系統程序實現資源隔離以防止 Task 濫用資源。
4.任務執行
4. TaskTracker 為 Task 準備好執行環境後, 便會啟動 Task。 在執行過程中, 每個 Task 的最
新進度首先由 Task 通過 RPC 彙報給 TaskTracker, 再由 TaskTracker 彙報給 JobTracker。
5.作業完成。
5. 待所有 Task 執行完畢後, 整個作業執行成功。13/04/2018 Page 263 of 283
26. Spark
26.1.1. 概念
Spark 提供了一個全面、統一的框架用於管理各種有著不同性質(文字資料、圖表資料等)的資料
集和資料來源(批量資料或實時的流資料)的大資料處理的需求。
26.1.2. 核心架構
Spark Core
包含 Spark 的基本功能;尤其是定義 RDD 的 API、操作以及這兩者上的動作。其他 Spark 的庫都
是構建在 RDD 和 Spark Core 之上的
Spark SQL
提供通過 Apache Hive 的 SQL 變體 Hive 查詢語言(HiveQL)與 Spark 進行互動的 API。每個
資料庫表被當做一個 RDD, Spark SQL 查詢被轉換為 Spark 操作。
Spark Streaming
對實時資料流進行處理和控制。 Spark Streaming 允許程式能夠像普通 RDD 一樣處理實時資料
Mllib
一個常用機器學習演算法庫,演算法被實現為對 RDD 的 Spark 操作。這個庫包含可擴充套件的學習演算法,
比如分類、迴歸等需要對大量資料集進行迭代的操作。
GraphX
控制圖、並行圖操作和計算的一組演算法和工具的集合。 GraphX 擴充套件了 RDD API,包含控制圖、
建立子圖、訪問路徑上所有頂點的操作13/04/2018 Page 264 of 283
26.1.3. 核心元件
Cluster Manager-制整個叢集,監控 worker
在 standalone 模式中即為 Master 主節點,控制整個叢集,監控 worker。在 YARN 模式中為資
源管理器
Worker 節點-負責控制計算節點
從節點,負責控制計算節點,啟動 Executor 或者 Driver。
Driver: 執行 Application 的 main()函式
Executor:執行器,是為某個 Application 執行在 worker node 上的一個程序
26.1.4. SPARK 程式設計模型
Spark 應用程式從編寫到提交、執行、輸出的整個過程如圖所示,圖中描述的步驟如下:13/04/2018 Page 265 of 283
1. 使用者使用 SparkContext 提供的 API(常用的有 textFile、 sequenceFile、 runJob、 stop 等)
編寫 Driver application 程式。此外 SQLContext、 HiveContext 及 StreamingContext 對
SparkContext 進行封裝,並提供了 SQL、 Hive 及流式計算相關的 API。
2. 使用SparkContext提交的使用者應用程式,首先會使用BlockManager和BroadcastManager
將任務的 Hadoop 配置進行廣播。然後由 DAGScheduler 將任務轉換為 RDD 並組織成 DAG,
DAG 還將被劃分為不同的 Stage。最後由 TaskScheduler 藉助 ActorSystem 將任務提交給
叢集管理器(Cluster Manager)。
3. 叢集管理器(ClusterManager)給任務分配資源,即將具體任務分配到Worker上, Worker
建立 Executor 來處理任務的執行。 Standalone、 YARN、 Mesos、 EC2 等都可以作為 Spark
的叢集管理器。
26.1.5. SPARK 計算模型
RDD 可以看做是對各種資料計算模型的統一抽象, Spark 的計算過程主要是 RDD 的迭代計算過
程。 RDD 的迭代計算過程非常類似於管道。分割槽數量取決於 partition 數量的設定,每個分割槽的數
據只會在一個 Task 中計算。所有分割槽可以在多個機器節點的 Executor 上並行執行。13/04/2018 Page 266 of 283
26.1.6. SPARK 執行流程13/04/2018 Page 267 of 283
1. 構建 Spark Application 的執行環境,啟動 SparkContext
2. SparkContext 向資源管理器(可以是 Standalone, Mesos, Yarn)申請執行 Executor 資源,
並啟動 StandaloneExecutorbackend,
3. Executor 向 SparkContext 申請 Task
4. SparkContext 將應用程式分發給 Executor
5. SparkContext 構建成 DAG 圖,將 DAG 圖分解成 Stage、將 Taskset 傳送給 Task Scheduler,
最後由 Task Scheduler 將 Task 傳送給 Executor 執行
6. Task 在 Executor 上執行,執行完釋放所有資源
26.1.7. SPARK RDD 流程
1. 建立 RDD 物件
2. DAGScheduler 模組介入運算,計算 RDD 之間的依賴關係, RDD 之間的依賴關係就形成了
DAG
3. 每一個 Job 被分為多個 Stage。劃分 Stage 的一個主要依據是當前計算因子的輸入是否是確
定的,如果是則將其分在同一個 Stage,避免多個 Stage 之間的訊息傳遞開銷
26.1.8. SPARK RDD
(1) RDD 的建立方式
1)從 Hadoop 檔案系統(或與Hadoop相容的其他持久化儲存系統,如Hive、 Cassandra、
HBase)輸入(例如 HDFS)建立。
2)從父 RDD 轉換得到新 RDD。13/04/2018 Page 268 of 283
3)通過 parallelize 或 makeRDD 將單機資料建立為分散式 RDD。
( 2) RDD 的兩種操作運算元(轉換(Transformation)與行動(Action) )
對於 RDD 可以有兩種操作運算元:轉換(Transformation)與行動(Action)。
1) 轉換(Transformation): Transformation操作是延遲計算的,也就是說從一個RDD轉
換生成另一個 RDD 的轉換操作不是馬上執行,需要等到有 Action 操作的時候才會真正觸
發運算。
2)行動(Action): Action 運算元會觸發 Spark 提交作業(Job),並將資料輸出 Spark 系統。13/04/2018 Page 269 of 283
27. Storm
27.1.1. 概念
Storm 是一個免費並開源的分散式實時計算系統。利用 Storm 可以很容易做到可靠地處理無限的
資料流,像 Hadoop 批量處理大資料一樣, Storm 可以實時處理資料。
27.1.1. 叢集架構
27.1.1.1. Nimbus(master-程式碼分發給 Supervisor)
Storm 叢集的 Master 節點,負責分發使用者程式碼,指派給具體的 Supervisor 節點上的 Worker 節
點,去執行 Topology 對應的元件(Spout/Bolt)的 Task。
27.1.1.2. Supervisor(slave-管理 Worker 程序的啟動和終止)
Storm 叢集的從節點,負責管理執行在 Supervisor 節點上的每一個 Worker 程序的啟動和終止。
通過 Storm 的配置檔案中的 supervisor.slots.ports 配置項,可以指定在一個 Supervisor 上最大
允許多少個 Slot,每個 Slot 通過埠號來唯一標識,一個埠號對應一個 Worker 程序(如果該
Worker 程序被啟動)。
27.1.1.3. Worker(具體處理元件邏輯的程序)
執行具體處理元件邏輯的程序。 Worker 執行的任務型別只有兩種,一種是 Spout 任務,一種是
Bolt 任務。13/04/2018 Page 270 of 283
27.1.1.4. Task
worker中每一個spout/bolt的執行緒稱為一個task. 在storm0.8 之後, task不再與物理執行緒對應,
不同 spout/bolt 的 task 可能會共享一個物理執行緒,該執行緒稱為 executor。
27.1.1.5. ZooKeeper
用來協調 Nimbus 和 Supervisor,如果 Supervisor 因故障出現問題而無法執行 Topology,
Nimbus 會第一時間感知到,並重新分配 Topology 到其它可用的 Supervisor 上執行
27.1.2. 程式設計模型(spout->tuple->bolt)
strom 在執行中可分為 spout 與 bolt 兩個元件,其中,資料來源從 spout 開始,資料以 tuple 的方
式傳送到 bolt,多個 bolt 可以串連起來,一個 bolt 也可以接入多個 spot/bolt.執行時原理如下圖:
27.1.2.1. Topology
Storm 中執行的一個實時應用程式的名稱。將 Spout、 Bolt 整合起來的拓撲圖。定義了 Spout 和
Bolt 的結合關係、併發數量、配置等等。
27.1.2.2. Spout
在一個 topology 中獲取源資料流的元件。通常情況下 spout 會從外部資料來源中讀取資料,然後轉
換為 topology 內部的源資料。
27.1.2.3. Bolt
接受資料然後執行處理的元件,使用者可以在其中執行自己想要的操作。
27.1.2.4. Tuple
一次訊息傳遞的基本單元,理解為一組訊息就是一個 Tuple。13/04/2018 Page 271 of 283
27.1.2.5. Stream
Tuple 的集合。表示資料的流向。
27.1.3. Topology 執行
在 Storm 中,一個實時應用的計算任務被打包作為 Topology 釋出,這同 Hadoop MapReduce
任務相似。但是有一點不同的是:在 Hadoop 中, MapReduce 任務最終會執行完成後結束;而在
Storm 中, Topology 任務一旦提交後永遠不會結束,除非你顯示去停止任務。計算任務
Topology 是由不同的 Spouts 和 Bolts,通過資料流(Stream)連線起來的圖。一個 Storm 在集
群上執行一個 Topology 時,主要通過以下 3 個實體來完成 Topology 的執行工作:
(1). Worker(程序)
(2). Executor(執行緒)
(3). Task
27.1.3.1. Worker(1 個 worker 程序執行的是 1 個 topology 的子集)
1 個 worker 程序執行的是 1 個 topology 的子集(注:不會出現 1 個 worker 為多個 topology
服務)。 1 個 worker 程序會啟動 1 個或多個 executor 執行緒來執行 1 個 topology 的
component(spout 或 bolt)。因此, 1 個執行中的 topology 就是由叢集中多臺物理機上的多個
worker 程序組成的。
27.1.3.2. Executor(executor 是 1 個被 worker 程序啟動的單獨執行緒)
executor 是 1 個被 worker 程序啟動的單獨執行緒。每個 executor 只會執行 1 個 topology 的 1 個
component(spout 或 bolt)的 task(注: task 可以是 1 個或多個, storm 預設是 1 個
component 只生成 1 個 task, executor 執行緒裡會在每次迴圈裡順序呼叫所有 task 例項)。13/04/2018 Page 272 of 283
27.1.3.3. Task(最終執行 spout 或 bolt 中程式碼的單元)
是最終執行 spout 或 bolt 中程式碼的單元(注: 1 個 task 即為 spout 或 bolt 的 1 個例項,
executor 執行緒在執行期間會呼叫該 task 的 nextTuple 或 execute 方法)。 topology 啟動後, 1
個 component(spout 或 bolt)的 task 數目是固定不變的,但該 component 使用的 executor 線
程數可以動態調整(例如: 1 個 executor 執行緒可以執行該 component 的 1 個或多個 task 實
例)。這意味著,對於 1 個 component 存在這樣的條件: #threads<=#tasks(即:執行緒數小於
等於 task 數目)。預設情況下 task 的數目等於 executor 執行緒數目,即 1 個 executor 執行緒只運
行 1 個 task。
27.1.4. Storm Streaming Grouping
Storm 中最重要的抽象,應該就是 Stream grouping 了,它能夠控制 Spot/Bolt 對應的 Task 以
什麼樣的方式來分發 Tuple,將 Tuple 發射到目的 Spot/Bolt 對應的 Task.13/04/2018 Page 273 of 283
目前, Storm Streaming Grouping 支援如下幾種型別:
27.1.4.1. huffle Grouping
隨機分組,儘量均勻分佈到下游 Bolt 中將流分組定義為混排。這種混排分組意味著來自 Spout 的
輸入將混排,或隨機分發給此 Bolt 中的任務。 shuffle grouping 對各個 task 的 tuple 分配的比
較均勻。
27.1.4.2. Fields Grouping
按欄位分組,按資料中 field 值進行分組;相同 field 值的 Tuple 被髮送到相同的 Task 這種
grouping 機制保證相同 field 值的 tuple 會去同一個 task。
27.1.4.3. All grouping :廣播
廣播發送, 對於每一個 tuple 將會複製到每一個 bolt 中處理。13/04/2018 Page 274 of 283
27.1.4.4. Global grouping
全域性分組, Tuple 被分配到一個 Bolt 中的一個 Task,實現事務性的 Topology。 Stream 中的所
有的 tuple 都會發送給同一個 bolt 任務處理,所有的 tuple 將會發送給擁有最小 task_id 的 bolt
任務處理。
27.1.4.5. None grouping :不分組
不關注並行處理負載均衡策略時使用該方式,目前等同於 shuffle grouping,另外 storm 將會把
bolt 任務和他的上游提供資料的任務安排在同一個執行緒下。
27.1.4.6. Direct grouping :直接分組 指定分組
由 tuple 的發射單元直接決定 tuple 將發射給那個 bolt,一般情況下是由接收 tuple 的 bolt 決定
接收哪個 bolt 發射的 Tuple。這是一種比較特別的分組方法,用這種分組意味著訊息的傳送者指
定由訊息接收者的哪個 task 處理這個訊息。 只有被宣告為 Direct Stream 的訊息流可以宣告這種
分組方法。而且這種訊息 tuple 必須使用 emitDirect 方法來發射。訊息處理者可以通過
TopologyContext 來獲取處理它的訊息的 taskid (OutputCollector.emit 方法也會返回
taskid)。13/04/2018 Page 275 of 283
28. YARN
28.1.1. 概念
YARN 是一個資源管理、任務排程的框架,主要包含三大模組: ResourceManager(RM)、
NodeManager(NM)、 ApplicationMaster(AM)。其中, ResourceManager 負責所有資
源的監控 、分配和管理 ; ApplicationMaster 負責每一個具體應用程式的排程和協調 ;
NodeManager 負責每一個節點的維護。對於所有的 applications, RM 擁有絕對的控制權和對資
源的分配權。而每個 AM 則會和 RM 協商資源,同時和 NodeManager 通訊來執行和監控 task。
幾個模組之間的關係如圖所示。
28.1.2. ResourceManager
1. ResourceManager 負責整個叢集的資源管理和分配,是一個全域性的資源管理系統。
2. NodeManager 以心跳的方式向 ResourceManager 彙報資源使用情況(目前主要是 CPU 和
記憶體的使用情況)。 RM 只接受 NM 的資源回報資訊,對於具體的資源處理則交給 NM 自己
處理。
3. YARN Scheduler 根據 application 的請求為其分配資源,不負責 application job 的監控、
追蹤、執行狀態反饋、啟動等工作。
28.1.3. NodeManager
1. NodeManager 是每個節點上的資源和工作管理員,它是管理這臺機器的代理,負責該節點
程式的執行,以及該節點資源的管理和監控。 YARN叢集每個節點都執行一個NodeManager。13/04/2018 Page 276 of 283
2. NodeManager 定時向 ResourceManager 彙報本節點資源(CPU、記憶體)的使用情況和
Container 的執行狀態。當 ResourceManager 宕機時 NodeManager 自動連線 RM 備用節
點。
3. NodeManager 接收並處理來自 ApplicationMaster 的 Container 啟動、停止等各種請求。
28.1.4. ApplicationMaster
使用者提交的每個應用程式均包含一個 ApplicationMaster,它可以執行在 ResourceManager 以外
的機器上。
1. 負責與 RM 排程器協商以獲取資源(用 Container 表示)。
2. 將得到的任務進一步分配給內部的任務(資源的二次分配)。
3. 與 NM 通訊以啟動/停止任務。
4. 監控所有任務執行狀態,並在任務執行失敗時重新為任務申請資源以重啟任務。
5. 當前 YARN 自帶了兩個 ApplicationMaster 實現,一個是用於演示 AM 編寫方法的例項程式
DistributedShell,它可以申請一定數目的 Container 以並行執行一個 Shell 命令或者 Shell
指令碼;另一個是執行 MapReduce 應用程式的 AM—MRAppMaster。
注: RM 只負責監控 AM,並在 AM 執行失敗時候啟動它。 RM 不負責 AM 內部任務的容錯,任務
的容錯由 AM 完成。13/04/2018 Page 277 of 283
28.1.5.YARN 執行流程
1. client 向 RM 提交應用程式,其中包括啟動該應用的 ApplicationMaster 的必須資訊,例如
ApplicationMaster 程式、啟動 ApplicationMaster 的命令、使用者程式等。
2. ResourceManager 啟動一個 container 用於執行 ApplicationMaster。
3. 啟動中的ApplicationMaster向ResourceManager註冊自己,啟動成功後與RM保持心跳。
4. ApplicationMaster 向 ResourceManager 傳送請求,申請相應數目的 container。
5. ResourceManager 返回 ApplicationMaster 的申請的 containers 資訊。申請成功的
container,由 ApplicationMaster 進行初始化。 container 的啟動資訊初始化後, AM 與對
應的 NodeManager 通訊,要求 NM 啟動 container。 AM 與 NM 保持心跳,從而對 NM 上
執行的任務進行監控和管理。
6. container 執行期間, ApplicationMaster 對 container 進行監控。 container 通過 RPC 協議
向對應的 AM 彙報自己的進度和狀態等資訊。
7. 應用執行期間, client 直接與 AM 通訊獲取應用的狀態、進度更新等資訊。
8. 應用執行結束後, ApplicationMaster 向 ResourceManager 登出自己,並允許屬於它的
container 被收回。13/04/2018 Page 278 of 283
29. 機器學習
29.1.1. 決策樹
29.1.2. 隨機森林演算法
29.1.3. 邏輯迴歸
29.1.4. SVM
29.1.5. 樸素貝葉斯
29.1.6. K 最近鄰演算法
29.1.7. K 均值演算法
29.1.8. Adaboost 演算法
29.1.9. 神經網路
29.1.10. 馬爾可夫
參考: http://www.cyzone.cn/a/20170422/310196.html13/04/2018 Page 279 of 283
30. 雲端計算
30.1.1. SaaS
SaaS 是 Software-as-a-Service(軟體即服務)
30.1.2. PaaS
PaaS 是 Platform-as-a-Service 的縮寫,意思是平臺即服務。 把伺服器平臺作為一種服務提供的
商業模式。通過網路進行程式提供的服務稱之為 SaaS(Software as a Service),而云計算時代相
應的伺服器平臺或者開發環境作為服務進行提供就成為了 PaaS(Platform as a Service)。
30.1.3. IaaS
IaaS(Infrastructure as a Service),即基礎設施即服務。 提供給消費者的服務是對所有設施的
利用,包括處理、儲存、網路和其它基本的計算資源,使用者能夠部署和執行任意軟體,包括操作
系統和應用程式。
30.1.4. Docker
30.1.4.1. 概念
Docker 映象
(Images)
Docker 映象是用於建立 Docker 容器的模板。13/04/2018 Page 280 of 283
Docker 的出現一定是因為目前的後端在開發和運維階段確實需要一種虛擬化技術解決開發環境和
生產環境環境一致的問題,通過 Docker 我們可以將程式執行的環境也納入到版本控制中,排除因
為環境造成不同執行結果的可能。但是上述需求雖然推動了虛擬化技術的產生,但是如果沒有合
適的底層技術支撐,那麼我們仍然得不到一個完美的產品。本文剩下的內容會介紹幾種 Docker 使
用的核心技術,如果我們瞭解它們的使用方法和原理,就能清楚 Docker 的實現原理。 Docker 使
用客戶端-伺服器 (C/S) 架構模式,使用遠端 API 來管理和建立 Docker 容器。 Docker 容器通過
Docker 映象來建立。
30.1.4.2. Namespaces
名稱空間(namespaces)是 Linux 為我們提供的用於分離程序樹、網路介面、掛載點以及程序間
通訊等資源的方法。在日常使用 Linux 或者 macOS 時,我們並沒有執行多個完全分離的伺服器的
需要,但是如果我們在伺服器上啟動了多個服務,這些服務其實會相互影響的,每一個服務都能
看到其他服務的程序,也可以訪問宿主機器上的任意檔案,這是很多時候我們都不願意看到的,
我們更希望執行在同一臺機器上的不同服務能做到完全隔離,就像執行在多臺不同的機器上一樣。
Docker 容器
(Container)
容器是獨立執行的一個或一組應用。
Docker 客戶端
(Client)
Docker 客戶端通過命令列或者其他工具使用 Docker API 與 Docker 的守護程序通訊。
Docker 主機
(Host)
一個物理或者虛擬的機器用於執行 Docker 守護程序和容器。
Docker 倉庫
(Registry)
Docker 倉庫用來儲存映象,可以理解為程式碼控制中的程式碼倉庫。
Docker Hub 提供了龐大的映象集合供使用。
Docker
Machine
Docker Machine 是一個簡化 Docker 安裝的命令列工具,通過一個簡單的命令列即可在相
應的平臺上安裝 Docker,比如 VirtualBox、 Digital Ocean、 Microsoft Azure。13/04/2018 Page 281 of 283
Linux 的名稱空間機制提供了以下七種不同的名稱空間,包括 CLONE_NEWCGROUP、
CLONE_NEWIPC、 CLONE_NEWNET、 CLONE_NEWNS、 CLONE_NEWPID、
CLONE_NEWUSER 和 CLONE_NEWUTS,通過這七個選項我們能在建立新的程序時設定新程序
應該在哪些資源上與宿主機器進行隔離。
30.1.4.3. 程序(CLONE_NEWPID 實現的程序隔離)
docker 建立新程序時傳入 CLONE_NEWPID 實現的程序隔離,也就是使用 Linux 的名稱空間實現
程序的隔離, Docker 容器內部的任意程序都對宿主機器的程序一無所知。 當我們每次執行
docker run 或者 docker start 時,都會在建立一個用於設定程序間隔離的 Spec,同時會設定進
程相關的名稱空間,還會設定與使用者、網路、 IPC 以及 UTS 相關的名稱空間, 所有名稱空間相關
的設定 Spec 最後都會作為 Create 函式的入參在建立新的容器時進行設定。
30.1.4.4. Libnetwork 與網路隔離
如果 Docker 的容器通過 Linux 的名稱空間完成了與宿主機程序的網路隔離,但是卻有沒有辦法通過宿
主機的網路與整個網際網路相連,就會產生很多限制,所以 Docker 雖然可以通過名稱空間建立一個隔離
的網路環境,但是 Docker 中的服務仍然需要與外界相連才能發揮作用。
Docker 整個網路部分的功能都是通過 Docker 拆分出來的 libnetwork 實現的,它提供了一個連線不同
容器的實現,同時也能夠為應用給出一個能夠提供一致的程式設計介面和網路層抽象的容器網路模型。
libnetwork 中最重要的概念,容器網路模型由以下的幾個主要元件組成,分別是 Sandbox、
Endpoint 和 Network。 在容器網路模型中,每一個容器內部都包含一個 Sandbox,其中儲存著當前
容器的網路棧配置,包括容器的介面、路由表和 DNS 設定, Linux 使用網路名稱空間實現這個
Sandbox,每一個 Sandbox 中都可能會有一個或多個 Endpoint,在 Linux 上就是一個虛擬的網絡卡
veth, Sandbox 通過 Endpoint 加入到對應的網路中,這裡的網路可能就是我們在上面提到的 Linux
網橋或者 VLAN。
每一個使用 docker run 啟動的容器其實都具有單獨的網路名稱空間, Docker 為我們提供了四種不同
的網路模式, Host、 Container、 None 和 Bridge 模式。
在這一部分,我們將介紹 Docker 預設的網路設定模式:網橋模式。在這種模式下,除了分配隔離的網
絡名稱空間之外, Docker 還會為所有的容器設定 IP 地址。當 Docker 伺服器在主機上啟動之後會建立
新的虛擬網橋 docker0,隨後在該主機上啟動的全部服務在預設情況下都與該網橋相連。 在預設情況下,13/04/2018 Page 282 of 283
每一個容器在建立時都會建立一對虛擬網絡卡,兩個虛擬網絡卡組成了資料的通道,其中一個會放在建立的
容器中,會加入到名為 docker0 網橋中。
30.1.4.5. 資源隔離與 CGroups
Control Groups(簡稱 CGroups) 能夠隔離宿主機器上的物理資源,例如 CPU、記憶體、磁碟 I/O 和網
絡頻寬。每一個 CGroup 都是一組被相同的標準和引數限制的程序,不同的 CGroup 之間是有層級關
系的,也就是說它們之間可以從父類繼承一些用於限制資源使用的標準和引數。
30.1.4.6. 映象與 UnionFS
Linux 的名稱空間和控制組分別解決了不同資源隔離的問題,前者解決了程序、網路以及檔案系統
的隔離,後者實現了 CPU、記憶體等資源的隔離,但是在 Docker 中還有另一個非常重要的問題需
要解決 - 也就是映象。
Docker 映象其實本質就是一個壓縮包,我們可以使用命令將一個 Docker 映象中的檔案匯出, 你
可以看到這個映象中的目錄結構與 Linux 作業系統的根目錄中的內容並沒有太多的區別,可以說
Docker 映象就是一個檔案。
30.1.4.7. 儲存驅動
Docker 使用了一系列不同的儲存驅動管理映象內的檔案系統並執行容器, 這些儲存驅動與
Docker 卷(volume)有些不同,儲存引擎管理著能夠在多個容器之間共享的儲存。
當映象被 docker run 命令建立時就會在映象的最上層新增一個可寫的層,也就是容器層,所有對
於執行時容器的修改其實都是對這個容器讀寫層的修改。13/04/2018 Page 283 of 283
容器和映象的區別就在於,所有的映象都是隻讀的,而每一個容器其實等於映象加上一個可讀寫
的層,也就是同一個映象可以對應多個容器
UnionFS 其實是一種為 Linux 作業系統設計的用於把多個檔案系統『聯合』到同一個掛載點的文
件系統服務。而 AUFS 即 Advanced UnionFS 其實就是 UnionFS 的升級版,它能夠提供更優秀
的效能和效率。
AUFS 只是 Docker 使用的儲存驅動的一種,除了 AUFS 之外, Docker 還支援了不同的儲存驅動,
包括 aufs、 devicemapper、 overlay2、 zfs 和 vfs 等等,在最新的 Docker 中, overlay2 取代了
aufs 成為了推薦的儲存驅動,但是在沒有 overlay2 驅動的機器上仍然會使用 aufs 作為 Docker
的預設驅動。
30.1.5. Openstack