回覆列表
  • 1 # 那年那些事er

    摘要:為了給企業提供穩定可靠且優質的服務,作為一名軟體架構師,在應用的架構設計上也是費盡心思,本文作者來自“風語者客服+”的CTO黃耀華,他從自己多年的實踐出發,總結了軟體架構設計的一些經驗,分享給大家。

    “風語者客服+”是針對中小型企業推出的客服SaaS,節約了企業自建客服系統所需的巨大成本。為了給企業提供穩定可靠且優質的服務,我們在整體架構上費盡心思。雖然不盡完美,希望藉此拋磚引玉,互相切磋。

    前言

    ”Look deep into nature, and then you will understand everything better.“ -- Albert Einstein

    中國傳統文化上,要做成一件事,講究三個方面:明道,優術,取勢。在軟體架構設計方面而言,也是類似的道理:遵循自然規律以明確大的方向,使用優秀的實操戰術,再根據實際情況落地。

    這是個快餐年代,幾乎所有人都只做一件事 -“取勢”。 幾乎沒有多少人會去理解一個Servlet的工作原理,去理解一次HTTP請求的完整流程,因為有超多框架幫你遮蔽了這裡的細節。詢問一個人會什麼技術,回答也往往是我會Hibernate、Spring、Ibatis、會PullToRefresh元件、會使用SDWebimage。不過這些框架(Framework)其實並不是軟體架構。軟體架構是一所有生命力的房子,而這些框架只是大一點的板磚。

    因為筆者水平有限,這裡只提一些普遍準則,也就是”正確的廢話“,以饗視聽。不會深入到實操戰術上,比如怎麼用Spring實施MVC架構,怎麼使用Maven管理依賴,Redis的常用操作,怎麼搭建一個負載均衡的叢集,如何使用阿里巴巴的Dubbo框架進行服務化等等。如果大家有興趣,可以自行搜尋,有很多優秀的文章可供參考。

    不幸的“程式猿”和“程式媛”各有各的痛苦,幸福的程式設計師都是相似的。其實說幸福有點言過其實,下面就說說怎麼讓他們不那麼痛苦。

    一. 很好的模組化支援

    “At the bottom of every person"s dependency, there is always pain, Discovering the pain and healing it is an essential step in ending dependency.” --Chris Prentiss

    他們都在一個相對穩定的軟體架構裡編碼,自己的程式碼不會依賴很多模組,不會因為自己微小的改動造成全域性的失敗。正如"1984"中的老大哥說的,Ignorance is strength(”對外界的“無知就是一種力量). 任何一個模組都不能有太強的存在感。

    曾經在一個大型網際網路公司裡面,任何人只要用到一個核心模組的功能,就必須依賴一個部署在某遠端伺服器的庫,而且還有IP限制,只能把程式碼部署到指定網段才能執行起來。導致基本上沒法在本地進行單元測試或者簡單除錯。這個核心庫的存在感太強,就成了開發的瓶頸,嚴重的降低了生產力和碼農的幸福程度。

    在“風語者客服+”的架構中,每個碼農都可以很方便的在本地把服務啟動起來,一分鐘up and running,隨便做一些改動就可以立竿見影的看到效果。這裡要歸功於幾個東西:

    1. Git程式碼管理

    在團隊作戰中,每個程式設計師可以取下來完整的最新程式碼庫,也可以在本地分支上盡情揮毫潑墨,而不擔心影響別人的工作。也可以把本地修改先stash起來,review一下別人的程式碼,再unstash恢復回來。要想提高團隊效率,程式碼倉庫管理建議儘快遷移到Git上。

    2. Maven、Gradle、Cocopods等依賴管理

    Maven是一個管理依賴(Dependency)的工具,現在在Java社群應該是比較普及的,無法想象現在還有團隊直接複製jar包來管理依賴。雖然早期沒有Maven的時候,都是複製jar包這麼過來的,碰到的問題也是顯而易見的,依賴的jar包作者改了某個bug,沒能及時傳導到呼叫方。多個呼叫方使用不一致的jar包,導致各種奇異bug。對應的在安卓社群,使用gradle的比較多,iOS的Objective-C開發中,多采用CocoaPods。

    二. 高內聚,低耦合

    He should focus on his knitting, not trying to do everything. Do one thing well.-- Steve Jobs

    "Do one thing well"其實不算是老喬的專利,UNIX哲學和Google哲學都提倡這一點。這句話本身不完全對,比如對於一個商人,如果只會Do one thing well,那他無法在市場中存活,但是在工程師中卻是萬般推崇的哲學。

    我們可以期望一個人具備一百種技能,然而對一個工具只期望它把一個需求解決好解決徹底,對於實現工具的一個類,一個方法,更是如此。但是,實際經驗中,我們經常看到一個5000行以上程式碼的類,活像一個巨人版的瑞士軍刀,什麼都能做,但是什麼都做不好。這就是”Separation of Duty"沒有做好的典範。

    在風語者”客服+“對外提供的SDK和API中,我們也提倡同樣的思想,力爭把App使用”客服+“SDK的門檻降到最低,每個API都能自言其一,而且API直接沒有時序上的依賴關係。內部各個模組的開發,也秉承同樣的責任分割原則。責任分割原則的落實,沒有什麼好的框架或者工具來支援。只能透過老鳥經常去做Code Review,找出存在的問題,提出重構方案,並督促菜鳥改進。

    個人一般採用的重構思路,僅作為參考,照搬後被老闆批評乃至造成工傷概不負責:

    把一個大的工具類,根據主題不同,拆分成若干個互不干擾的高內聚工具類;舉個例子,一個萬能的NetworkUtils可能可以拆成HttpUtils, FTPUtils,TelnetUtils等;對於一個被頻繁呼叫的類,仔細觀察呼叫情況,如果有一些方法的被呼叫頻率遠遠低於其他方法,那麼需要考慮這個方法是不是應該放在這個類中;存在A,B兩個類之間的相互依賴,或者更多類的混亂依賴,那麼就更要抽絲剝繭,透過合理安排類的功能來去除環形依賴;嘗試一句話說清楚一個類的功能,不要使用“和”,“以及”,“或者”等連線詞;如果出現了這些連線詞,就需要引起重視;三. 用進化擁抱變化

    “It is not the strongest or the most intelligent who will survive but those who can best manage change.” ― Leon C. Megginson

    前段時間,朋友圈瘋傳一篇文章 -——“架構腐化之謎”,大家都深表同感,紛紛表示對自己架構的未來的擔憂。然而,說句不合時宜的話, 90%的擔憂是杞人憂天,因為以現在產品更新換代的速度,90%的專案面市即意味著死亡,沒等到架構腐朽,產品已經入土了。

    剩下10%裡面,也許有9%會一直堅持活下去,但是不會蓬勃發展,也就是說,只要保證不出現記憶體洩露之類的問題,程式碼就會一直在幾臺小伺服器上執行下去,哪怕後面沒有人維護也沒關係。只有1%的產品,會日新月異的更新迭代,最終成長為巨無霸,或者巨無霸的生態下的一個環節。這個言論看似悲觀,卻是對現實最好的妥協。

    謬用一下泰戈爾的名言:“不是槌的打擊,而是水的載歌載舞,使鵝卵石臻於完美”, 不是閉門造車的架構,而是不斷擁抱變化的需求,才使得架構臻於完美。

    假如在早期就糾結於架構的完美性,而延遲產品的交付,是非常得不償失的。只有生存下來,才有機會。再根據市場變化,不斷最佳化架構,從而延長軟體的生命週期。那麼,假如撞大運,真的成了這1%,怎樣做才能算是擁抱變化?

    首先,請參考本文第一點和第二點。如果這兩點基本功沒有練好,那麼談架構的進化就和還沒有通關十八羅漢的新手就想練成九陰真經是一個道理。

    在設計之初,初步考慮系統的Scalability(可伸縮性)

    下面在第四點會詳細闡述。

    內部的各個模組儘量做到可插拔

    一方面是介面和實現的分離,可以隨著需求的變化更換實現;另一方面,儘量把功能服務化,成為微服務,並且可以監控到服務的互相呼叫情況,當某個服務老化,可以逐步廢棄或使用新的服務取代之。這一點上,阿里巴巴的Dubbo框架是一個不錯的選擇。

    儘量採用優秀的框架,站在巨人的肩膀上

    例如在Web層面,我們使用Twitter的Bootstrap前端框架來實現響應式Web程式設計,提高生產效率的同時減少了為解決各種裝置適配問題的投入。當然,這就需要設計師配合,按照Bootstrap規範來設計頁面,減少一些個性化設計。

    最後,考慮系統的Resilience(彈性,也叫耐受性)

    俗一點說,就是變成一隻打不死的小強,程式碼中儘量提前預判可能遇到的各種情形。經常看到程式碼裡面有一堆的if(){}判斷語句,我就問作者,“你考慮過else{}嗎?”一般回答都是,“這絕對只有if,不會有else的”,可如果真的遇到else怎麼辦?千年蟲問題就是這麼誕生的。可能很多新同學還不知道什麼是千年蟲問題,簡單地說,就是當年的碼農,為了省一點記憶體空間,只用了2位數來表達年份,比如int year = 98; 表達1998年。我猜碼農當時的心態也是,“就我這程式碼,還能活到2000年,搞笑吧?”

    程式設計師們平時可以多擴大自己的腦洞,想想有哪些else情況自己沒有處理,而且可以輕易處理的。比如伺服器掛了,那麼App端是不是也要跟著crash,還是給出友好一點的提示,或者更友好一點,使用本地快取。

    四. 設計可擴充套件,但不要過度設計

    it"s better to have infinite scalability and not need it, than to need infinite scalability and not have it--@littleidea 網友

    無限的擴充套件能力是一種奢望,但是起碼不能讓擴充套件能力成為0。試想一下,你辛辛苦苦為老闆開發了一個網站,過了一個月,網站超負荷了,老闆說,“小A啊,之前2臺伺服器花了我5萬塊,預計流量馬上要翻倍了,再給你5萬塊,幫我扛過去啊。”結果你發現,問題不是線性增加伺服器就能解決的,原來的程式沒有做分層(Web,Business Logic, Data Access等),導致加伺服器也只能把所有層的程式碼全搬到新的伺服器,雖然只是Business Logic的計算有壓力,卻要浪費老闆很多伺服器。更糟糕的是,因為程式裡面用到了檔案系統和作業系統命令,不好做負載均衡。

    這裡有一些準則供參考:

    程式碼分層是必須的,層次明朗以後,當哪個層次的負載較重,想辦法對該層次進行最佳化或者擴容即可;保持核心服務是無狀態的,所謂無狀態就是沒有和請求相關的資料依賴;儘可能的選用已被驗證的廣泛採用的成熟基礎架構;充分利用Zookeeper等叢集管理工具,來對服務進行管理;

    風語者“客服+”中,把業務相關的程式碼內部組裝為風語者ServiceBox,使用阿里巴巴的Dubbo服務進行註冊管理。當負載增加時,可以迅速在運維層面增加服務節點,以提供更高的服務能力,從而保證客戶的優質體驗。

  • 2 # 木子教程

    程式碼複用

    無論是開發哪種軟體產品,成本和時間都是最重要的。較少的開發時間意味著可以比競爭對手更早進入市場。較低的開發成本意味著能夠留出更多的營銷資金,覆蓋更廣泛的潛在客戶。

    其中,程式碼複用是減少開發成本最常用的方式之一,其目的非常明顯,即:與其反覆從頭開發,不如在新物件中重用已有的程式碼。

    這個想法表面看起來很棒,但實際上要讓已有的程式碼在全新的程式碼中工作,還是需要付出額外努力的。元件間緊密的耦合、對具體類而非介面的依賴和硬編碼的行為都會降低程式碼的靈活性,使得複用這些程式碼變得更加困難。

    使用設計模式是增加軟體元件靈活性並使其易於複用的方式之一。但是,這可能也會讓元件變得更加複雜。

    一般情況下,複用可以分為三個層次。在最底層,可以複用類、類庫、容器,也許還有一些類的“團體(例如容器和迭代器)”。

    框架位於最高層。它們能幫助你精簡自己的設計,可以明確解決問題所需的抽象概念,然後用類來表示這些概念並定義其關係。例如,JUnit 是一個小型框架,也是框架的“Hello, world”,其中定義了 Test、TestCase 和 TestSuite 這幾個類及其關係。框架通常比單個類的顆粒度要大。你可以透過在某處構建子類來與框架建立聯絡。這些子類信奉“別給我們打電話,我們會給你打電話的。”

    還有一箇中間層次。這是我覺得設計模式所處的位置。設計模式比框架更小且更抽象。它們實際上是對一組類的關係及其互動方式的描述。當你從類轉向模式,並最終到達框架的過程中,複用程度會不斷增加。

    中間層次的優點在於模式提供的複用方式要比框架的風險小。建立框架是一項投入重大且風險很高的工作,模式則能讓你獨立於具體程式碼來複用設計思想和理念。

    擴充套件性

    需求變化是程式設計師生命中唯一不變的事情。比如以下幾種場景:

    你在 Windows 平臺上釋出了一款遊戲,現在人們想要 Mac OS 的版本。你建立了一個使用方形按鈕的 GUI 框架,但幾個月後開始流行原型按鈕。你設計了一款優秀的電子商務網站,但僅僅幾個月後,客戶就要求新增電話訂單的功能。

    每個軟體開發者都經歷過許多相似的故事,導致它們發生的原因也不少。

    首先,在完成了第一版的程式後,我們就應該做好了從頭開始最佳化重寫程式碼的準備,因為現在你已經能在很多方面更好的理解問題了,同時在專業水平上也有所提高,所以之前的程式碼現在看上去可能會顯得很糟糕。

    其次,可能是在你掌控之外的某些事情發生了變化,這也是導致許多開發團隊轉變最初想法的原因。比如,每位在網路應用中使用 Flash 的開發者都必須重新開發或移植程式碼,因為不斷地有瀏覽器停止對 Flash 格式地支援。

    最後,可能是需求的改變,之前你的客戶對當前版本的程式感到滿意,但是現在希望對程式進行 11 個“小小”的改動,使其可完成原始計劃階段中完全沒有提到的功能,新增或改變功能。

    當然這也有好的一面,如果有人要求你對程式進行修改,至少說明還有人關心它。因此在設計程式架構時,有經驗的開發者都會盡量選擇支援未來任何可能變更的方式。

  • 中秋節和大豐收的關聯?
  • 約旦屬於哪個洲?