摘要:續前文后端好書閱讀與推薦,幾十天過去了,又看了兩本好書還有以前看過的書,這里依然把它們總結歸納一下,加入一些自己的看法有用的鏈接和可能的延伸閱讀,并推薦給需要的同學。
續前文 后端好書閱讀與推薦 - Mageek`s Wonderland ,幾十天過去了,又看了兩本好書(還有以前看過的書),這里依然把它們總結歸納一下,加入一些自己的看法、有用的鏈接和可能的延伸閱讀,并推薦給需要的同學。
深入理解Java虛擬機深入理解Java虛擬機 (豆瓣) https://book.douban.com/subje...
Java怎么用,是一個問題;怎么用好是一個大問題;這么用是為什么,是一個更大的問題。搞懂這三個問題應該是每一個搞Java的人都要追求的目標,讀完本書,就能把這個更大的問題搞懂了。
本書亮點:
模塊化是解決應用系統與技術平臺越來越復雜,越來越龐大的問題的一個重要途徑,也是建立各種功能的標準件的前提。
Java運行時數據區幾個主要部分:程序計數器(可看作當前線程所執行的字節碼的行號指示器)、虛擬機棧(每一個方法從調用直至執行完成的過程,就對應著一個棧幀在虛擬機棧中入棧到出棧的過程,而棧幀存儲局部變量表、 操作數棧、 動態鏈接、 方法出口等信息)、本地方法棧(Native方法對應的棧)、堆(所有線程共享的一塊內存區域,存放對象實例)、方法區(各個線程共享的內存區域,存儲已被虛擬機加載的類信息、常量、靜態變量、即時編譯器編譯后的代碼等數據)、常量池(方法區的一部分,存放編譯期生成的各種字面量和符號引用)。
對象訪問方式取決于虛擬機實現而定的,目前主流的訪問方式有使用句柄和直接指針兩種:如果使用句柄訪問的話,那么Java堆中將會劃分出一塊內存來作為句柄池,reference中存儲的就是對象的句柄地址,而句柄中包含了對象實例數據與類型數據各自的具體地址信息;如果使用直接指針訪問,reference中存儲的直接就是對象地址。
Java垃圾回收采用分代回收機制,新生代和老生代采用不同的算法(node.js也是),而不管什么機制,判斷一個對象是否存活都是基本的步驟,方法有:引用計數法,給對象添加一個引用計數器,每當一個地方引用它時,計數器就加1,引用失效時,計數器就減1,任何時刻計數器為0的對象就是不可能再被使用的,這個方法實現簡單,但是不能解決循環引用問題,所以主流JVM不使用,但是這個算法也適用于許多地方如Python,微軟的COM;可達性分析,按照對象之間的引用關系維護一個引用鏈,如果一個對象不可達GC Roots,那么就是可回收的,應用于主流JVM中。
finalize是一種迎合C++程序員的妥協,運行代價高昂,不確定性大,無法保證各個對象的調用順序。有些教材中描述它適合做“關閉外部資源”之類的工作,這完全是對這個方法用途的一種自我安慰。finalize能做的所有工作,使用try-finally或者其他方式都可以做得更好、更及時,所以建議大家完全可以忘掉Java語言中有這個方法的存在。
幾乎各種語言或多或少都提供過一些語法糖來方便程序員的代碼開發,這些語法糖雖然不會提供實質性的功能改進,但是它們或能提高效率,或能提升語法的嚴謹性,或能減少編碼出錯的機會。不過也有一種觀點認為語法糖并不一定都是有益的,大量添加和使用“含糖”的語法,容易讓程序員產生依賴,無法看清語法糖的糖衣背后代碼的真實面目。所以我們既要會使用語法糖,同時也要搞懂背后的原理,這樣才能進階呀。比如泛型技術:泛型只在程序源碼中存在,在編譯后的字節碼文件中,就已經替換為原來的原生類型。
Java程序最初是通過解釋器(Interpreter)進行解釋執行的,當虛擬機發現某個方法或代碼塊的運行特別頻繁時,就會把這些代碼認定“熱點代碼”(Hot Spot Code)。 為了提高熱點代碼的執行效率,在運行時,虛擬機將會把這些代碼編譯成與本地平臺相關的機器碼,并進行各種層次的優化,完成這個任務的編譯器稱為即時編譯器(Just In Time Compiler,簡稱JIT編譯器)
基于高速緩存(Cache)的存儲交互很好地解決了處理器與內存的速度矛盾,但是也為計算機系統帶來更高的復雜度,因為它引入了一個新的問題:緩存一致性;除了增加高速緩存之外,為了使得處理器內部的運算單元能盡量被充分利用,處理器可能會對輸入代碼進行亂序執行(Out-Of-Order Execution)優化,類似的JVM也有指令重排(Instruction Reorder)。這兩點是提高程序運行的主要方法,然而也是多線程程序難以正確編寫的主要原因。
先行發生(happens-before)是Java內存模型中定義的兩項操作之間的偏序關系,如果說操作A先行發生于操作B,其實就是說在發生操作B之前,操作A產生的影響能被操作B觀察到,“影響”包括修改了內存中共享變量的值、發送了消息、調用了方法等。時間先后順序與先行發生原則之間基本沒有太大的關系,所以我們衡量并發安全問題的時候不要受到時間順序的干擾,一切必須以先行發生原則為準。
這本書是基于jdk1.7的,如果要追蹤最新的Java和JVM規范,可以看這里。
另外,上面提到的第一個問題可以參閱Java核心技術,第二個問題可以參閱Java編程思想、Effective Java,第三個問題還可以參閱HotSpot實戰。這幾本書我都瀏覽過,部分有細讀,都是相當經典的。
高性能MySQL (豆瓣) https://book.douban.com/subje...
這本書可謂是MySQL領域的權威之作,從架構到測試,從性能分析到查詢優化,從軟件配置優化到服務器硬件優化,從單實例到主從復制、負載均衡,從底層數據庫優化到應用層優化......本書真可謂是面面俱到,同時又很有深度,絕非淺嘗輒止。
本書亮點:
所謂的鎖策略,就是在鎖的開銷和數據的安全性之間尋求平衡,這種平衡自然也影響到性能。這句話適用于世間的一切工具,安全性和可用性總是矛盾的,我們在使用工具或者開發工具的時候,都要尋求一個最佳平衡點。
除非需要使用InnoDB不具備的的特性,并且沒有其他辦法可以替代,則都應該優先使用InnoDB引擎,也不要多引擎混用,例如全文索引可以使用InnoDB+Sphinx,而不要使用MyISAM引擎,因為InnoDB其他方面的優點可以完全碾壓MyISAM,比如崩潰恢復快,支持事務,支持行級鎖,支持真正的熱備份等等。
基準測試可以驗證對于系統的假設,檢查異常行為,找出擴展性瓶頸等等。需要注意的是不要使用真實數據的子集,作物的數據分布,忽略預熱等等,這些錯誤的操作會使得測試結果無用或者不精確。而且要建立參數與結果文檔化的規范,這樣才利于結果分析與優化。
很多人在優化時都將精力放在修改某些東西上,卻很少去測量;正確的做法是要盡量測量響應花的時間在哪,正確的測量一般都能將性能問題的點暴露出來,我們就能更好的對癥下藥,而不是盲目優化(花1000塊優化一個只值500塊的業務,或者已經處于頂點的業務不就是虧了嗎)。所以說,決策要基于數據而不是感覺。
良好的邏輯設計和物理設計是高性能的基石,某些反范式的設計可能加快某些查詢,比如計數表和匯總表是一種很好的查詢優化方式,能提高統計類的查詢速度,但是維護起來就比較麻煩,可能降低數據插入速度。這些都需要自己根據業務來進行權衡(比如讀寫比),有陰就有陽嘛。
選擇能正確存儲數據的最小類型:既省空間又省計算時間,簡單就好:比如使用MySQL內建時間戳而不是自己使用字符串,盡量避免null:因為null使得索引、統計、值比較都更加復雜還可能會占用更多空間。
InnoDB有一個“自適應哈希索引”的功能,當引擎注意到某些索引值被頻繁使用時就會在內存中基于B+Tree索引之上再創建一個hash索引,這樣就讓B+Tree也具有hash索引的優點比如快速查找。
小表通常全表掃描更高效,中大型表才適合用索引,使用索引過程中要注意,索引列必須多帶帶的出現在比較符號的右側而不是表達式的一部分(這會使索引失效),使用前綴索引來節省空間提高檢索效率,多列索引要注意順序不然容易失效,聚簇索引可以提高訪問速度。
慢查詢優化:只向數據庫請求需要的列(比如不要隨意select * )、避免不必要的行掃描、必要的時候分解查詢(拆分大的查詢,分解關聯查詢)。
默認配置文件是經過大量測試,所以屬于較優解,一般符合普通用戶,要修改也主要是根據業務而不是服務器配置;任何打算長期使用的配置都應該寫到全局配置文件而不是在命令行指定,因為如果偶然啟動忘了(事實是經常會忘,好記性不如爛筆頭是個真理)設置就會有風險。
MySQL復制功能不僅有利于構建高性能應用,同時也是高可用性(負載均衡、故障切換)、可擴展性(升級)、災難恢復、備份以及數據倉庫等工作的基礎。
數據如果非常龐大,比如幾億行了,單臺機器已經撐不住了,通常要采取分片技術,分片最大的問題就是查詢與獲取數據,我們的目標是對最重要且頻繁查詢的數據減少分片(熱點數據通常就那么多)。所以分片關鍵問題就在于選擇一個好的分區鍵,通常是一個數據庫中非常重要的實體的主鍵。
不僅要關注MySQL,還要關注應用層優化:Apache服務器處理靜態文件都可能使用一個占用內存很大的進程(上一個請求處理完后,該進程仍然保持著),所以最好使用Nginx或者Lighttpd來處理靜態內容服務,而且靜態文件名不要重用,要加上版本號,這樣就能避免瀏覽器緩存問題;主動緩存如Squid,被動緩存如Memcached,都可以對性能獲得數量級的提升,關鍵就在于找到正確的粒度和緩存過期策略組合,通常主動緩存更好,因為對應用層隱藏了檢查-生成-存儲這個過程;
......
亮點太多,列不完了,需要大家自己去尋找。另外,這本書不適宜一次性的精讀完畢(太厚,內容太多),可以快速瀏覽一遍,大概了解,以后遇上問題就可以把這本書當作一本問題解答手冊來查詢解決方案,或者找找靈感。
Redis實戰Redis實戰 (豆瓣) https://book.douban.com/subje...
本書對redis的介紹是相當全面了,從基本用法講起,然后講了許多應用場景,包括購物車、數據庫緩存等,然后講了一些常見問題的解決辦法,比如內存占用過高,自定義擴展來豐富redis的用法等等,看完一本書過后就能很好的使用redis了(還有本好書:redis設計與實現,我大致瀏覽了一下,這本書主要講了redis的實現原理,這兩本書加起來就能既懂原理又會使用,把redis搞個透徹)。
本書亮點:
使用冒號 : 或者管道號 | 等來實現命名空間的作用,比如一個名為 article:12222 的hash存了這篇article的title,link,time等屬性,article:12223又是另一篇文章,這個可以部分實現數據庫檢索的功能。
為了減少redis與客戶端之間的通信次數,可以用multi和exec來做事務處理,事務會一次性的把一批命令一次發給redis,提高吞吐率;另外事務還能保證一批操作的原子性。在node-redis實現中,如果只是想提高吞吐率則可以用batch替代multi。
通過復制(主從)和AOF能夠增強redis抵抗系統崩潰損失數據的能力,AOF如果用得不好的話,要么損失很多數據,要么嚴重降低吞吐量,比較合適的做法是appendfsync everysec 亦即把每一秒的命令一次同步進文件。
使用 redis 的 setnx 來實現基本上正確的的分布式鎖,再加上expire可以實現具有超時功能的鎖,保證即使獲得鎖的客戶端崩潰沒有主動釋放鎖時,其他進程也有機會獲得鎖。
用 list 來替代 subscribe、publish 實現更可靠的發布訂閱系統;另外,利用subscribe、publish加list來實現具有離線緩存的消息隊列系統,保證即使發生連接故障也能把消息送達。
利用反向索引以及 redis 集合的并、交、差功能可以實現簡易的搜索引擎,利用 hash 結構的 sort 功能還能對搜索結果進行簡易的排序功能,利用有序集合 zset 能實現更高級的排序功能。
社交網站通常是用時間線這一數據結構來實現新鮮事瀏覽這一功能,雖然導致了大量的冗余信息,但是能夠節省查詢時間,這是典型的以空間換時間的操作,我以前做社交APP后臺的時候直接把所有新鮮事直接放在一張表中,然后按用戶id查詢,這樣雖然省了空間,但是如果用戶劇增((⊙﹏⊙)b,我們的APP并沒有用戶劇增)就不行了,一張表幾億行還怎么查。
使用短結構來節約內存,使用較短的鍵名節約內存,分片降低單實例的內存占用。
使用lua腳本,在不編寫C代碼的情況下,為redis添加新的功能。
時效性問題,本書沒有一些最新特性 比如 geohash 解決了地理坐標問題,鍵的異步釋放使得我們可以放心刪除而不必擔心大量數據的刪除使得redis短暫不可用。總之,要更好的利用redis還是要追蹤redis的最新變化,以便更簡潔、更靠譜的解決問題。
深入剖析Nginx深入剖析Nginx (豆瓣) https://book.douban.com/subje...
本書從源碼入手,依次講解了進程模型、模塊、響應處理機制、過濾、負載均衡等相關原理,極大的滿足了我的好奇心,因為之前一直就對nginx高并發處理能力有一丟丟了解(比如nginx采用事件驅動機制而非apache的進程、線程每請求方式),自己也用過nginx,但是對他的原理還不是特別的明白,本書算是填補了我的這個空白。
本書亮點:
Nginx 將職責分為監控進程(主進程)和工作進程(主進程fork的子進程),監控進程與用戶交互并對工作進程進行監控管理,工作進程完成具體業務邏輯,兩者都有一個無限的for循環,這是服務進程的基本寫法。
Nginx僅提供針對大塊內存的回收不提供小塊內存的回收,這是因為web server的特殊性亦即階段和時效,請求就申請內存,處理完畢就釋放內存,所以不會存在nginx長時間占據大量無用內存的情況,那么小內存也自然不必急于回收,而是成為大內存后在回收。
對于客戶端的請求,nginx將整個過程分為11個階段,每個階段有數個回調函數進行專門的處理,每個階段的處理功能都比較單一,達到高內聚低耦合的目的。
Nginx是以事件為驅動的,也就是說Nginx內部流程的向前推進基本都是靠各種事件的觸發來驅動,內部事件主要有兩類:IO事件與定時器事件。其中IO事件主要靠epoll,epoll主要優點是監控數目不受文件描述符限制、事件響應是觸發式的,不需要遍歷描述符(select需要)。
Nginx要處理動態的內容一般需要轉發給后端服務器,常見的搭配是nginx+fastcgi+php,nginx把http請求轉化為fastcgi協議的數據后轉發給PHP引擎,PHP引擎處理結果后把數據返回給nginx,nginx把數據轉化為http格式返回給客戶端。
負載均衡有多重含義(或者說多重級別),可以是進程上的(根據master進程根據子進程壓力調整其獲取監聽套接口的幾率),更廣的意義上是指反向代理上,亦即nginx把請求均衡的轉發給后端服務器如PHP引擎,發揮多個單元的整體效能。一般采取加權輪詢、IP hash等策略,但是只靠nginx是不能實現完整的負載均衡的,詳見我以前寫的一篇文章。
這本書主要從應用及其原理方面來介紹nginx,對于后端程序員應該是夠用了(我也忽略了許多源碼,因為只是想了解一下原理)。但是對于要想自己深入、進行模塊編寫的讀者應該還不夠用,可以再參考一下這本書 深入理解Nginx(第2版)(我大概瀏覽了一下,本書會指導讀者編寫具體的模塊及其底層原理,比我們今天介紹的書更深入一些)。
第一本Docker書第一本Docker書 修訂版 (豆瓣) https://book.douban.com/subje...
書如其名,這就是真正的第一本docker書。docker是什么、怎么安裝、如何使用、測試集成、構建服務等都有介紹。之前就久仰docker大名,也試著試用了一下,但是直到這本書讀完我才對docker有了一個完整的認知。另外,不出意料,本書的推薦序也很精彩。
本書亮點:
docker的核心價值在于可能改變軟件的交付方式和運行方式。傳統的交付方式下,軟件運行期依賴的環境是無法控制,不能標準化的,開發人員常常需要解決開發環境和生產環境的差別帶來的問題,而docker則把軟件及其依賴環境打包在一起,以鏡像形式交付,讓軟件運行在標準環境中,非常符合云計算的需求,同時docker的輕量虛擬化技術也符合實例水平擴展,資源動態調整的要求。
docker提供以下幾個好處:簡單輕量的建模方式,工程容易docker化,隨時修改代碼,運行快速;職責分離,開發人員只管開發,運維人員只管容器管理,減少環境不同帶來的問題;快速高效的開發生命周期,程序容易部署、移植和協作;容易實現面向服務的架構和微服務架構。
docker只支持64位架構,原生的Linux容器格式:libcontainer,使用命名空間來隔離文件系統(每個容器都有自己的root系統)、進程(每個容器都運行在自己的進程環境中)和網絡(容器間虛擬網絡地址和IP地址都是分開的),使用cgroups將CPU內存之類的資源獨立分配給容器,寫時復制使得文件系統分層隔離、速度更快、占用空間更小,還提供日志和交互式shell。
鏡像分層:新鏡像是從 base 鏡像一層一層疊加生成的,文件系統發生變化時,就在現有鏡像的基礎上增加一層,這一層叫做“容器層”(讀寫層),“容器層”之下的都叫“鏡像層”(只讀層),只有當需要修改時才從鏡像層復制一份數據到容器層,這種特性被稱作寫時復制(Copy-on-Write),達到了鏡像共享,快速構建的目的。
可以使用docker commit來構建鏡像,也可以基于dockerfile和docker build命令構建,通常建議使用后者,因為dockerfile更具備透明性(可以清晰地看出安裝了什么軟件,修改了什么配置)、可重復性(一次編寫,多次使用。此外構建緩存還可以制作構建模板)、冪等性(同一個dockerfile不論執行多少次,結果都是相同的)。
docker容器之間通訊有3種方式,1.9之前推薦用Docker Link(安全:只有link之前的容器可以通信,不必硬編碼,不支持多主機),1.9之后推薦Docker Networking(支持多主機容器連接,可以熱更新容器,Networking網絡內部容器可以自主發現),不太推薦docker內部網絡(IP硬編碼等導致該方法不夠靈活)。
volume(卷)具有一些有用的特性:容器之間共享數據,對卷的修改會直接反映在包含改卷的容器里所以可以在不修改容器的情況下向容器里加入、更新、刪除數據,更新鏡像時不會影響卷。利用這些特性可以更好的進行數據共享與持久化。
docker編排與集群化之路:Docker Compose 是用來做 docker 的多容器控制,使用Compose ,你可以在一個文件中定義多個容器應用,然后使用一條命令來啟動你的所有應用,避免繁復操作,docker 自動化構建容器棧;Consul提供了一個易于使用,基于開放標準的服務發現解決方案,服務發現允許某個組件在想與其他組件通訊時自動找到對方;Docker Swarm是一個用于創建Docker主機(運行Docker守護進程的服務器)集群的工具,使用Swarm操作集群,會使用戶感覺就像是在一臺主機上進行操作亦即將容器抽象到集群級別。還有其他很多工具如:fleet、etcd、Kubernetes、Apache Mesos、Helios、Centurion。
時效性原因,書中一些例子已經過時(如Docker1.12開始內置編排機制,Docker1.13正式支持docker stack),需要結合最新版本來使用。
另外,想要深入理解docker可以閱讀這本書 Docker——容器與容器云。我大致瀏覽了下,這本書不僅講了docker如何使用,還深入講解了docker的核心原理如namespace資源隔離、cgroups資源限制、libcontainer原理和一些高級實踐技巧。此外,還講了對容器、容器云的思考,包括如何構建自己的容器云,以及Kubernetes實現一切皆容器的“大同理想”。
UNIX/Linux 系統管理技術手冊 (豆瓣) https://book.douban.com/subje...
又是一本進千頁的大部頭,但是不怕,這本書如其名,是一本手冊性質的書,非常大而全,包括基本管理技術、網絡管理技術和其他補充管理技術,幾乎包攬了所有我們可能用到的功能(小到一行代碼整么寫,大到數據中心怎么建),對于宏觀把握整個Linux生態系統有很大作用。我的應對策略是跳躍式閱讀(不錯,就像上面那本MySQL),留下整體映像,等遇上問題時再來具體的查詢相關部分的內容。也就是說,大腦相當于內存,書本相當于硬盤數據庫,我們首次閱讀就是在內存中建立索引,便于提升以后的查找速度:-D。
本書亮點:
Linux各個發行版其實并沒有那么巨大的差別,我們選擇一個發行版時主要考慮幾點:是否能長期存在,是否會有持續的安全補丁,是否會持續更新軟件,發行商是否會在出了問題時幫我們解決,不同發行版側重點會有所不同我們要根據自己的業務來進行選擇。
編寫腳本時注意形成一種指導風格,這樣你和你的團隊成員可以按照相同的規范來書寫代碼,有了這種指導在閱讀別人寫的代碼或者別人閱讀你的代碼時都會更容易;注釋不要多也不要少,最好的效果是一兩個月后再來讀代碼發現注釋和有用。
一個進程由一個地址空間(一組內存頁面)和內核一部分數據(有關進程的信息如地址空間映射、狀態、優先級、資源)組成;一個線程是在進程內執行fork的結果,繼承了包含它的進程的許多屬性,多個線程可以共享該進程內數據,并行(多核)或并發(單核,模擬并行)執行
Unix家族的文件系統目前沒有一個標準,我們盡量按照如下標準來組織。bin:核心操作系統命令;sbin:系統最小規模運行所需命令;boot:內核及加載內核所需軟件;etc:關鍵啟動文件及配置文件;usr:次要的命令文件;var:隨主機變化的文件如日志,數據文件;mnt:可移動介質臨時掛載點;opt:可選的應用軟件包;proc:正在運行的進程信息;tmp:臨時文件;
合適的備份計劃取決于:文件系統的活躍性,轉儲設備的容量,用戶期望的冗余度,想要購買的備份介質數量。
版本控制與多人合作:svn是集中式的,一臺中央服務器充當了一個項目的權威庫;git是分布式的,沒有中央庫,每個用戶都含有一個完整的項目,采用的是拷貝-分支策略。
信息安全領域的基本思想——CIA原則:Confidentiality(機密性),Integrity(完整性),Availability(可用性)。在設計、實現或者維護系統的時候,需要考慮CIA安全三原則,正如老話所說“安全性是一個過程”。
負載均衡既提高了性能又增加了冗余性,包括幾種方式:循環域名服務(也就是DNS輪詢)、負載均衡硬件(比如Big-IP Controller、Content Services Switches等)、軟件負載均衡(比如Nginx)。
Squid既是一個高速緩存軟件也是一個代理服務程序,代理服務很有用,但是Squid真正厲害之處是其高速緩存,它甚至能夠形成一個緩存層次結構,以最大化提高緩存命中率。Squid是有意義的,因為用戶對web的探索具有趨同性,所以在適度的規模上會出現相當多重復請求,運行高速緩存可以節省帶寬和計算資源。
虛擬化技術讓多個彼此獨立的操作系統同時運行在相同的物理硬件上,系統管理員把每個虛擬機當做一臺獨立的服務器,既滿足了軟件廠商的要求,又降低了單一服務的成本。包括全虛擬化(如VMwareESX)、半虛擬化(Xen)、操作系統級別虛擬化(如workload partition)。除了傳統的虛擬化技術,近年來的云計算也是一種(或者說類)虛擬化技術,它把計算能力作為對外提供的服務類似于水電等基礎設施,直接使得硬件層對開發人員和系統管理員透明,提高了效率。
分析性能問題步驟:明確表述問題、收集證據并分類、批判性的評價數據、用語言和圖示總結證據、形成一份總結說明。這一套其實不止適用于性能分析問題,也適用于大多數其他問題,比如架構、重構、debug等等。
系統管理不是一種行為藝術。無論做的什么,都應該能重復完成,切前后一致。通常意味著最底層的變化應該有腳本或者配置程序來做,而不是管理員來做。配置上的變化應該體現在系統管理軟件的配置文件里。說白了,就是文檔的重要性,在我看來:首先文檔就是一家公司的財富,沒有文檔,人走了,那么之前公司的積累也就沒了,又得重頭來,損失很大;其次,文檔是一種保證,大家都按約定辦事,保證操作可重復,避免歧義與甩鍋;再其次,文檔能節約時間,雖然寫的時候可能費點事,但是能節省后面大量的人員溝通的時間;最后,文檔保證了系統的完整性,亦即文檔保證了系統的后續修改遵循一致的思路和風格,才不至于系統隨著時間流逝而越來越亂,難以維護與使用。
本書教會了我們怎么使用Linux,這對于我們后端開發人員是足夠受用了。而這本書Linux內核設計與實現(原書第3版)就會告訴我們Linux是如何實現它這么多這么強大的功能的內在原理,精力有限,我只是瀏覽了一下沒有細讀,等以后真的用得上Linux的深入知識時,或者遇上了什么解決不了的問題,我會再來求助于這本書的。
代碼整潔之道代碼整潔之道 (豆瓣) https://book.douban.com/subje...
本書提出一個觀點就是:代碼質量與整潔度成正比,圍繞提高整潔度,作者展開了方方面面的闡述,從命名的方法到函數的定義,從注釋的使用到格式的目的,還介紹了對象,錯誤處理等等等等。看完后的感覺就是:寫好代碼,從本書開始。雖然我現在寫的代碼還比較“亂”,但是今后的代碼中會努力踐行書中的原則,爭取寫出整潔的代碼。
本書亮點:
軟件質量既依賴于架構和項目管理,又與代碼質量緊密相關,而代碼質量與整潔度成正比,所以我們要致力于寫出干凈整潔的代碼。并且不僅要知道書寫整潔代碼的原則,還有查看大量代碼實例,進行案例研究,在實踐中貫徹這些原則,才能真正做到知行合一。
不同的人對整潔代碼有不同的理解,總歸起來,有如下一些特點:代碼邏輯直截了當,依賴關系很少,性能最優,每個函數模塊類專注于一件事,可以輕易的被其他人閱讀,具有完整的測試,作者自己要在乎自己的代碼悉心維護,沒有重復代碼提前構建重復代碼應有的抽象,體現系統的所有設計理念,包含盡量少的實體。
命名看似簡單卻無處不在,所以我們不妨命好名,幾個原則:名副其實,不用注釋也能明白這個變量代表什么;避免誤導,不要用保留詞或太相同的詞引起歧義;做有意義的區分,不要添加在名字后數字或者廢話而是以讀者能鑒別不同之處的方式來區分;名字要讀的出來,便于交流;名稱要便于搜索;一個概念統一用一個詞,比如不要混用manager和Controller;避免雙關詞;給名詞添加語境,但是避免冗余的語境,含義明確的情況下,短名字總是比長名字好呀......
函數構成了當今程序的基石,函數要寫的明白要遵循如下原則:短小;只做一件事,也就是函數中的語句要在同一個抽象層級上,不在同一個抽象層級就得拆出來形成一個新的函數;把switch埋藏在較低的抽象層級,比如抽象工廠中,雖然依然免不了判斷或者條件增多時要增添代碼,但是能夠把變化截留在工廠內,減少影響范圍;不要向函數傳入標志參數,比如TRUE/FALSE,而是應該直接把這個函數重構成兩個函數;一個函數要么下達什么指令(set)要么回答什么問題(get),不要添加副作用;DRY(別重復自己)。
注釋是一種必須的惡,如果代碼寫的好根本不需要注釋(感覺有點過了,至少一個大的模塊用來干啥還是因該用注釋或者說文檔直接來說明),而且代碼才是真實的地方,注釋很可能沒有被維護而導致失效;別給糟糕的代碼加注釋,重寫吧;版本控制系統可以幫我們省掉許多注釋如作者署名,注釋掉的代碼。
或許你認為“讓代碼工作”是開發者的頭等大事,但是其實并不是這樣的,代碼風格關系著溝通,而溝通才是頭等大事。代碼風格影響著可維護性和擴展性,即使代碼已不復存在,其風格和律條依然存在,因而一個團隊應該形成一個統一的風格,便于溝通與維護。
隱藏實現并非就是簡單的在變量之間放一個函數層。隱藏關乎抽象,類并不是簡單的用取值器和賦值器將變量向外推,而是暴露抽象接口,以便用戶無需了解數據的具體實現就能操作數據本身。對象暴露行為,隱藏數據;數據結構暴露數據,沒有明顯的行為。要合理的利用二者。
錯誤處理一旦處理不好就容易污染整潔的代碼:使用異常來保證錯誤處理不會打亂正常的程序邏輯;可檢異常可能會破壞開閉原則,要謹慎使用;打包第三方API就降低了對它的依賴;不要返回null值,只是突然增加了工作量,可以用異常或者特例對象來代替。
測試非常重要:測試代碼和生產代碼一樣重要,臟測試就等于沒測試;有了測試就可以毫無顧慮的進行重構和改善,因為有保證,消除了清理代碼就會破壞代碼的恐懼(這句話可謂是戳中了我,我之前的項目也有過一些重構的努力,但是就是怕重構過后不能正常運行,導致重構舉步維艱);測試代碼要清晰,符合構造-操作-檢驗三個環節;單個測試中的斷言應該最小化,保證一個測試對應一個概念;整潔的測試遵循 FIRST 原則(fast,independent,repeatable,self-validing,timely)。
系統層級的整潔:城市能有效運轉是因為演化出了恰當的抽象層級和模塊,有人負責全局有人負責細節,所以軟件系統也有架構師和項目經理等;將構造和使用分開(依賴注入,控制反轉);擴容(要考慮可擴展性,但是注重現有的系統的構造,將來再重構和添加,沒必要Big Design Up Front,考慮好模塊化和關注切面劃分就好)。
KISS(Keep It Simple, Stupid),簡單設計原則:運行所有測試(測試是對一個系統的保證);不要重復(重復意味著額外的工作、風險、復雜度);表達了程序員的意圖(清晰可讀);盡可能少的類和方法數量(避免教條如:每個類都要有接口)。
編程是一種技藝甚于科學的東西,不要指望一開始就寫出優雅整潔的代碼,一般是先寫出骯臟的代碼,然后進行重構,清理。所以代碼僅僅能工作還不夠,滿足于代碼能工作的程序員不夠專業,他們害怕沒時間進行代碼結構的重新設計,其實沒有什么比糟糕的代碼給項目帶來更長遠的損害了,糟糕的代碼會一直腐敗發酵,影響各個模塊(找出這些依賴和影響相當不容易)無情的腐蝕整個項目和團隊。
......
這本書里面的良心建議實在是太多了,無法一一列舉,真的得自己看一遍才能有收獲。
重構-改善既有代碼的設計重構 (豆瓣) https://book.douban.com/subje...
上一本書教會我們怎樣書寫整潔的代碼,那么面對不整潔的代碼,我們怎么辦呢?這本書就手把手教我們怎么重構,改善現有的代碼。從一個實例入手,講了重構的理由、原則、技巧、步驟與時機。我讀完過后感覺心里就有了些烙印,無論是寫代碼還是改代碼,基本都會不自主的向這本書靠攏,很有收獲。
本書亮點:
重構是在不改變代碼外在行為的前提下對代碼作出修改,以改進程序的內部結構,本質上說重構就是在代碼寫好后改進其設計,提高其可理解性,降低修改成本;記住所有代碼的“壞味道”及其對應的重構手法,才能有信心面對各種情況——學會所有招式才可能“無招勝有招”;為了避免自掘墳墓,重構必須系統化進行,用一些經過檢驗的重構手法,就可以一次一小步的修改代碼,所以任何錯誤都可以比較容易的發現,降低了重構過程中的風險。
如果你發現自己需要為程序添加一個特性,而現有的代碼結構使你無法方便的添加,那就先重構程序,使得特性添加變得容易,然后再添加這個特性。而重構的第一步就是建立一組可靠的測試,來盡量避免bug,保證重構的正確性。
對象A最好不要在另外一個對象B的屬性基礎上使用switch語句,因為將來B變了A也必須變,如果不得不使用switch也要在自己的屬性上使用,也就是把switch移動至B里面去;最好是用多態替換switch語句(其實也不是替換,而是把switch放在了較低的抽象層級,使得可能變化的部分就在一個對象里面,將其與不變的部分隔離開)。
添加新功能和重構是兩種行為,一定要加以區分,并且同一時刻只做好一件事就行。
任何能夠查詢的東西,我都不太愿意去記,因為怕把大腦擠爆了。所以說大腦這個珍貴而有限的內存主要做的事一定是建好索引,而非存儲數據;另一方面,常用的數據也應該存進大腦,以提高效率,這和內存中的緩存是一個道理。
不要為了重構而重構,一定是因為你想做某件事時重構恰好可以幫你做好,重構的幾個時機:添加新特性時,修補錯誤時,復審代碼時。
程序有兩面價值“今天可以為你做什么”和“明天可以為你做什么”,如果只關注今天的工作,那么明天我們將無法工作,所以需要重構,來避免代碼出現以下四個情況:難以閱讀,重復太多,新行為無法簡單添加,邏輯復雜。這四個情況導致的結果都是程序難以改變(修改),而唯一不變的就是改變:-D
代碼的壞味道——重構的時機:重復代碼、過長函數、過大的類、過長的參數列、發散式變化、霰彈式修改、依戀情結、數據泥團、基本類型偏執、switch語句、平行繼承體系、冗贅類、夸夸其談未來性、臨時字段、過渡耦合的消息鏈、中間人、不適合的親昵關系、異曲同工的類......
本書給出了一份重構列表,包含了很多很多的重構類型的名稱、動機、方法、范例,涵蓋了函數、對象、數據、表達式、調用、概括關系、大型項目等方方面面,值得我們借鑒,下面摘抄幾條作為示例。
當我看見一個過長的函數或者一段需要注釋才能讓人理解用途的代碼就會把這段代碼放進一個獨立的函數中。短函數的好處:更可能被復用,讓高層函數看起來更清晰易懂,復寫更容易。“短”的含義不在代碼行數,而在于函數名稱和函數本體之間的語義距離。
如果一個類的某個方法、字段被另一個類頻繁使用,就應該搬移該方法、字段。
混亂的繼承體系是一個嚴重的問題,因為它會導致重復的代碼,這正是程序員職業生涯的致命毒藥。它還會使修改變得困難,因為特定問題的解決策略被分散到了整個繼承體系,最終你的代碼難以理解。所以可以通過建立多個繼承體系,并利用委托來互相調用,使得原來負責多個任務的繼承體系變成多個負責單個任務的繼承體系。
......
感覺本書最大的問題就是太老了(1999年),有些工具或者方法在如今技術進步的情況下顯得有些多余,比如現在的IDE如Eclipse或者IDEA都有很強大的功能,書里提到的一些技巧完全用不著。不過經典終究是經典,里面的絕大部分思想我們如今都還是可以借鑒的,尤其是對于重復代碼的觀點——應該堅決消滅重復。
2017.9.8 后記花費了幾個月來看書,又花了幾天來整理,希望對我們都有所幫助:-D。歡迎拍磚,我的主頁Mageek`s Wonderland。
查看原文
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/70368.html
摘要:續前文后端好書閱讀與推薦,幾十天過去了,又看了兩本好書還有以前看過的書,這里依然把它們總結歸納一下,加入一些自己的看法有用的鏈接和可能的延伸閱讀,并推薦給需要的同學。 續前文 后端好書閱讀與推薦 - Mageek`s Wonderland ,幾十天過去了,又看了兩本好書(還有以前看過的書),這里依然把它們總結歸納一下,加入一些自己的看法、有用的鏈接和可能的延伸閱讀,并推薦給需要的同學。...
摘要:可以通過大數據生態的一系列工具生態來解決大數據問題數據分片主要有兩種方式哈希和范圍。哈希的問題是范圍查詢支持不佳,范圍的問題是可能冷熱數據不均。 后端好書閱讀與推薦系列文章:后端好書閱讀與推薦后端好書閱讀與推薦(續)后端好書閱讀與推薦(續二)后端好書閱讀與推薦(續三)后端好書閱讀與推薦(續四)后端好書閱讀與推薦(續五)后端好書閱讀與推薦(續六) Elasticsearch權威指南 El...
摘要:可以通過大數據生態的一系列工具生態來解決大數據問題數據分片主要有兩種方式哈希和范圍。哈希的問題是范圍查詢支持不佳,范圍的問題是可能冷熱數據不均。 后端好書閱讀與推薦系列文章:后端好書閱讀與推薦后端好書閱讀與推薦(續)后端好書閱讀與推薦(續二)后端好書閱讀與推薦(續三)后端好書閱讀與推薦(續四)后端好書閱讀與推薦(續五)后端好書閱讀與推薦(續六) Elasticsearch權威指南 El...
閱讀 2872·2021-11-11 10:58
閱讀 1926·2021-10-11 10:59
閱讀 3497·2019-08-29 16:23
閱讀 2343·2019-08-29 11:11
閱讀 2791·2019-08-28 17:59
閱讀 3841·2019-08-27 10:56
閱讀 2077·2019-08-23 18:37
閱讀 3119·2019-08-23 16:53