![](http://img3m1.ddimg.cn/17/27/29226401-1_u_3.jpg)
開本:16開 紙張:膠版紙 包裝:平裝-膠訂 是否套裝:否 國際標準書號ISBN:9787519852948 作者:[美]傑拉德·馬斯(Gerard,Maas),[美]弗朗索瓦·加裡洛(Fran?ois,Garillot) 出版社:中國電力出版社 出版時間:2021年04月 
" 編輯推薦 利用分析工具可以快速獲取商業洞察,為此你需要了解如何對數據進行實時處理。通過這本實用指南,對於熟悉Apache Spark的讀者來說,你們將了解如何將這個內存框架應用於流數據。你會發現Spark讓你幾乎可以像面對批處理作業一樣來開發流處理作業。
本書介紹了Apache Spark的理論基礎。這本綜合指南通過兩個部分比較了Spark支持的流處理API:早期的Spark Streaming和後來的Structured Streaming。 內容簡介 ?學習流處理的基本概念、研究各種流處理架構。 ?通過具體實例來研究Structured Streaming、深入理解流處理概念。 ?通過Spark Streaming創建並管理流作業,使用其他Spark API與Spark Streaming集成。 ?學習Spark Streaming高級技巧,包括近似算法和機器學習算法。 ?將Apache Spark與其他流處理項目比較,包括Apache Storm、Apache Flink和Apache Kafka Streams。 作者簡介 Gerard Mass是Lightbend的首席工程師,致力於將Structured Streaming與流處理擴展技術無縫集成到Lightbend平臺中。
François Garillot在Facebook從事於分布式計算相關工作。曾在Lightbend從事Spark Streaming反壓機制研究,同時擁有巴黎綜合理工學院的博士學位。 目錄 目錄 序 1 前言 3 部分 Apache Spark 流處理的基本原理 第1 章 流處理概述 13 什麼是流處理 14 批處理與流處理 15 流處理中的時間概念 15 不確定性因素 16 流處理案例 16 可擴展的數據處理能力 18 擴展性與容錯性 19 分布式流處理 19 Apache Spark 概述 20目錄 序 1 前言 3 部分 Apache Spark 流處理的基本原理 第1 章 流處理概述 13 什麼是流處理 14 批處理與流處理 15 流處理中的時間概念 15 不確定性因素 16 流處理案例 16 可擴展的數據處理能力 18 擴展性與容錯性 19 分布式流處理 19 Apache Spark 概述 20 波浪潮:函數式API 20 第二波浪潮:SQL 21 統一的引擎21 Spark 組件 22 Spark Streaming 23 Structured Streaming 23 接下來? 24 第2 章 流處理模型 25 數據源與接收器 26 不可變流 27 轉換與聚合 27 窗口聚合 28 滾動窗口 28 滑動窗口 29 無狀態處理與有狀態處理 29 有狀態的流 30 案例:Scala 中的本地狀態計算 31 無狀態流,還是有狀態流? 33 時間效應 33 帶時間戳的事件計算 33 通過時間戳來定義時間概念 34 事件時間與處理時間 34 使用水位線計算 37 小結 39 第3 章 流處理架構 41 數據平臺的組件 41 體繫模型 43 在流處理應用中使用批處理組件 44 流處理架構參考 45 Lambda 架構 45 Kappa 架構 46 流處理算法與批處理算法 47 流處理算法有時候本質上與批處理算法完全不同 47 流處理算法難以保證比批處理算法表現更好 48 小結 50 第4 章 流處理引擎Apache Spark 51 兩種API 的由來 51 Spark 的內存使用 52 故障恢復 53 惰性執行 53 緩存使用建議 53 延遲 54 面向吞吐的數據處理 55 Spark 的多語言API 55 快速實現數據分析 56 更多關於Spark 的內容 57 小結 57 第5 章 Spark 分布式處理模型 59 通過集群管理器來運行Apache Spark 59 Spark 的集群管理器 61 分布式繫統的彈性與容錯 62 故障恢復 63 集群管理器對容錯的支持 63 數據交付語義 64 微批處理與一次處理一個 66 微批處理:整體同步處理應用案例 66 一次處理一個 68 微批與一次處理一個:權衡 68 微批處理與一次處理一個方式地結合 69 動態的批次間隔 69 Structured Streaming 處理模型 69 第6 章 Spark 彈性模型 71 Spark 中的彈性分布式數據集 71 Spark 組件 73 Spark 容錯保證 75 Task 失敗恢復 76 stage 失敗恢復 77 Driver 失敗恢復 77 小結 78 參考文獻 81 第二部分 Structured Streaming 第7 章 Structured Streaming 概述 87 學習Structured Streaming 之前 88 批處理分析 89 流分析 92 連接Stream 93 準備流數據93 流式Dataset 操作 94 創建查詢(Query) 95 啟動流處理96 瀏覽數據 97 小結 97 第8 章 Structured Streaming 編程模型 99 初始化Spark 實例 100 source:接收流數據 100 流數據的轉換 102 sink:輸出結果數據 105 format 106 outputMode 107 queryName 107 option 108 options 108 trigger 108 start() 109 小結 109 第9 章 Structured Streaming 實戰 111 消費流source 112 程序邏輯 114 寫入流sink 114 小結 116 第10 章 Structured Streaming 的Source 117 source 概述 117 可靠的source 需要能夠重放數據 118 source 需要提供schema 119 可用的source 121 文件source 122 指定文件格式 122 公共配置 123 常用的文本解析配置項(CSV、JSON) 124 JSON 文件source 125 CSV 文件source127 Parquet 文件source 128 文本文件source 128 Kafka source 129 創建Kafka source 130 選擇訂閱主題的方法 131 Kafka source 的配置項 132 Kafka 消費者的配置項 133 Socket source 134 配置 135 操作 136 Rate source 136 第11 章 Structured Streaming 的Sink 139 sink 概述 139 可用的sink 140 可靠的sink 140 實驗型sink 141 sink API 141 深入學習sink 141 文件sink 142 在文件sink 中使用觸發器 143 文件格式的通用配置項 145 常見的時間和日期格式化設置(CSV、JSON) 146 CSV 文件sink 146 JSON 文件sink 147 Parquet 文件sink 148 文本文件sink 148 Kafka sink 148 Kafka 發布模型 148 Kafka sink 的使用 149 內存sink 152 控制臺sink 153 配置項 153 輸出模式 153 Foreach sink 154 ForeachWriter 接口 154 TCP Writer sink: ForeachWriter 使用案例 155 案例總結 158 ForeachWriter 序列化問題排查 159 第12 章 基於事件時間的流處理 161 Structured Streaming 中的事件時間 161 事件時間 163 處理時間 164 水位線 164 基於時間的窗口聚合 165 基於時間的窗口定義 166 如何計算間隔 167 多維聚合 167 滾動窗口和滑動窗口 168 記錄去重 170 小結 170 第13 章 高級狀態操作 173 案例:車隊管理 174 分組狀態操作 174 使用MapGroupsWithState 176 使用FlatMapGroupsWithState 180 輸出模式 182 管理狀態的時變性 183 小結 186 第14 章 Structured Streaming 應用監控 187 Spark 指標子繫統 188 StreamingQuery 實例 189 StreamingQueryListener 接口 192 第15 章 實驗性模塊:連續處理與機器學習 195 連續處理 195 連續處理介紹 195 使用連續處理 197 局限性 198 機器學習 199 學習與應用199 在流中使用機器學習模型 200 案例:利用環境傳感器估計房間使用情況 201 在線訓練 204 參考文獻 207 第三部分 Spark Streaming 第16 章 Spark Streaming 概述 213 DStream 抽像 214 基於DStream 的編程模型 214 基於DStream 的執行模型 216 Spark Streaming 程序結構 216 創建Spark Streaming Context 217 定義DStream 218 定義輸出操作 218 啟動Spark Streaming Context 218 停止Streaming 進程 219 小結 219 第17 章 Spark Streaming 編程模型 221 RDD,DStream 的底層抽像 221 DStream 轉換操作 224 RDD 級別的DStream 轉換228 Count 操作 229 修改結構的轉換操作 230 小結 231 第18 章 Spark Streaming 執行模型 233 整體同步架構 233 Receiver 模型 235 Receiver API 235 Receiver 工作流程 236 Receiver 數據流 237 內部的數據彈性 238 Receiver 的並行 239 資源平衡:Receiver 與處理核數 239 通過預寫式日志(Write-Ahead Log)達到零數據丟失 240 無Receiver(Direct)模式 241 小結 242 第19 章 Spark Streaming 的Source 245 source 分類 245 基礎source 246 基於Receiver 模式的source 246 基於Direct 模式的source 247 常用source 247 文件source 248 隊列source 251 工作流程 252 使用隊列source 測試 252 隊列source 的簡單替代方案:ConstantInputDStream 254 Socket source 256 Kafka source 258 使用Kafka source 260 工作流程 261 哪裡有更多的source 262 第20 章 Spark Streaming 的Sink 263 輸出操作 263 內置的輸出操作 265 print 266 saveAs 繫列 266 foreachRDD 268 使用foreachRDD 編寫自定義sink 268 第三方輸出操作 271 第21 章 基於時間的流處理 273 窗口聚合 273 滾動窗口 274 滑動窗口 275 滑動窗口與批次間隔 275 滑動窗口與滾動窗口 276 使用窗口還是較長的批次間隔 276 窗口上的reduce 操作 277 reduceByWindow 278 reduceByKeyAndWindow 278 countByWindow 278 countByValueAndWindow 279 可逆的窗口聚合操作 279 流的分片 281 小結 281 第22 章 任意有狀態的流處理 283 流的狀態性 283 updateStateByKey 284 updateStateByKey 的局限性 286 性能 286 內存占用 287 使用mapwithState 進行有狀態的計算 287 使用mapWithState 289 使用mapWithState 進行基於事件時間的流計算 291 第23 章 使用Spark SQL 295 Spark SQL 296 在Spark Streaming 中使用Spark SQL 函數 297 靜態數據處理 301 Join 優化 304 更新流處理程序中的參考數據集 307 小結 309 第24 章 checkpoint 機制 311 checkpoint 的使用 312 DStream 的checkpoint 機制 317 從checkpoint 中恢復 318 checkpoint 的代價 320 checkpoint 調優 320 第25 章 Spark Streaming 應用監控 323 Streaming UI 324 通過Streaming UI 了解作業性能 326 輸入速率圖326 調度延遲圖327 處理時間圖328 總延遲圖 328 批次細節 329 監控REST API 331 監控REST API 的使用 331 監控REST API 詳解 331 指標子繫統 333 內部事件總線 335 小結 339 第26 章 性能調優 341 Spark Streaming 中的性能平衡 341 批次間隔與處理延遲的關繫 342 作業是如何失敗的 342 深入理解調度延遲和處理延遲 343 checkpoint 對處理時間的影響 344 影響作業性能的外部因素 345 如何提高性能 345 調整批次間隔 346 以固定速率對數據攝入限流 346 反壓 347 動態限流 348 PID 反壓調優 349 自定義速率估算器 349 另一種動態處理策略 350 緩存 350 推測執行 352 參考文獻 353 第四部分 Spark Streaming 高級技巧 第27 章 流式近似與抽樣算法 357 準確性、實時性與大數據 358 準確性 358 實時處理 358 大數據 359 準確性、實時性與大數據之間的三角關繫 359 近似算法 362 哈希與素描 362 去重統計:HyperLogLog 363 角色扮演練習:假如我們是繫統管理員 364 HyperLogLog 在Spark 中的實踐 367 布隆過濾器概述 372 在Spark 中使用布隆過濾器 373 使用Count-min Sketch 統計頻率 373 排名與分位數:T-Digest 算法 376 隨機抽樣 379 分層抽樣 380 第28 章 實時機器學習 381 利用樸素貝葉斯進行流分類 382 streamDM 介紹384 樸素貝葉斯實踐案例 385 訓練一個電影評論分類器 386 決策樹介紹 387 Hoeffding 樹 389 使用在線K-means 進行流聚類 392 K-means 聚類 392 在線數據與在線K-means 算法 393 聚類的衰退問題 394 使用Spark Streaming 運行流式K-means 396 參考文獻 399 第五部分 Apache Spark 之外的世界 第29 章 其他分布式實時流處理繫統 405 Apache Storm 405 編程模型 406 Storm 拓撲 406 Storm 集群 407 與Spark 相比 407 Apache Flink 408 流處理優先的框架 408 與Spark 相比 409 Kafka Streams 409 Kafka Streams 編程模型 410 與Spark 相比 410 雲上的流處理繫統 411 AWS 上的Amazon Kinesis 411 微軟的Azure Stream Analytics 412 Apache Beam 和Google Cloud Dataflow 413 第30 章 展望 417 緊隨Spark 項目 418 在Stack Overflow 上尋求幫助 418 在郵件列表中進行討論 418 參加會議 419 參加交流會 419 貢獻Apache Spark 項目 419 參考文獻 421 作者介紹 423 封面介紹 423 前言 前言 本書適合哪些讀者 本書針對專業的軟件從業人員,他們可能經常與數據打交道,或者希望在流處理領域提高相應的知識技能,或者已經了解Apache Spark,又或者希望利用Apache Spark 來構建流式應用。 本書對於流處理背後的概念進行了全面的介紹。這些概念是理解Apache Spark 兩種流式API(Structured Streaming 和Spark Streaming)的基礎。 對於這些API 我們會進行深入研究,並對其特性、應用詳細介紹,以及根據實踐經驗提供相應建議。 除了介紹API 和實際應用之外,我們還討論了流處理的高級技巧。 所有人都值得仔細閱讀這篇序言,隻不過專業經驗更加豐富的讀者可能會從高級技巧的篇章中受益更多,並知道如何進一步學習。前言 本書適合哪些讀者 本書針對專業的軟件從業人員,他們可能經常與數據打交道,或者希望在流處理領域提高相應的知識技能,或者已經了解Apache Spark,又或者希望利用Apache Spark 來構建流式應用。 本書對於流處理背後的概念進行了全面的介紹。這些概念是理解Apache Spark 兩種流式API(Structured Streaming 和Spark Streaming)的基礎。 對於這些API 我們會進行深入研究,並對其特性、應用詳細介紹,以及根據實踐經驗提供相應建議。 除了介紹API 和實際應用之外,我們還討論了流處理的高級技巧。 所有人都值得仔細閱讀這篇序言,隻不過專業經驗更加豐富的讀者可能會從高級技巧的篇章中受益更多,並知道如何進一步學習。 這裡並未假定你所需的Spark 基礎知識,但如果你對於Spark 數據處理不熟悉,需要注意本書的核心是Spark 流式API 及其功能。關於Spark 功能以及相關生態的基礎知識,我們推薦Bill Chambers 和Matei Zaharia 所著的《Spark: The Definitive Guide》。 本書所使用的編程語言為Scala。雖然Spark 在Scala 之外還提供了Java、Python,以及R 語言的支持,但是我們仍然認為Scala 是流式編程的語言。雖然很多代碼示例可以被翻譯為其他語言,但是在某些領域(比如復雜的狀態計算)Scala 還是合適的編程語言。 安裝Spark Spark 是Apache 基金會正式托管的Apache 開源項目,不過大部分人都是在Github 上對其進行開發。你可以在以下地址下載二進制預編譯包:https://spark.apache.org/downloads.html。 你可以將Spark 運行在一臺或者多臺機器上,這一點我們會稍後解釋。目前對於主要的Linux 發行版Spark 都有對應的安裝包,這對於安裝來說很方便。 本書我們所使用的代碼示例都是兼容Spark 2.4.0 版本的,除了少量的輸出和格式化細節之外,這些示例應該能夠兼容Spark 的未來版本。 另外由於Spark 是運行在Java 虛擬機(JVM)上的,所以你需要安裝它,並且在Spark 組件運行的所有機器上都可以訪問。 對於安裝Java 開發包(JDK),我們推薦使用OpenJDK,它一般已經被打包在多數繫統中了。 當然,你也可以安裝Oracle JDK。 與Scala 程序類似,Spark 也是運行在JDK 6 或者更高版本的繫統上。現在推薦的Spark 所依賴Java 版本是: • Spark 2.0 以下版本,推薦Java 7。 • Spark 2.0 及其以上版本,推薦Java 8。 Scala 基礎 本書中的代碼示例由Scala 編寫。它也是Spark 核心的實現語言,但它並非可用的語言,Spark 也提供了Python、Java,以及R 語言的API。 Scala 是目前世界上特性完備的編程語言之一,因為它同時提供了函數式與面向對像的支持。而且,它的簡潔性和類型推斷機制讓其語法易於理解。 從教育學的角度來看,Scala 作為一門初學語言有很多優勢,其中重要的是它的語法規則以及語義表示。 ——Björn Regnell,隆德大學 因此,我們希望本書的示例足夠簡潔,讓任何讀者都能夠理解其含義。對於想要Scala 入門教程以及喜歡通過書籍學習的讀者來說,我們推薦《Atomic Scala》[Eckel2013]。還有些讀者可能隻是想要一本參考書來擴展知識, 那麼推薦《Programming in Scala》[Odersky2016]。 學習指引 本書分為五個部分: • 部分繼續深入講解我們在序言中提到的這些概念。我們將會介紹流處理的基本概念及其實現架構,深入學習Spark。 • 第二部分,我們會學習Structured Streaming 及其編程模型,以及如何實現流處理程序,包括從簡單的無狀態轉換到高級的有狀態操作。還會討論與監控工具的集成從而支持24/7 級別的運維,以及目前還在開發階段的實驗性模塊。 • 第三部分,我們會學習Spark Streaming。與Structured Streaming 類似,我們會學習如何創建流處理程序,管理Spark Streaming 作業,並與Spark 中其他API集成。後會簡要介紹性能調優的相關內容。 • 第四部分將介紹流處理的高級技巧,用概率數據結構和近似技術解決流處理中的一些挑戰,並探討Spark Streaming 對於在線機器學習方面的局限。 • 第五部分談到Apache Spark 之外的世界。我們調研了其他的流處理器,並提供進一步學習Spark 以及流處理相關內容的方法路徑。 這裡推薦你通過部分的閱讀來理解流處理中的概念。這會對於理解接下來的相關術語和概念很有幫助。 第二部分介紹Structured Streaming,第三部分介紹Spark Streaming,內容結構與前者類似。你可以選擇其中一個,這取決於你的興趣和當前的優先級: • 你可能正在啟動一個新項目或者希望了解Structured Streaming ?那麼可以從第二部分開始閱讀。 • 你當前的項目已經使用了Spark Streaming,而你又希望更加了解它?那麼請從第三部分開始閱讀。 第四部分則對於概率結構背後的數學知識進行了深入介紹,正所謂“坎坷前面是美景”。 第五部分將Spark 流處理與其他流處理框架進行比較,有助於你嘗試多種技術選型並確認終方案。 本書在線資源可以對你的學習進行補充,其中notebooks 以及代碼可以自行嘗試。 當然也可以使用其中的代碼來開始自己的項目。在線資源的地址是https://github.com/stream-processing-with-spark。 我們非常高興能夠把這些知識以及自身經驗記錄下來,希望你能夠喜歡上這本書。 參考書籍 • [Eckel2013] Eckel, Bruce and Dianne Marsh, Atomic Scala (Mindview LLC, 2013). • [Odersky2016] Odersky, Martin, Lex Spoon, and Bill Venners, Programming in Scala, 3rd ed. (Artima Press, 2016). 排版約定 在本書中使用以下排版約定: 斜體字(Italic) 表示新的術語、URL 網址、email 地址、文件名和文件擴展名。 等寬字體(Constant width) 表示代碼行以及段落內引用的程素,如變量或函數名、數據庫、數據類型、環境變量、語句和關鍵詞。 等寬黑體(Constant width bold) 表示由用戶輸入的命令或其他文本。 等寬斜體(Constant width italic) 表示應替換為用戶提供的值或由上下文確定的值的文本。 使用示例代碼 本書在線資源中的補充資料(交互式的notebooks、工程代碼示例,以及一些項目)可以擴充你的學習,幫助你進行嘗試、理解相關的技術知識。具體地址是https://github.com/stream-processing-with-spark。 需要注意的是,notebooks 需要運行在Spark Notebook 上。Spark Notebook 是一個基於Web 的交互式編程環境,主要使用Scala 來運行Apache Spark 任務。其中實時組件非常適合流處理編程使用,可用於從繫統中獲取的數據做可視化展示。 Spark Notebook 項目位於Github 上:https://github.com/spark-notebook/sparknotebook,也可以直接從其發布站點http://spark-notebook.io 去下載編譯好的版本。 本書的目的在於幫助你更好地完成工作。通常情況下,可以在你的程序或者文檔中使用本書的代碼。不必聯繫我們獲取代碼的使用權,除非你需要使用大量的代碼。例如,在寫程序的時候引用幾段代碼不需要向我們申請許可。但以光盤方式銷售或者重新發行O’Reilly 書中的示例則需要獲得許可。引用本書或引用本書中的示例代碼來回答問題也不需要申請許可。但是,如果要將本書中的大量代碼加入到你的產品文檔,則需要申請許可。 我們欣賞你在引用時注明出處,但不強求。引用通常包括書名、作者、出版社和ISBN。如:“Stream Processing with Apache Spark by Gerard Maas and François Garillot (O’Reilly). Copyright 2019 François Garillot and Gerard Maas Images, 978-1-491-94424-0”。 如果覺得使用示例代碼的情況不屬於前面列出的合理使用或許可範圍,請通過電子郵件聯繫我們,郵箱地址為permissions@oreilly.com。 O’Reilly 在線學習平臺(O’Reilly Online Learning) 近40 年來,O’Reilly Media 致力於提供技術和商業培訓、知識和卓越見解,來幫助眾多公司取得成功。 我們有一群獨家專家和創新者,他們通過圖書、文章、會議和在線學習平臺分享知識和技術。O’Reilly 的在線學習平臺提供按需訪問的直播培訓課程、詳細的學習路徑、交互式編程環境,以及由O’Reilly 和其他200 多家出版社出版的書籍和視頻。詳情請訪問http://oreilly.com。 聯繫我們 任何有關本書的意見或疑問,請按照以下地址聯繫出版社。 美國: O’Reilly Media, Inc. 1005 Gravenstein Highway North Sebastopol, CA 95472 中國: 北京市西城區西直門南大街2 號成銘大廈C 座807 室(100035) 奧萊利技術咨詢(北京)有限公司 本書有一個對應網頁,可以看到勘誤表、案例及其他相關信息,具體可以訪問http://bit.ly/stream-proc-apache-spark。 發表評論或咨詢有關本書的技術問題,請發送電子郵件至bookquestions@oreilly.com 郵箱。 關於我們的書籍、課程、會議和新聞的更多信息, 請參閱http://www.oreilly.com。 我們的Facebook:http://facebook.com/oreilly。 我們的Twitter:http://twitter.com/oreillymedia。 我們的YouTube:http://www.youtube.com/oreillymedia。 致謝 本書初起源於Spark Streaming 學習手冊,後來迅速發展成全面介紹Apache Spark在流處理方面的資料。這裡要感謝所有的審稿人,你們寶貴的反饋促使了本書發展至今。另外特別感謝來自Datastax 的Russell Spitzer 和Facebook 的Serhat Yilmaz,以及來自Klarrio 的Giselle Van Dongen。 感謝Holden Karau 在本書草稿階段給予的幫助和建議,以及Bill Chambers 對於我們後來新增的Structured Streaming 模塊的幫助。 至於O’Reilly 的編輯Jeff Bleiel,從我們提出初的想法到草稿版本,再到你手裡拿到的這個完整版本,一直全程參與,很有耐心並積極地反饋建議。還要感謝我們在O’Reilly 所接觸的位編輯Shannon Cutt,她為整個項目的啟動提供了不少幫助。此外O’Reilly 其他人在本書撰寫的各個階段都給予了不少幫助,這纔讓我們走得更遠。 另外還要感謝Tathagata Das 與我們之間多次的互動交流,特別是在早期寫Spark Streaming 的那段時間,當時我們正在挑戰這個框架所能提供的極限。 Gerard 我要感謝Lightbend 的同事對於我在寫作和工作之間來回忙碌之時所給予的支持和理解。特別要感謝的是Ray Roestenburg 在我困難時刻對我的鼓勵,還有DeanWampler 對於我所做出的努力一直很支持,以及Ruth Stento 對我在寫作風格上給出了良好建議。 特別需要提及的是Kurt Jonckheer、Patrick Goemaere 和Lieven Gesquière,他們給予了我不少機會和空間來加深對於Spark 的理解。感謝Andy Petrella 創建的SparkNotebook,但更重要的是他那富有感染力的激情鼓勵我去探索編程與數據的交集。重要的是,我無比感謝我的妻子Ingrid,兩個女兒Layla 和Juliana,以及我的母親Carmen。如果沒有她們的愛和關心以及理解,那麼不可能完成這個項目。 François 非常感謝Swisscom 以及Facebook 的同事在本書寫作期間給予的支持,感謝Chris Fregly、Paco Nathan 和Ben Lorica 對我的建議和支持,以及我的妻子AJung 所做的一切。 ![](http://img3m1.ddimg.cn/17/27/29226401-2_u_3.jpg) ![](http://img3m1.ddimg.cn/17/27/29226401-3_u_2.jpg) |