DevOps之旅:運維人員閱讀原始碼的實用技巧(以OpenStack為例)
作者簡介
陳晨
基礎架構工程師,目前就職於中國銀聯。主要負責IaaS平臺、容器平臺以及運維管理平臺的建設工作。
在昨天的文章中,我們初步講解了一些關於學習原始碼的基礎知識,今天我們著重介紹一下學習原始碼的一些技巧。原文連結如下:
一、準備階段
1. 制定計劃
讀原始碼和讀書一樣,必須有時間計劃,deadline是第一生產力。
合理的制定計劃可能需要你先全域性掌握一下程式碼的結構,以及各個函式的重要程度、難易程度。
2. 選擇一本好書
作為學習資料,書一定是最好的。
- 網上資料太零散,學習起來可能不繫統;
- 較為系統的書一般都會將開原始碼的配置、整合進行詳細的講解;然後會介紹一些通用模組,最後再對每一個元件或者流程進行程式碼的跟蹤分析。
在選書的時候,也應該注意選擇。
- 可以通過書的各個章節介紹,來看該書是否按照這樣的邏輯去講述。
例如,筆者當初學習openstack的時候,根據目錄選擇了這樣一本書:他的四個篇幅分別是基礎-安裝-程式碼-二次開發。這就是一個非常好的循序漸進的書。
3. 選擇一個好的IDE
筆者除了Java以外,全部使用Vim。當然,這完全取決於每一個人的習慣。
筆者一般比較喜歡輕量級的IDE,因此推薦一些輕量級的:
- Vim
- Sublime
- SourceInsight
IDE的全稱是整合開發環境。如果只是要將程式碼執行起來,只需要編譯器或者直譯器。程式碼完全可以在純文字上進行編輯。
IDE提供更多的開發輔助功能,使得開發人員專注於程式碼的邏輯。
最常見的如自動糾錯、程式碼補全、函式查詢等功能。C的很多IDE還自動生成makefile,也省去很大的繁瑣內容。
不過,IDE的功能性和簡約型永遠是一個悖論。
讀者在選擇IDE的時候,應當選擇一個符合自己需求的IDE,不要過分追求功能強大。
一般來說,我們使用IDE可能有哪些輔助性的需求呢:
- 測試工具的整合
- 自動打包
- 程式碼定位
- 定製化、外掛豐富
- 錯誤檢查
- 除錯
- 專案模板
4. 下載完整版本庫
完整的程式碼庫是指反映程式碼迭代過程的各個歷史版本。這樣做有很多好處:
- 你可以獲取程式碼的修改記錄。
- 你還可以獲取到完整的測試程式碼,當你要提交patch的時候,你可以藉助版本管理工具生成針對不同版本的patch。
二、初識程式碼
1. 閱讀專案文件
大部分的開源專案都會對其架構有一定的描述,通讀一下會讓你專案有一個比較深入的認識。
重點關注類似Getting started、Example之類的文件,從中學習如何下載、安裝、使用該專案所需要的知識。
比如openstack,官網上的網路拓撲講解是最全面、準確的:
2. 分類檔案
分清楚程式碼庫的各個檔案的作用。
在恰當的時候,對所有檔案做一個總體把握,有助於後續閱讀程式碼的時候的優先順序的選擇。清楚哪些是核心、哪些是可以定製的。
如下是筆者收藏的nova的原始碼檔案的部分內容:
/nova/api/auth.py:通用身份驗證的中介軟體,訪問keystone; /nova/api/manager.py:Metadata管理初始化; /nova/api/ec2/__init__.py:Amazon EC2 API繫結,路由EC2請求的起點; /nova/api/ec2/apirequest.py:APIRequest類; /nova/api/metadata/__init__.py:Nova元資料服務; 。。。 。。。
剛開始寫註釋的時候,其實有的東西自己也不是很確定。這樣的註釋也沒有最終能讓你對程式碼的所有檔案的關係有非常清晰的瞭解。
不過不要緊,在初期的時候就嘗試去做這樣的事情是有好處的,這可能是你掌握原始碼整體結構的第一步。
3. 掌握開發框架
框架存在的目的就是簡化開發。但是也會讓程式碼不那麼直觀。
舉個例子,很多用spring開發的開源軟體,如果你連spring都不懂,你就會發現連程式碼入口都找不到。因為在開發框架下的程式碼都被“劫持”啦。
我們舉個例子,spring+springmvc+mybatis開發web應用的時候。如果理解了這三個基礎框架,你就可以很清楚的知道如下檔案的作用:
- 所有的url對應的controller都在com.dc.controller中
- 所有的資料介面都在com.dc.dao中
- 所有的實體物件都在com.dc.entity中
- 所有資料介面和sql語句對應關係都在com.dc.dao.mapper中
- 所有的服務定義都在com.dc.service和com.dc.service.Impl中
更具體的,當我看到這樣一個函式:
馬上就知道是spring中的一個處理url路徑時/的controller函式。
所以,如果確信開原始碼使用了成熟的開發框架,請一定先熟悉該框架。
三、熟悉程式碼行為
1. 元件執行流程
較為複雜的系統都是分元件的,分別熟悉各個元件,理清他們之間的關係。
例如,openstack的執行流程圖:
虛擬機器啟動過程如下:
a. 介面或命令列通過RESTful API向keystone獲取認證資訊。
b. keystone通過使用者請求認證資訊,並生成auth-token返回給對應的認證請求。
c. 介面或命令列通過RESTful API向nova-api傳送一個boot instance的請求(攜帶auth-token)。
d. nova-api接受請求後向keystone傳送認證請求,檢視token是否為有效使用者和token。
2. 利用示例程式碼和單元測試
示例程式碼可以幫助你學會使用相關開源專案的API。
大部分的開源專案在開發的過程中,為了驗證其實現的功能,都會寫很多單元測試程式碼。這些程式碼其實是非常好的示例程式碼。
讀單元測試的好處太多了,這裡給大家羅列一下知乎網友總結出來的好處:
- 由於一個單元測試一般也就是幾個小時的開發工作量,你很容易就能讀懂相關的程式碼。
- 每個單元測試都是可以獨立執行的,這樣節省你跟蹤除錯的時間。
- 單元測試在很大程度定義了軟體的功能,可以幫助你快速掌握專案的相關API。
- 如果你修改的開源專案的程式碼,你可以通過修改單元測試來驗證你的修改是否正確。
注1:原文連結
如果該專案有提供現成的example工程:
- 首先嚐試按照開始文件的介紹執行example,如果執行順利,那麼恭喜你順利開了個好頭;如果遇到問題,首先嚐試在專案的FAQ等文件裡查詢答案。
- 再次,可以將問題(例如異常資訊)當成關鍵詞去搜索,查詢相關的解決辦法,你遇到了,別人一般也會遇到,熱心的朋友會記錄下解決的過程。
- 最後,可以將問題提交到專案的郵件列表,請大家幫你看看。在沒有成功執行example之前,不要嘗試修改example。
運行了第一個example之後,嘗試根據你的理解和需要修改example,測試高階功能等。
3. 跟蹤分析
複雜的開源軟體幾乎沒有一個是一個流程走到底的,這個時候就需要我們選擇一個主要流程。
例如,在openstack中,筆者一開始就畫了大量功夫去梳理建立虛擬機器的流程。
第一步,從程式碼入口處沿著建立虛擬機器這條流程進行一行一行的註釋:
當該流程基本註釋完成,自己也有所掌握後,抽絲剝繭,總結出更為直觀、簡介的表現方式:
當你逐漸理解了一個或者兩個主要流程後,一般會發現其他的分支流程都十分類似。這就為掌握整個流程打下了很好的基礎。
4. 對需要詳細瞭解的函式進行排序
在安排自己深入閱讀時,應該根據預估的工作量進行合理安排。
一般來說,初始化、讀取引數等都是次要的,也是相對簡單的。而核心模組就複雜的多。
還是以haproxy為例,main函式中最核心的程式碼就是run_poll_loop()。
筆者曾經嘗試從init()函式開始,但是發現很多初始化的資料壓根就不知道幹什麼,看過一遍後就都什麼都記不得了。
但是直接從主函式開始,不斷的發現對一些引數進行處理的時候,反向追蹤他的初始化過程,則更容易理解。
四、掌握資料狀態
1. 掌握資料流
從資料流的角度來講,所有的程式碼邏輯都是在加工資料。
比如說openstack,從最初使用者輸入的虛擬機器名稱、配置等資料開始,openstack的程式碼邏輯對資料進行加工、處理、過濾、選擇等內容,最終傳遞給libvirt,進行虛擬機器的最後建立。
因此,掌握資料的組織方式,對理解程式碼邏輯是很有幫助的,也是二次開發的前提條件。
所以,在學習程式碼的時候,不斷詢問自己,我掌握資料的組織方式了嗎?我掌握資料在整個流程中不斷加工的流程了嗎?
2. 使用debug觀察資料狀態
前面說到細化研究某一個流程的時候,一定要注意研究資料傳遞的方式和內容。資料流是理解流程的基礎,擴充套件資料流也是二次開發常用的技能。
例如,在eclipse通過debug打斷點,獲取流程中某個點的資料內容:
3. 使用標準輸出觀察資料狀態
筆者有時候也喜歡直接使用console進行輸出,列印物件的一些資訊。這個用於驗證某段程式碼有沒有被執行、或者檢視某個資料的時候,也十分有效。
例如,通過chrome的標準輸出檢視javascript的輸出:
五、舉一反三階段
1. 研究底層呼叫
研究底層呼叫往往是運維的常用手段。比如在openstack的執行初期,我們對openstack的原始碼不熟悉,怎麼辦呢?
直接研究openstack的底層呼叫。Openstack底層都是呼叫libvirt的介面,建立虛擬機器等基本操作我們都研究了個遍。
因此,對於大多數openstack的問題,我們都能直擊問題現場,進行恢復和排查。
那研究底層呼叫對理解原始碼有什麼好處呢?
筆者在基本熟悉了openstack的所有底層呼叫之後,帶著這樣的問題去看原始碼“程式碼究竟是如何從入口逐漸執行到我所知道的那個底層呼叫的呢?”,筆者很快就梳理了程式碼的執行流程。
2. 學會在社群或者stackoverflow提問題
社群裡面的熱心人是相當多的。
當然筆者認為,提問也需要一定的技巧,這裡引用知乎網友的話:
stackoverflow很多人問問題有一個共性,就是對提出的問題先發表自己的見解,描述自己的思路,自己達到了什麼地方,這是對各位回答者的尊重。
你在闡述自己所能達到的地步,你表明了你已經做出了什麼樣的努力,這是你對問題的誠意。
這樣回答者才會覺得有回答的價值,或許是想起自己過去也曾經小白卻努力的歲月,或許是覺得你有相助的價值,或者等等。所謂自助者人助罷了。
原文連結:
3. 學會畫流程圖
流程圖可以更方便的展現程式碼執行的邏輯。忽略不重要的程式碼,強調主要的函式。
概要設計中常用的框圖:
思維導圖:
文章來源:高效運維