【架構入門系列】從業務到平臺的思維轉變

91Code發表於2019-01-14

一個需求:抽獎系統

如果你接到了一個開發需求:開發一個抽獎活動的後臺系統。第一反應如果是開啟IDEA,新建一個專案,輸入專案名:lottery。然後開始根據需求著手開始進行設計資料庫,api,然後進行開發,這是太平常不過的開發流程。

過了一段時間,你又接到一個類似的需求:開發另一個抽獎活動的後臺系統,但是裡面的一些業務邏輯、流程處理不太一樣,然後就把前一個完成的後臺系統複製過來,準備開始修改。

【架構入門系列】從業務到平臺的思維轉變

是時候停止這種看起來很高效但實際上很愚蠢的開發方式了!本文將告訴你如何將你的思想轉變過來,給你一個將業務、將系統、甚至將自己昇華的機會。我會用偏後端的思維、儘量易懂的圖文、儘量精簡的程式碼、儘量多的例子來將你的等級從熟練的業務馴服者提升為初階架構獵人

【架構入門系列】從業務到平臺的思維轉變

反思:做錯了什麼

使用複製、貼上、修改的方法去開發一個類似的新的業務系統,從開發單個系統上來看開發效率上確實挺高的,但仍然存在以下問題:

  • 程式碼複用性低。類似的業務系統可能越來越多,複製貼上導致程式碼的重複率太高,可複用性太低
  • 測試、部署效率太低。需要當做一個新的後臺服務新建一套測試、構建、部署、釋出流程並進行實施,對一些通用的業務邏輯進行了重複的測試,構建釋出流程也需要單獨管理
  • 收集資料難度大。類似的業務系統的資料被分散在不同的資料庫、日誌中,如果想收集彙總分析一些通用的業務日誌難度大
  • 業務系統太過私有化,無法提供類似SAAS服務那樣讓任何人都能建立自己的抽獎活動

簡而言之,老鼠的視力很差,它沒有辦法看到遠處的事物,但是我們為了日後業務系統的重複利用,就需要儘量避免鼠目寸光的重複開發。

所以,需要將業務系統抽象為業務平臺,支援通用的業務流程,讓業務平臺為日後的類似的業務系統提供靈活多變的基礎業務實現方案,至於特殊的業務流程就單獨開發一個特殊的業務系統,兩者共同結合提供完整的服務。

另外,平臺的使用者應該類似SAAS,具備為所有人服務的能力,提供給能讓每個使用者甚至每個組織都能建立屬於自己的獨有環境,可以通過在平臺上的簡單配置構建出屬於個體的服務。

【架構入門系列】從業務到平臺的思維轉變

如圖所示,業務系統與業務平臺共同為使用者服務。業務平臺包含某個業務的通用流程,可能剛好能滿足業務的所有需求,那麼此時就不需要另外開發一個業務系統。但如果有一些比較特殊的業務流程或者必須單獨建立一個業務方便管理控制的流程的話,那麼可以將該業務系統與業務平臺結合到一起使用,業務系統提供定製介面,業務平臺提供通用介面,業務系統也能通過授權的呼叫介面來呼叫業務平臺獲取、修改資料。

通用業務

首先我們需要想清楚,如何從平臺的角度考慮,將設計業務邏輯才能比較通用?如何區分是否為通用的業務邏輯,從而進行業務拆分?

通用性

目的:解決業務實現的N種可能。在業務平臺上儘量支援多的、通用的業務邏輯。

如何解決:把眼光放長遠一些,我們這個業務系統現在需要支援哪些業務邏輯?日後需要支援哪些?然後進行有選擇的取捨,不需要考慮太多,否則系統將被設計的特別複雜且用到的部分太少。一個簡單有效的方法就是參照以前做過的系統和當前需要實現的系統,將通用的邏輯抽取出來。

舉幾個例子:

  • 在業務流程上的考量。比如上個活動只開了一天,獎品也都相同,但是這次的活動需要開兩場,每場的獎品可能不一樣。所以我們能抽象出:一個抽獎活動可能存在多個活動場次,每個場次對應的獎品、時間可能都不同,還需要支援隨時進行修改,這就將活動拆分為活動與場次兩個層次。所以在資料庫設計的時候就需要新增一個欄位來對應場次這個維度
  • 在資料庫表欄位上的考量。獎品的兌換方式可能是通過快遞傳送實物獎品、直接展示兌換碼,所以對於表欄位的種類要考慮多一些,比如需要儲存使用者的收貨地址、聯絡方式。在單個欄位的屬性選擇也要適當考慮可擴充套件一些,比如欄位長度、欄位型別

邊界處理、業務拆分

這裡的邊界指的是業務拆分時的邊界,比如,哪些屬於平臺通用邏輯?就能把這些通用邏輯放到平臺裡。哪些屬於業務特有邏輯?這些邏輯就不需要加到平臺裡。

比如,對於抽獎活動,配置獎品、分發獎品是抽獎平臺通用邏輯,但使用者排行榜則不夠通用,除了遊戲相關的場景,很少需要用到排行榜的功能,那麼排行榜功能就應該屬於業務特有邏輯。

在整個抽獎的業務流程中,還有一個業務流程是當抽獎次數用光,就可以通過分享的方式增加抽獎次數。所以抽獎業務通用邏輯依然可以繼續拆分,對於分享功能,是不屬於抽獎平臺通用邏輯的,那麼可以將分享功能也單獨抽出來成為分享平臺,使之也能服務於其他的平臺或業務系統。

看似當前已經拆得比較清晰了,但還沒有結束,因為這是僅僅針對於【分享成功->增加一次抽獎機會】這樣的流程,如果需要分享多次才能增加一次機會呢?如果分享之後每次增加不同的抽獎機會呢?誰負責維護【分享-增加機會】這個獎勵機制呢?這就需要一個引擎、流程控制中心來處理,不如就叫任務排程平臺

【架構入門系列】從業務到平臺的思維轉變

任務排程平臺的核心業務流程是【完成任務-觸發獎勵】,至於是誰完成的任務,觸發誰的獎勵對它來說並不重要。只要它能夠將任務的觸發者與被觸發者的對應關係、觸發條件、被觸發者的獎勵介面呼叫方式維護在內部,當檢測到觸發者達到了觸發條件,能夠呼叫到對應的獎勵即可

資料處理

大資料量的儲存

既然是平臺,會因為使用者使用時長、資料量方面就會比一般的特有業務系統多,在設計時需要考慮到提前為書資料量較大的某些表進行水平分表設計,貼一個簡單的分表的例子:

【架構入門系列】從業務到平臺的思維轉變

在建立資料庫時按上圖進行建立,在程式碼中進行增刪改查時用下面的方法根據分表欄位(shard key)來獲取表名:

func GetTableNameByActivityId(activityId int64) string {
	if activityId > 0 {
		return "activity_prize_redeem_" + strconv.FormatInt(activityId % 16, 10)
	}
	return "activity_prize_redeem"
}
複製程式碼

分表時,根據哪個欄位(shard key)進行分表也是需要考慮的,需要使資料能夠均衡的分佈在多張表中,並且不影響正常的查詢,這樣才能通過分表的方式將資料均勻分佈到不同的表中,根據shard key進行查詢時效率與不分表時一樣,但如果使用另外的一個欄位查詢資料可能需要遍歷所有的表才能將資料查詢到,所以shard key的選擇是與業務查詢需求、資料均勻分佈相關。

分表之後,最直接的影響是開發的時候需要對SQL語句進行動態調整,某些ORM框架不支援導致的開發效率降低,但是改動的程式碼不多,一勞永逸。

高效能查詢

對於平臺來說,併發訪問量也可能較大,那麼快取、佇列、ES必不可少。

對於快取,現在基本上說的都是使用Redis,可以使用的叢集模式有主從、哨兵、叢集。使用的策略也有延遲載入、直寫,還需要考慮到一些擊穿、失效的快取問題,具體可以看這裡

對於訊息佇列,在業務解耦、流量削峰方面是一個非常重要的中介軟體,具體可以看這裡

對於Elastic Search,如果業務上有全文檢索的需求就是要結合EFK一起上的一個模組,具體使用方式、場景請自行查閱。

運營埋點、報表

資料太重要了!分析使用者行為,預測市場走向,還是以後設計系統的參考指標,我們作為技術開發人員,也需要為運營人員著想,需要統計出運營人員感興趣的一些資料,最好是直接找他們提前詢問清楚,哪些資料他們需要,以便我們在設計資料庫的時候將一些關鍵資料用一些欄位儲存下來。

比如在抽獎活動中,運營人員需要知道web平臺、安卓平臺、IOS平臺分別參與抽獎的人分別有多少,但是設計出來的資料庫欄位、程式碼中列印出的日誌可能都不存在或者難以通過現有設計進行統計,那麼就需要對這個統計需求進行單獨的設計。

日誌、操作記錄表

在程式碼中輸出日誌是必須的,比如通常需要在訪問API、某個API裡的關鍵邏輯、結合關鍵資料列印成功或失敗資訊。

另外,對於某些業務場景需要嚴格統計資料操作之前的情況、操作之後的情況、操作型別、操作人員、操作時間。雖然也可以通過輸出日誌的方式,但不夠規範,查詢統計難度大,容易丟失。所以需要單獨設計出一張操作流水錶,將重要資訊持久化起來。

【架構入門系列】從業務到平臺的思維轉變

在每個需要統計的重要操作執行之後,使用訊息佇列或者另外的執行緒去插入一條操作記錄到這張表中,如下

go service.Record(&models.SysOperateRecord{
	BizType:     dao.BizType_Customer,
	OperType:    dao.OperType_INSERT,
	OperContent: fmt.Sprintf("batch insert one new customer: %s", customer),
	Operator:    userinfo.Id,
	CreateTime:  currentTime,
	UpdateTime:  currentTime,
})
複製程式碼

平臺管理

類SAAS獨立環境

如何讓使用者有自己的獨立環境?讓他們可以在平臺上簡單配置之後建立自己的應用?

其實非常簡單,在只考慮使用者而不是租戶的名稱空間下,在系統層面和資料庫設計層面多進行一層考慮即可,比如在資料庫的主表/主實體上增加兩個欄位:namespace_idapp_id

  • namespace_id代表一個使用者擁有的唯一且與其他使用者相互隔離的名稱空間。一個使用者建立的所有業務系統、資料都在一個namespace_id下面,所以該使用者建立的所有業務系統的資料都能用namespace_id查詢得到。
  • app_id代表具體的某個業務系統(應用)的唯一id。所以該使用者建立的某個特定的業務系統的資料能用app_id查詢到。

多平臺統一管理

在多個平臺(抽獎、分享、任務平臺)建立起之後,需要一個後臺管理平臺將這幾個平臺統一管理起來,方便為使用者提供統一的配置。

以建立一個業務系統為例,如果使用管理平臺進行配置,那麼使用者只需要在管理平臺上填寫不同平臺的對應業務配置即可,在管理平臺中將自動處理:

  1. 管理平臺的建立應用功能將先建立一個統一的namespace_id、app_id
  2. 將namespace_id、app_id、不同平臺的對應配置通過內部呼叫寫入到對應平臺的資料庫配置表中

對配置好平臺配置進行修改、刪除,對業務資料進行統計的操作可通過管理平臺的調整配置、統計資料介面進行

【架構入門系列】從業務到平臺的思維轉變

技術選型

微服務

為什麼說適合用微服務架構來開發平臺?

  • 平臺業務的擴張。往平臺的方向考慮,業務邏輯會越來越複雜,程式碼量會越來越龐大,為了服務的獨立部署測試、調整,服務的拆分成為必然。
  • 平臺的統一管理。通過API閘道器、配置中心、服務發現註冊中心、熔斷器等元件,可以將多個服務統一管理起來。比如在閘道器新增鑑權模組、收集所有請求日誌,在配置中心統一管理服務的配置檔案,在熔斷器中對不同服務進行管理配置。以上都是為了將零散的服務通過相關元件統一成整體

業務決定架構

業務如何決定架構?

  • 業務的併發量、穩定性。如果併發量是業務需要滿足的一個條件,則需要能夠支撐大量請求的高可用架構。比如每個服務節點的多節點負載均衡、資料庫使用MySQL還是MongoDB的選擇、資料庫架構的主從複製讀寫分離、中介軟體的選擇、前端的快取與CDN技術
  • 業務的第三方支援。如推送、搜尋功能自己實現的話比較麻煩,一般使用第三方服務,這種第三方與自己服務的結合也是架構中的體現
  • 業務需求引發的內部互動。如一般應用的註冊功能,都會使用訊息佇列這個中介軟體將寫資料庫行為與其他行為(發簡訊、發郵件)解耦;還可能設計一些定時任務從某些資料來源定期獲取資料更新到另一個資料來源

這裡給出另一個比較能體現業務決定架構思想的一篇例項文章參考

效能

如果只考慮效能,那麼不能選擇微服務架構,因為單體應用在效能上是完全碾壓微服務架構的。如果為了全域性考慮之後還是得選擇微服務,那麼如何在使用微服務的情況下儘量提升訪問的效能呢?

首先需要通過壓測的方式測試出效能的瓶頸在哪裡,這篇文章可能有所幫助。

至少需要保證能在效能問題發生之後能夠立即解決,所以至少要做到服務能夠在任意時刻能夠版本回滾、能夠動態擴充套件到更多機器上。然後通過對應的方式如優化程式碼、使用快取、佇列、新增伺服器、改進中介軟體架構的方式去解決效能問題。

部署方式

對一個架構師來講,程式碼從提交到上線也需要考慮到,甚至需要考慮為團隊構建DevOps體系。簡單來說,使用自動化流水線將程式碼提交、測試、構建、部署自動化起來,並在專案上線的過程中協助開發測試運維進行程式碼版本的切換、部署方式的實踐。

為了日後服務的藍綠部署、回滾、動態伸縮,必須使用容器技術如Docker與容器編排平臺如K8S來實現。至於具體的搭建方式、維護細節不用完全掌握,但一定要知道各種部署方式、服務維護方式和其利弊。

安全考慮

業務安全

對於抽獎系統,因為獎品有實際價值,所以直接跟錢掛鉤。為了防止惡意使用者擼羊毛,必須對可能存在的抽獎後門進行封殺。

對於抽獎系統,最大的後門是:中獎介面。如何判斷使用者是真正的玩了這個遊戲並一路闖關達到終點奪得獎勵的呢?對於web平臺來說真的是一個比較困難的問題,因為沒辦法跟遊戲一樣實時獲得使用者的資料進行詳細的判定,所以只能在最大程度上增加中獎介面的校驗規則。如下:

  • 遊戲邏輯校驗。根據前端設計,先測試出奪得獎品最快用時是多少,然後在後臺加入判斷:如果用時少於最快用時,封殺。另外可以根據遊戲業務邏輯判斷:必須通過點選【同意協議】、【開始遊戲】這兩個按鈕(訪問這兩個介面)才能成功呼叫中獎介面。
  • 驗證碼。防止惡意使用者使用指令碼自動刷獎,中獎介面新增驗證碼方式校驗。但這極大程度上影響用的體驗,謹慎使用。
  • 黑名單控制。對於反覆呼叫很多次的使用者,直接將該使用者id加入到黑名單中,在一段時間內禁止介面訪問。

網站安全

這是一些比較通用的web安全問題,一般在框架內部已經解決,但也需要確認是否開啟相關安全機制。

  • SQL隱碼攻擊。如果使用的是ORM框架則不需要擔心這個問題,因為框架內部已經使用轉義的方式幫你處理好會影響SQL語句的特殊字元。如果涉及到複雜的SQL手動拼接,那麼必須在開發時使用佔位符的方式來拼接字串來解決該問題
  • csrf。像Go語言的Beego框架,Ruby語言的Ruby On Rails框架,Java語言的Spring Security中就已經提供解決方案,但並不是預設開啟,需要在前後端統一配置才能生效,後端配置之後,會在session儲存一個csrf的token,等待下次請求前端傳過來這個token來驗證。所以對於某些安全性較高的操作是需要新增csrf防範的
  • xss。在框架裡可能沒有現成方案,那麼需要在前後端都加以防範,比如在前端JS中要避免把不可信的資料當作程式碼執行了,在後端需要配置專門的過濾器,在過濾器中使用轉義工具將影響JS執行的特殊字元進行轉義
  • https。防止中間人攻擊,提升網站安全性、搜尋權重,眾人皆知,不再贅述
  • 許可權控制。避免普通使用者能夠使用管理員的許可權類似的問題,需要在後端進行限制
  • 密碼管理。即使資料庫被人破解,也不能讓別人看到你的明文密碼。對於不需要解密的密碼,比如使用者登入的密碼,使用bcrypt演算法或md5加鹽進行加密;對於需要解密的密碼,使用AES或對稱加密演算法加密儲存,加解密使用的金鑰需要另外儲存

等等等等,網站安全問題是在太多,沒辦法都寫出來,不過以上問題比較常見,需要在開發時外加註意,這些雖然不會在架構設計圖上展示出來,但是會是一個存在的架構隱患

總結

我們從業務、資料、技術、安全方面綜合考慮如何將簡單的業務設計成為一個平臺,看重的是長遠的收益,提升的是自己的能力。作為開發人員的你也許已經煩透寫業務程式碼,不妨換一種開發思想挑戰一下自己吧。

來源:91Code

關注公眾號獲取更多內容!

在這裡插入圖片描述

相關文章