對于Web系統(tǒng)開發(fā)來說,Net其實也是有好多知識點需要學的,雖然目前JAVA是主流,就業(yè)市場比較大,但Net也在積極的擁抱開源,大Net Core 2 出來了,這無疑給Net開發(fā)者帶來更大的希望,好了,以下是自己畫的知識圖,給正在找工作的自己一個時間梳理下,同時也希望給你帶來些許幫助,第一次畫并鑒于自己知識點有限畫的不好,歡迎拍我,我及時糾正,謝謝!
注:圖是總-分結構,會針對一個知識點展開說明:
總結構圖:
(圖一)
分布式-結構圖:
(圖二)
分布式緩存 :
Memcached : 當然非真正意義的分布式緩存系統(tǒng),分布式需要客戶端自己實現(xiàn),特性:key-value 方式存儲;內(nèi)存緩存;不支持持久化;多臺服務器負載之間不支持通信;簡單,高效,可靠性弱。
Redis:用途較多,當緩存用,高并發(fā)用(提高吞吐量),當分布式鎖用(SETNX 方式)。特性:key-value 方式存儲(二進制);集群;有事務卻無事務回滾功能;持久化,可靠性強
Mongodb:No-SQL 數(shù)據(jù)庫,當然沒有數(shù)據(jù)結構,可以做海量數(shù)據(jù)存儲,特性:主從復制集,集群;持久化,基本支持所有的數(shù)據(jù)類型;靈活強大的查詢功能;
算法:
算法是我們解決分布式問題的利器。
取模:公式 a= hash(key)%N a 是哈希值,hash 為哈希函數(shù),key 是我們的服務器IP或名稱,N是服務器數(shù)量;從公式中可以直觀看出,當我們增加或刪除服務器數(shù)量時會導致大部分失效,如果是緩存則命中率大部分失效,可能導致緩存雪崩,另外分庫,分表 也可以采用取模算法,因為數(shù)據(jù)庫可以做復制集或鏡像能進行數(shù)據(jù)同步,也就是不存在失效的情況了。
哈希一致:是一個環(huán)形鏈表,首先hash(key)---key (可看作是key-value 的形式) 是均衡的分布在鏈表中,同樣的問題當我們動態(tài)增加和刪除服務器時,會動態(tài)重新負載到另外一臺Key即服務器上,這樣就保證了新的請求仍然正常工作,但會存在局部數(shù)據(jù)存在丟失的故障,那解決辦法是物理冗余,做好數(shù)據(jù)同步。
網(wǎng)絡通信-結構圖
(圖三)
WebApi:基于Rest 架構風格,采用Http協(xié)議,使用Asp.net 編程模型進行實現(xiàn),其常見的規(guī)范和約束:1,URLAPI 使用名稱;2,使用Http行為來表述行為:Get,POST,PUT,Delete;3,請求和響應約定序列化方式,Content-Type,Accept;4,返回狀態(tài)碼盡可以和http返回狀態(tài)一致,比如200代表成功,500 代表服務端異常;5,無狀態(tài)可方便擴展;6,使用HATEOAS約束,對客戶端提供一個URL,響應中返回你所有資源的URL及行為方式,后續(xù)客戶端可根據(jù)響應中的鏈接進行資源訪問,無論服務端如何變化,而對客戶端而言始終透明,以此降低客戶端的維護成本。
WCF:采用SOAP 協(xié)議,是以RPC-XML 規(guī)范為依據(jù),使用WSDL 進行描述和定義,支持http/s ,TCP,MSMQ 等通信協(xié)議;定義契約,實施開發(fā),客戶端集成方式有多種,可以直接引用服務,可以繼承ClientBase,可以通過ChannelFactory 信道工廠創(chuàng)建客戶端實例,當然無論采用方式都是采用客戶端代理模式的方式來透明實現(xiàn),在代理中有很多技巧:比如實現(xiàn)服務端的負載均衡,比如自定義讀取客戶端WCF相關的配置文件和其它配置進行解耦方便維護;WCF內(nèi)部很多基礎實施已然有很多擴展點,對客戶端的認證 可以使用 UserNamePassowrdValidator 進行擴展,授權 可以使用 IAuthorizationPolicy 進行擴展并使用CAS(Code Access Securty) 和 特性技術,全局異常點可以對 IErrorHandler 進行擴展,服務行為 可以對 IServiceBehavior 進行擴展,而且這些擴展代碼實現(xiàn)并能基于配置完成;
C#-結構圖
(圖四)
數(shù)據(jù)類型,總體分為值類型和引用類型,值類型包括 int,double,enum,float等,其特點是不變性;引用類型包括:string , class, interface 等,這里面尤其說明的是string 也有不變性,strng a="1" 和 string b="1" 中 a 和 b 是指向同一個引用,而且 string a="1"+"2" 中間其實生成了臨時的新對象并占用了一定內(nèi)存,建議使用 StringBulider ; 值類型和引用類型的轉(zhuǎn)換為裝箱和拆箱,影響性能,要盡量少的強制類型轉(zhuǎn)換,可以使用泛型進行強類型約定;
集合,List 無序,可重復,線程非安全,存在泛型,復雜度為 O(n) ; Dictionary 字典,key 不可重復,線程非安全,存在泛型,查找復雜度 O(1);HashTable 字典,key 不可重復,線程安全,查找復雜度 O(1); 當然還有 Queue,Stack 等 , 一般情況有對應的線程安全集合在前面加Concurrent,如 ConcurrentQueue,ConcurrentDictionary ;
委托和事件都可以進行內(nèi)部解耦,回調(diào),廣播,只是委托更加靈活,可以做為函數(shù)參數(shù)傳入,而事件只能在內(nèi)部調(diào)用;當然委托和事件的定義也不同,事件是建立在委托定義之上,更強調(diào)的是消息通知。
類和接口,多態(tài),封裝,繼承,是面向?qū)ο蟮幕緮?shù)據(jù)和特性,通過設計模式和設計原則能很清晰的熟悉面向?qū)ο蟮姆绞椒椒?如5大原則:開閉原則,單一原則,依賴倒置原則,里氏替換原則,接口隔離原則;其定義不再贅述。
并發(fā)-結構圖
(圖五)
有高并發(fā)的開發(fā)需求,首先要規(guī)劃你的硬件、軟件架構,那規(guī)劃的依據(jù)是量化的數(shù)據(jù),如PV,QPS;還要清楚軟件的特性和用什么技術,然后還要通過性能測試進行輔助,如JMeter , 根據(jù)測試工具壓測,得出整體服務的響應時間,吞吐量,另外還要對數(shù)據(jù)進行分析,看是否存在數(shù)據(jù)錯亂的情況。
緩存,主要是避免數(shù)據(jù)庫IO的瓶頸,而在內(nèi)存進行處理,當然單機緩存容量有一定局限,可以N臺機器互聯(lián)共享內(nèi)存,則采用分布式緩存,在高并發(fā)情況下,使用緩存有一個思路是 相關業(yè)務操作及代碼實現(xiàn)可以完全在緩存中操作,有些數(shù)據(jù)可以先預熱到緩存中,服務運行的時候可以直接從緩存中讀取,那緩存中數(shù)據(jù)更改后最終要同步到數(shù)據(jù)庫,可以異步+隊列的方式進行消息訂閱和發(fā)送或者采用輪詢定時從緩存中取出數(shù)據(jù)寫入到數(shù)據(jù)庫,在設計過程中格外注意同步失敗或出現(xiàn)故障的情況,要有重試機制;其實在獨立模塊,獨立服務中,有些服務并發(fā)并不高,那數(shù)據(jù)可以直接先寫入到數(shù)據(jù)庫中,而后再被緩存起來;最后 依據(jù)CAP定律,如果服務可用,并是容量分區(qū)這里是如果采用分布式緩存,最后只能是弱一致性,所以我們設計是解決90%以上的問題(當然如果能解決99.999%的問題更好了),另外10% 可以人工介入加以解決。
限流,高并發(fā)情況下肯定不能將全部請求全部接受并一次性處理那服務有被搞崩潰的可能,那就可以將部分重復請求丟棄,可以使用Nginx 的 對客戶端IP進行限制,同時高并發(fā)下肯定有些重要數(shù)據(jù)資源會存在競爭,如何保持數(shù)據(jù)一致性,使用鎖機制,悲觀鎖會嚴重影響性能,但不會存在臟讀,寫錯的情況,樂觀鎖,會存在臟讀的情況,但能保證數(shù)據(jù)寫入沒有問題,一般我們采用分布式鎖,使用Redis 的SETNX 特性,Redis 是單線程,存在事務,但事務沒有回滾機制,Redis的事務是命令集的方式,如 SETNX 如果不存在Key 值則返回true, 如果已存在Key 值則返回false , 如果返回false 代表請求已存在,則請求被直接丟棄,其實Redis 的 SETNX 和 Membcached 的Add 有點類似,然后使用隊列將請求串行化,到數(shù)據(jù)庫基本訪問不會太高,最后的數(shù)據(jù)庫起碼要最好主從模式,避免單一故障,最好數(shù)據(jù)冗余。
算法,對請求進行限流有個很常用的手法,令牌桶,我們先申請訪問的token, 并注入到一個桶容器中,設置容器的最大token 量,超過的請求直接丟棄,當請求訪問時驗證token 是否存在,如存在則正常處理后續(xù)業(yè)務邏輯并刪除token, 否則直接丟棄請求,以上算法的實現(xiàn)方式可以使用 Redis 中的 SETNX + Delete 命令實現(xiàn);
隊列,有很對成熟的隊列消息中間件,其中RabbitMQ 是較為常用,支持消息的持久化,避免中心服務DOWN 機后消息丟失;支持ACK機制,當消費者宕機或其它網(wǎng)絡原因?qū)е聸]有收到消息,則隊列會進行重發(fā)消息,直到消費者確認收到通過BasicAck 命令進行發(fā)送則消息從隊列中刪除;有限流機制,可以通過內(nèi)存大小,磁盤大小及上游流量大小三種方式對請求進行限流;可以集群,但各服務器隊列進程不互相通信,所以需要客戶端實現(xiàn)分布式,算法可以采用哈希一致性;
安全-結構圖
(圖六)
加密,對稱加密a=E(key) , b =D(key) a 是加密后值,E 是加密算法,key 是密鑰,b 是解密后值,D 是解密算法,可以看出加密解密的key 是一致的,共享的,所以只要知道key,雙方都可以進行加密解密,存在不安全因素,常用的對稱加密算法 DES; 數(shù)字簽名,即對內(nèi)容進行加密的一個字符串(數(shù)字摘要),主要是為了保證內(nèi)容的完整性及身份認證,常用的數(shù)字簽名的算法MD5;非對稱加密 c=E(key1),d=D(key2) c 是加密后值,key1 為私鑰,E 是加密算法,d 是解密后值,D 是解密算法,key2 為公鑰;可以看出加解密所使用的密鑰不同,當然也可以私用私鑰解密,公鑰加密,雙方的家解密的私鑰不公開,保證一定安全性,常見的非對稱加密算法RSA.
認證,常用的技術是使用Token或數(shù)字證書,Token 一般可以認為是數(shù)字簽名;數(shù)字證書可以認為是X.509(是證書的標準規(guī)范和解析);X.509 使用的是非對稱加密,客戶端根據(jù)證書中私鑰對特定內(nèi)容進行加密,然后發(fā)送到服務端,服務端通過公鑰進行解密校驗認證;微信公眾號開發(fā)中涉及的微信端和我們服務端的雙向認證是Token,但采用的對稱加密.
授權,Net 里面比較常用且規(guī)范的模式是RBAC,即基于角色控制授權,具體實現(xiàn)可以是 HttpModule + Attribute ; HttpModule 是對所有(一般不包括靜態(tài)資源)請求(線程)進行權限實例初始化并進行維持該會話,然后在每個需要授權的方法上標注Attribute及權限ID,Attribute 可以對CodeAccessSecurityAttribute進行擴展實現(xiàn)。
SSO,一般我們有這樣的需求,比如有多個子系統(tǒng),我們在一個系統(tǒng)中登陸成功后,其它系統(tǒng)就會共享此登陸狀態(tài)而不用再進行登陸,或者 我們有一個系統(tǒng),但是有多端可以使用,比如PC端,App端,當在一個客戶機上登陸成功后,其它端或客戶機即刻自動退出避免多端操作,由此我們想到了肯定要把一端登陸成功的狀態(tài)進行保存并能夠在多端共享并能全局訪問,擁有此特點的想到了有分布式緩存。
Web后端框架-結構圖
(圖七)
從最開始的Asp.net 到 現(xiàn)在的MVC3 以上版本,對開發(fā)越發(fā)靈活和成熟,MVC 是一種模式而非技術實現(xiàn),此處具體技術不再闡述,可以重點關注下一些比較實用的高級特性,比如 Area , Filter 等。
SignalR 是實時性框架,即主動通知,客戶端和服務端基于Http 建立長連接,當服務端有新的消息時可以主動推送到客戶端,常見客戶端訂閱可以基于JavaScript , C# 實現(xiàn),Android 端也有叫SignalA但沒用過,SignalR 內(nèi)部通信機制有4種,最好的是WebSocket , 客戶端可以指定采用何種通信機制,一般是自動切換模式,采用或切換到那種通信模式依據(jù)是客戶端的環(huán)境,理論上是越高配的性能就越好。
領域驅(qū)動設計,是架構模式,由此引申出來一套理論和方法,值對象像C#里面的值類型也是不變性的,只是值對象是一個Class實例的對象,此Class 里面有1個或多個屬性,為此我們必須覆寫它的Equal 和 GetHashCode 保證它的不可變,GetHashCode 是在用到 字典時進行驗證的,所以保險起見也要覆寫,實體對象 是唯一的,每次我們New 完都是使用的不同的對象實體,避免內(nèi)存對象不一致的情況,所以需要給定一個唯一標識,即也要對Equal 和 GetHashCode 進行覆寫;聚合根是包含了值對象和實體對象,一個聚合根可以理解為一個領域內(nèi)的模型,一般聚合根可以通過接口進行標注說明,而多個聚合根之間的會話通過領域事件來完成,領域事件的實現(xiàn)一般采用發(fā)布-訂閱模式,事件通過接口進行標注,發(fā)布者可以將訂閱者注入到容器中,容器的實現(xiàn)可以使用IOC框架如Unity,Autofac等,發(fā)布時根據(jù)不同的事件通知給不同的訂閱者進行觸發(fā),由于事件通知的是另外一個聚合根,而很可能出現(xiàn)跨網(wǎng)絡邊界進行通知,依據(jù)CAP定律,會出現(xiàn)數(shù)據(jù)不一致的情況,所以使用Event Source 進行事件回放,可以理解為重試機制,Event Source 最好和當前聚合根在同一個事務中,進行事件回放可以另一個進程來完成,而另一個聚合根和Event Source (比如處理完成后修改其狀態(tài))最好也是在一個事務中,這樣保證數(shù)據(jù)一致性,當然這里面會存在事件重復發(fā)的可能,解決方法增加一個字段標識,當事件發(fā)出去后,即可將此標識標記為一個特定狀態(tài)比如發(fā)送中,下次調(diào)度就不再取發(fā)送中的事件即可;在高并發(fā)情況不建議使用事務所以另外一個Event Source 實現(xiàn)方法是 使用隊列;我們對操作進行抽象無非就是CRUD,而CUD 我們可以抽象成命令也就是寫操作,R 我們知道是查詢也就是讀操作,而CQRS就是命令查詢職責分離,一般用在讀寫分離的架構當中,寫操作一般使用的工作單元模式,而讀操作可以很靈活,可以使用原生的Ado.Net,而讀寫操作可能會存在一定的時間延遲,比如寫后異步處理,讀的時候可能還是臟數(shù)據(jù),所以這個延遲或者要求不能延遲具體項目要具體設計和實現(xiàn)。
多線程-結構圖
(圖八)
為什么會有線程,為了進程的穩(wěn)定,一個進程死掉了,不能影響到其他進程,而為什么會有進程,為了操作系統(tǒng)的穩(wěn)定,不能在操作系統(tǒng)上操作一個記事本就把整個系統(tǒng)給搞崩潰了;線程是進程的邏輯單元,多個線程存在共用一個CPU的情況,會出現(xiàn)資源競爭,為了保證數(shù)據(jù)不錯亂,要對線程進行調(diào)度,每隔30毫秒進行一次切換,當然切換的過程存在性能磨損,也是為了用戶體驗和系統(tǒng)的可靠性犧牲一點性能,在我們多核時代這一點性能其實也是可以忽略的;
線程實現(xiàn),Net 中關鍵字 Thread , 分前臺線程和后臺線程,通過IsBackgournd來標記,一般我們都使用后臺線程即異步,線程分優(yōu)先級去處理,在調(diào)度過程中優(yōu)先級高的優(yōu)先處理,Net 中提供了比較豐富的線程API,比如Sleep,Join,Abort 就不再一一贅述了,線程池是為了解決線程來回切換損失的性能,它的主要作用就是 線程復用,當一個線程處理完成任務后重新回到線程池中,等待下次再用,有時我們異步處理數(shù)據(jù)時為了提高性能,對數(shù)據(jù)進行分組,然后分別放在不同的線程中去處理,比如 一個線程池中有5個線程,一批數(shù)據(jù)有 1000 條,則每一個線程處理200 條,當然可能會出現(xiàn)不整除的情況,(取模),當線程處理完成后重新回到池中等待下一個任務,線程池也有不足比如無法實時查看線程進度,不能反饋執(zhí)行結果,這樣Task 就出現(xiàn)了,Task 是在線程池的基礎上進行了封裝,優(yōu)化,Net 同樣也提供了豐富的API ,比如 Run, StartFactory, Wait,WaitAll,ContinueWith 等;
線程模式,有很多模式可以做為模板進行實現(xiàn),Single Thread 即我們常見的 線程內(nèi)加鎖的方式,多線程時通過加鎖來保證數(shù)據(jù)同步;Product-Customer 即我們發(fā)布訂閱的模式,生產(chǎn)者、消費者可以是多線程,而中間的數(shù)據(jù)容器可以使用內(nèi)存列表,數(shù)據(jù)庫,或者隊列;Thread-Pre-Message , 即線程池模式,多個線程在池中進行生命周期的維護;Asyn和Asyn And CallBack 即是我們常用的Task 來異步處理和帶委托進行回調(diào)的方式,當然你也可以架構方面更高層面進行理解。
(本文來源昵稱狗混子的博客園文章)