任何一位工程師都不可能瞭解全部領域的技術知識;任何一個團隊也不可能包含全部類型的專業人才。而一個完整的產品被開發出來,或者一個系統被構建出來,這個過程都會用到種類繁多的技術,通常來講總會有一部分超出當前團隊所能掌握的現有經驗。這個矛盾怎麼解決呢?這就須要工程師來進行技術攻關了。程序員
沒錯,工程師的真正價值就是把未知變成已知的能力。面試
如今假設你的leader交給你一件你歷來沒接觸過的任務,好比,它可能涉及到研究若干框架以及系統架構,優化某些算法,設計和實現某一類型的網絡協議,對音視頻進行處理,研究系統底層,甚至這個過程可能會涉及到一些複雜的數學知識。總之,這項任務對你來講有點複雜,你歷來沒有接觸過,因此徹底沒有概念。算法
你之因此領到這樣一項任務,首先,是由於項目在當前或者將來須要解決這樣的技術問題,而團隊中沒有人有現成的經驗可以應付。另外一方面,你確定是在之前的工做中表現出了過人的學習和研究能力,你的leader纔敢把這個工做交給你。編程
我在上篇文章《馬拉松式學習與技術人員的成長性》中提到過,按技術領域來劃分,編程能夠分爲「通常性」和「專業性」兩大類。做爲一件須要進行技術攻關的任務來講,它有可能會涉及到一些「專業性」的領域了。我相信,每一位執着於技術的工程師,在他成長的過程當中,總會碰到相似的經歷,去挑戰一些本身未知的東西。在這個過程當中,既爲團隊解決了眼前的問題,也爲本身打開了一片新的技術天地。這也是技術小白進階到專業人才的必經之路。c#
今天,我就根據本身的經歷和體驗,說一說從零開始進行技術攻關的一系列過程,以及可能碰到怎樣的一些問題。但願你看完能有共同的感覺。設計模式
有些狀況下,你的leader無法告訴你具體應該作什麼,他只是告訴你問題是怎樣的,好比,視頻播放老是卡頓,或者,用戶老是說丟消息,再好比,用戶反饋說搜東西的時候結果給的不許,打語音或視頻電話老是接不通,老是有些圖片訪問不到,諸如此類。安全
咱們第一步應該作的,就是先研究問題自己是怎麼產生的。首先試圖從用戶的角度去理解問題,而後從技術的角度去了解現有的實現,包括細節。這時候,全局的視角變得很是重要,你若是既能理解服務器的邏輯,也能理解客戶端的邏輯,那麼解決問題的思路會大大開闊。有些「系統性」的問題,屬於設計缺陷,並非作局部改動就能解決的。這種問題,對於只接觸過客戶端編程或只接觸過服務器開發的工程師,解決起來就有必定的難度,由於他們考慮問題的思路容易被見到的東西限制住。因此,適時擴大本身的知識域,永遠都有好處。服務器
若是問題足夠複雜,咱們可能還須要增長跟蹤日誌,便於在出現特殊的問題時可以分析它發生的過程;並定義性能指標,對現有系統的整體情況有一個量化的度量。它們一方面有助於咱們更深刻地理解當前系統,另外一方面也爲後面的優化和重構過程提供了方向。微信
經過對問題自己的研究,咱們知道了系統的瓶頸或問題癥結在哪裏。若是咱們發現只有推翻現有系統徹底重構才能根治問題,那麼接下來就跟從頭開始設計一個全新系統的過程同樣了。網絡
在接觸一個新領域以前,對它進行一個整體的概覽是頗有必要的,這讓咱們對後面整個的精力投入作到心中有數。
這個階段的目標是,花最短的時間快速瞭解相關的各個概念,不求深刻理解,只求瞭解技術概況。因此,這個過程怎麼快就怎麼來,選擇本身熟悉的方式。能夠去你本身喜歡的技術社區搜索相關的文章,或者經過百度搜索一些概念。不少人不建議用百度來搜索技術問題,但瞭解一些概況仍是沒有問題的,不過要適可而止。等到真正須要系統地研究技術細節的時候,仍是應該直接閱讀更規範的資料(後面咱們還會提到)。
在當今的技術條件下,幾乎什麼技術領域的問題都有開源的軟件能夠借鑑。若是針對咱們要解決的問題能找到開源項目,那麼很是幸運,咱們探索的過程會大大縮短。
不少技術領域都存在衆多抽象的概念,經過前面Overview的階段,咱們通常只能瞭解到這一層。而開源項目能幫助咱們快速地將抽象的概念具體化,得到感性的認識。下載一份代碼,編譯經過,而後運行起一個簡單的Demo,從API層面去理解它(內部的實現尚不是重點)。
不少狀況下,開源的實現不止一個,這就面臨一個選擇的問題。人們對於開源項目的第一印象通常來源於項目的入門教程(tutorial),可見一份好的文檔對於一個開源項目來講多麼重要。根據我我的的經驗,文檔是否健全,也是選擇開源項目的重要依據。
固然,在這個階段,咱們要解決的主要還不是選擇開源項目的問題,而是要經過快速Run起一個相關的實例來達到對技術得到感性認識的目的。注意這種感性認識是技術層面的,是至少基於API層面的。咱們常常看到,對於一些熱門的技術領域,不少非技術人員也能略知一二,甚至對一些技術概念有所瞭解,可是,技術人員與非技術人員的區別,應該說,從這個階段開始就有所不一樣了。
可以Run起一個實現,並從API層面粗略地瞭解一項技術以後,在這個認識的基礎上,咱們就差很少能夠找同行交流一下了。若是在咱們正要涉足去研究的領域裏,咱們剛好認識一些這方面的專家,那麼無疑是很是幸運的。邏輯清晰的技術高手,通常用不了幾句話就能把某項技術的關鍵問題描述清楚了。從這種交流中,咱們受益不淺。
但要注意,咱們必定要在對該項技術有所瞭解以後,再去找專業人士交流。不然這種交流創建在信息嚴重不對稱的基礎上,就是極其低效的。對該項技術的初步瞭解,也是讓咱們能問出真正有效的問題的基礎條件。
我曾經寫過一篇關於如何學習新技術的文章《技術的正宗與野路子》,在文中提到的一個重要的觀點,就是必定要找到能稱得上Spec的文檔去閱讀。所謂Spec,是集中體現該項技術的設計思想的東西,是高度抽象的描述,通常也是一份完備的、系統性的描述。它的存在形式有不少種,多是一份官方文檔,也多是一份公開的技術標準,好比RFC或者W3C的規範,還多是以論文的形式,甚至與其它技術資料混雜在一塊兒。
總之,你應該設法識別出哪些文檔是Spec,而後在須要的時候通讀它們。有些涉及到抽象概念的技術,你不讀通這麼一份Spec,有可能後面是看不懂代碼的。這確實是比較費力的一個過程,但也正是這個過程,才真正開啓了從門外漢向技術專家邁進的征程。
假設咱們找到了開源代碼可供參考。前面咱們已經能Run起來一些小的Demo了,而且基本通讀了一份大而全的Spec,如今須要研究的就是再深刻一層,看看這份Spec中的關鍵點是如何實現出來的。你前面已經花了不少時間來調研,這中間確定產生了不少疑問,好比有些抽象的概念以及類似概念之間的聯繫仍是難以理解,有些過程的實現初看起來並非那麼地顯而易見,而如今就到了該解決它們的時候了。頭腦中的疑問和關鍵點,要本身總結出來,而後在代碼中去找到答案,這是把抽象概念最終落地的一個過程。
若是有多個開源的實現,那麼就涉及到如何選擇的問題。有不少因素須要考慮:
有可能咱們要實現的東西其餘家的線上產品已經提供相似的功能了。咱們有必要在實現本身的方案以前研究一下他們的作法(逆向工程),對比以後從而作出一個更優的實現。
具體怎麼研究呢?兩種常見的方式:一種是反解客戶端的包,看看裏面引用了什麼,是否是在咱們調研過的那些技術範圍以內;另外一種固然就是抓包,從網絡通信上猜想他們用了哪一類技術。
通過前面的調研,咱們基本上已經在頭腦中產生了本身的方案了。但在真正實現它以前 ,咱們通常還想作一件事,就是「循證」。
記得胡峯同窗在他的微信公衆號「瞬息之間」上,發過一篇文章《技術乾貨的選擇性問題》,裏面就提到了經過閱讀技術文章來「循證」的作法。不少我的博主和團隊博客會在網上發表他們本身系統的實現過程,以及系統先後版本的演進過程。若是咱們剛好找到相關的相似這樣的文章,那麼它們就有很大的參考價值。咱們從別人分享的技術方案中得到一個印證,確保本身的想法沒有走向極端,或者漏掉了什麼重要的東西。
對於複雜的系統,即便有開源的代碼,一般也不能直接拿來就用。現有系統總有一些特殊性。這涉及到多種選擇的可能性:
總之,這裏的選擇過程是比較痛苦的,由於它對後面的實現工做以及往後的維護影響很大。具體如何選擇,除了要考慮開源實現與當前系統的實際需求之間的匹配程度,還要考慮預期收益和項目預算(budget),你有多少時間去完成整個的事情。
如今進入很關鍵的實現階段了。實際上,對於一個複雜的系統,在真正寫代碼以前,須要首先進行設計,系統架構的設計和軟件接口的設計。這個過程很是重要,花費的時間和精力極可能超過代碼編寫的過程。這個過程逼迫你在真正實現以前就必須想清楚系統在各個層面上是如何運行的,確保不會實現到一半推翻重作。
首先,系統架構的設計,劃分出組成系統的各個組件(各個獨立的進程,經過網絡進行交互)。有兩個問題須要在設計時就重點考慮:一個是可擴展性;一個是容錯性。可擴展性說的是,當流量逐漸變大的時候,你的系統如何擴展。系統中有些組件是無狀態的,有些是有狀態的。無狀態的組件通常經過增長節點,應用簡單的負載均衡策略就能夠擴展;而有狀態的組件則須要明確擴展的方式。容錯性說的是,系統應該主動處理失敗狀況,在設計中就應該考慮進去。你應該認可網絡會丟消息,程序會崩潰,而後在這個基礎上進行系統架構設計。好比,你想作到不丟消息,那麼必須把網絡丟包和處理異常的狀況當作正常狀況來處理,設計重傳機制;而後既然有了重傳,就不得不考慮去重機制。容錯性還包括,系統應該具備從錯誤狀態中恢復的傾向。固然,系統架構的設計還有不少因素須要考慮,好比高可用、高性能、可維護,等等,咱們這裏就不展開說了。
其次,在更細的層面,要完成接口設計,也就是俗稱的「面向接口編程」。這個過程的重要性怎麼強調都不爲過。咱們都知道「面向接口編程」,在面試的時候也常常討論設計模式,但實際中真正按這種方式工做的人少之又少。形成這種情況的緣由多是,咱們日常作業務開發,在大部分狀況下,都不用本身設計接口。好比作客戶端開發,各類MVC, MVP模式已經把代碼框架都定義好了,咱們只用往接口實現裏填東西。
但咱們應該知道,當爲一個新系統編寫代碼的時候,代碼應該從接口設計開始。先用代碼定義出各層的接口(包括回調接口),沒有實現,只是可以編譯經過。有了這些接口,就能夠拿它們與同事進行很是細節的討論了。應該先把接口討論得足夠清楚,再進行下一步的具體實現。這也是一個比較痛苦的過程,咱們須要反覆抉擇,而一般「選擇」就意味着痛苦。根據我我的的經驗,設計接口代碼的過程,通常都要先後改不少遍,才能達到令本身基本滿意的程度。
接口設計的時候,要時刻考慮這兩個問題:
在編寫和修改接口代碼的過程當中,還有幾個問題值得考慮:
最後,就是愉快地實現了。只要前面的過程作得比較細緻,編寫實現代碼基本就是水到渠成了。在實現中有一個很是重要但容易被忽視的問題是——日誌(log)。每一個人都知道怎麼打日誌,但打一份好的日誌,實際沒有幾我的可以作到的。通常來講,若是沒有足夠的重視,工程師打出來的日誌,或者過於隨意,或者邏輯缺失。一份好的日誌其實要花不少精力來調整細節,把程序運行當作一個狀態機,每個關鍵的狀態變化,都要在日誌中記錄。一份好的日誌其實反映了一套好的程序邏輯。總之,打日誌的目標是:若是線上發生奇怪的狀況,拿過這份日誌來就能分析出問題所在。這在客戶端上分析線上問題的時候尤爲有用。
前面講到的各個過程並非要嚴格按照這樣的步驟進行,實際中有些過程要先後交叉,甚至反覆進行。中間碰到問題,可能還要退回到前面的步驟,從新進行。
在時間、精力和項目進度各類條件容許的狀況下,儘可能把事情作到當前認爲「最好」的狀態。固然,若是因爲客觀條件,一時沒有那麼多時間去作到完美,也不要太過沮喪,後面可以持續優化纔是最重要的。
進行一項技術攻關,從這個過程當中學習新的東西,這是一個利用現有各類資源來學習和解決問題的過程。每一步並不是必不可少。你參考的東西越多,作出一個差勁的實現的可能性就越小,但付出的精力也就越多。
關鍵的關鍵,當團隊中出現現有經驗沒法解決的問題的時候,你可以站出來,敢於承擔。這樣,你的技術之路也會愈來愈寬廣。
(完)
其它精選文章: