1. 程式人生 > >Net知識圖譜

Net知識圖譜

andro 結構 不可變 存在 token 數據 更改 分離 得出

對於Web系統開發來說,Net其實也是有好多知識點需要學的,雖然目前JAVA是主流,就業市場比較大,但Net也在積極的擁抱開源,大Net Core 2 出來了,這無疑給Net開發者帶來更大的希望,好了,以下是自己畫的知識圖,給正在找工作的自己一個時間梳理下,同時也希望給你帶來些許幫助,第一次畫並鑒於自己知識點有限畫的不好,歡迎拍我,我及時糾正,謝謝!

註:圖是總-分結構,會針對一個知識點展開說明:

  • 總結構圖:

技術分享

(圖一)

  • 分布式-結構圖:

技術分享

                      (圖二)

  • 分布式緩存 :
  1. Memcached : 當然非真正意義的分布式緩存系統,分布式需要客戶端自己實現,特性:key-value 方式存儲;內存緩存;不支持持久化;多臺服務器負載之間不支持通信;簡單,高效,可靠性弱。
  2. Redis:用途較多,當緩存用,高並發用(提高吞吐量),當分布式鎖用(SETNX 方式)。特性:key-value 方式存儲(二進制);集群;有事務卻無事務回滾功能;持久化,可靠性強
  3. Mongodb:No-SQL 數據庫,當然沒有數據結構,可以做海量數據存儲,特性:主從復制集,集群;持久化,基本支持所有的數據類型;靈活強大的查詢功能;
  • 算法:

算法是我們解決分布式問題的利器。

  1. 取模:公式 a= hash(key)%N a 是哈希值,hash 為哈希函數,key 是我們的服務器IP或名稱,N是服務器數量;從公式中可以直觀看出,當我們增加或刪除服務器數量時會導致大部分失效,如果是緩存則命中率大部分失效,可能導致緩存雪崩,另外分庫,分表 也可以采用取模算法,因為數據庫可以做復制集或鏡像能進行數據同步,也就是不存在失效的情況了。
  2. 哈希一致:是一個環形鏈表,首先hash(key)---key (可看作是key-value 的形式) 是均衡的分布在鏈表中,同樣的問題當我們動態增加和刪除服務器時,會動態重新負載到另外一臺Key即服務器上,這樣就保證了新的請求仍然正常工作,但會存在局部數據存在丟失的故障,那解決辦法是物理冗余,做好數據同步。
  • 網絡通信-結構圖

技術分享

(圖三)

  1. WebApi:基於Rest 架構風格,采用Http協議,使用Asp.net 編程模型進行實現,其常見的規範和約束:1,URLAPI 使用名稱;2,使用Http行為來表述行為:Get,POST,PUT,Delete;3,請求和響應約定序列化方式,Content-Type,Accept;4,返回狀態碼盡可以和http返回狀態一致,比如200代表成功,500 代表服務端異常;5,無狀態可方便擴展;6,使用HATEOAS約束,對客戶端提供一個URL,響應中返回你所有資源的URL及行為方式,後續客戶端可根據響應中的鏈接進行資源訪問,無論服務端如何變化,而對客戶端而言始終透明,以此降低客戶端的維護成本。
  2. WCF:采用SOAP 協議,是以RPC-XML 規範為依據,使用WSDL 進行描述和定義,支持http/s ,TCP,MSMQ 等通信協議;定義契約,實施開發,客戶端集成方式有多種,可以直接引用服務,可以繼承ClientBase,可以通過ChannelFactory 信道工廠創建客戶端實例,當然無論采用方式都是采用客戶端代理模式的方式來透明實現,在代理中有很多技巧:比如實現服務端的負載均衡,比如自定義讀取客戶端WCF相關的配置文件和其它配置進行解耦方便維護;WCF內部很多基礎實施已然有很多擴展點,對客戶端的認證 可以使用 UserNamePassowrdValidator 進行擴展,授權 可以使用 IAuthorizationPolicy 進行擴展並使用CAS(Code Access Securty) 和 特性技術,全局異常點可以對 IErrorHandler 進行擴展,服務行為 可以對 IServiceBehavior 進行擴展,而且這些擴展代碼實現並能基於配置完成;
  • C#-結構圖

技術分享

(圖四)

  1. 數據類型,總體分為值類型和引用類型,值類型包括 int,double,enum,float等,其特點是不變性;引用類型包括:string , class, interface 等,這裏面尤其說明的是string 也有不變性,strng a="1" 和 string b="1" 中 a 和 b 是指向同一個引用,而且 string a="1"+"2" 中間其實生成了臨時的新對象並占用了一定內存,建議使用 StringBulider ; 值類型和引用類型的轉換為裝箱和拆箱,影響性能,要盡量少的強制類型轉換,可以使用泛型進行強類型約定;
  2. 集合,List 無序,可重復,線程非安全,存在泛型,復雜度為 O(n) ; Dictionary 字典,key 不可重復,線程非安全,存在泛型,查找復雜度 O(1);HashTable 字典,key 不可重復,線程安全,查找復雜度 O(1); 當然還有 Queue,Stack 等 , 一般情況有對應的線程安全集合在前面加Concurrent,如 ConcurrentQueue,ConcurrentDictionary ;
  3. 委托和事件都可以進行內部解耦,回調,廣播,只是委托更加靈活,可以做為函數參數傳入,而事件只能在內部調用;當然委托和事件的定義也不同,事件是建立在委托定義之上,更強調的是消息通知。
  4. 類和接口,多態,封裝,繼承,是面向對象的基本數據和特性,通過設計模式和設計原則能很清晰的熟悉面向對象的方式方法;如5大原則:開閉原則,單一原則,依賴倒置原則,裏氏替換原則,接口隔離原則;其定義不再贅述。
  • 並發-結構圖

技術分享

(圖五)

  1. 有高並發的開發需求,首先要規劃你的硬件、軟件架構,那規劃的依據是量化的數據,如PV,QPS;還要清楚軟件的特性和用什麽技術,然後還要通過性能測試進行輔助,如JMeter , 根據測試工具壓測,得出整體服務的響應時間,吞吐量,另外還要對數據進行分析,看是否存在數據錯亂的情況。
  2. 緩存,主要是避免數據庫IO的瓶頸,而在內存進行處理,當然單機緩存容量有一定局限,可以N臺機器互聯共享內存,則采用分布式緩存,在高並發情況下,使用緩存有一個思路是 相關業務操作及代碼實現可以完全在緩存中操作,有些數據可以先預熱到緩存中,服務運行的時候可以直接從緩存中讀取,那緩存中數據更改後最終要同步到數據庫,可以異步+隊列的方式進行消息訂閱和發送或者采用輪詢定時從緩存中取出數據寫入到數據庫,在設計過程中格外註意同步失敗或出現故障的情況,要有重試機制;其實在獨立模塊,獨立服務中,有些服務並發並不高,那數據可以直接先寫入到數據庫中,而後再被緩存起來;最後 依據CAP定律,如果服務可用,並是容量分區這裏是如果采用分布式緩存,最後只能是弱一致性,所以我們設計是解決90%以上的問題(當然如果能解決99.999%的問題更好了),另外10% 可以人工介入加以解決。
  3. 限流,高並發情況下肯定不能將全部請求全部接受並一次性處理那服務有被搞崩潰的可能,那就可以將部分重復請求丟棄,可以使用Nginx 的 對客戶端IP進行限制,同時高並發下肯定有些重要數據資源會存在競爭,如何保持數據一致性,使用鎖機制,悲觀鎖會嚴重影響性能,但不會存在臟讀,寫錯的情況,樂觀鎖,會存在臟讀的情況,但能保證數據寫入沒有問題,一般我們采用分布式鎖,使用Redis 的SETNX 特性,Redis 是單線程,存在事務,但事務沒有回滾機制,Redis的事務是命令集的方式,如 SETNX 如果不存在Key 值則返回true, 如果已存在Key 值則返回false , 如果返回false 代表請求已存在,則請求被直接丟棄,其實Redis 的 SETNX 和 Membcached 的Add 有點類似,然後使用隊列將請求串行化,到數據庫基本訪問不會太高,最後的數據庫起碼要最好主從模式,避免單一故障,最好數據冗余。
  4. 算法,對請求進行限流有個很常用的手法,令牌桶,我們先申請訪問的token, 並註入到一個桶容器中,設置容器的最大token 量,超過的請求直接丟棄,當請求訪問時驗證token 是否存在,如存在則正常處理後續業務邏輯並刪除token, 否則直接丟棄請求,以上算法的實現方式可以使用 Redis 中的 SETNX + Delete 命令實現;
  5. 隊列,有很對成熟的隊列消息中間件,其中RabbitMQ 是較為常用,支持消息的持久化,避免中心服務DOWN 機後消息丟失;支持ACK機制,當消費者宕機或其它網絡原因導致沒有收到消息,則隊列會進行重發消息,直到消費者確認收到通過BasicAck 命令進行發送則消息從隊列中刪除;有限流機制,可以通過內存大小,磁盤大小及上遊流量大小三種方式對請求進行限流;可以集群,但各服務器隊列進程不互相通信,所以需要客戶端實現分布式,算法可以采用哈希一致性;
  • 安全-結構圖

技術分享

(圖六)

  1. 加密,對稱加密a=E(key) , b =D(key) a 是加密後值,E 是加密算法,key 是密鑰,b 是解密後值,D 是解密算法,可以看出加密解密的key 是一致的,共享的,所以只要知道key,雙方都可以進行加密解密,存在不安全因素,常用的對稱加密算法 DES; 數字簽名,即對內容進行加密的一個字符串(數字摘要),主要是為了保證內容的完整性及身份認證,常用的數字簽名的算法MD5;非對稱加密 c=E(key1),d=D(key2) c 是加密後值,key1 為私鑰,E 是加密算法,d 是解密後值,D 是解密算法,key2 為公鑰;可以看出加解密所使用的密鑰不同,當然也可以私用私鑰解密,公鑰加密,雙方的家解密的私鑰不公開,保證一定安全性,常見的非對稱加密算法RSA.
  2. 認證,常用的技術是使用Token或數字證書,Token 一般可以認為是數字簽名;數字證書可以認為是X.509(是證書的標準規範和解析);X.509 使用的是非對稱加密,客戶端根據證書中私鑰對特定內容進行加密,然後發送到服務端,服務端通過公鑰進行解密校驗認證;微信公眾號開發中涉及的微信端和我們服務端的雙向認證是Token,但采用的對稱加密.
  3. 授權,Net 裏面比較常用且規範的模式是RBAC,即基於角色控制授權,具體實現可以是 HttpModule + Attribute ; HttpModule 是對所有(一般不包括靜態資源)請求(線程)進行權限實例初始化並進行維持該會話,然後在每個需要授權的方法上標註Attribute及權限ID,Attribute 可以對CodeAccessSecurityAttribute進行擴展實現。
  4. SSO,一般我們有這樣的需求,比如有多個子系統,我們在一個系統中登陸成功後,其它系統就會共享此登陸狀態而不用再進行登陸,或者 我們有一個系統,但是有多端可以使用,比如PC端,App端,當在一個客戶機上登陸成功後,其它端或客戶機即刻自動退出避免多端操作,由此我們想到了肯定要把一端登陸成功的狀態進行保存並能夠在多端共享並能全局訪問,擁有此特點的想到了有分布式緩存。
  • Web後端框架-結構圖

技術分享

(圖七)

  1. 從最開始的Asp.net 到 現在的MVC3 以上版本,對開發越發靈活和成熟,MVC 是一種模式而非技術實現,此處具體技術不再闡述,可以重點關註下一些比較實用的高級特性,比如 Area , Filter 等。
  2. SignalR 是實時性框架,即主動通知,客戶端和服務端基於Http 建立長連接,當服務端有新的消息時可以主動推送到客戶端,常見客戶端訂閱可以基於JavaScript , C# 實現,Android 端也有叫SignalA但沒用過,SignalR 內部通信機制有4種,最好的是WebSocket , 客戶端可以指定采用何種通信機制,一般是自動切換模式,采用或切換到那種通信模式依據是客戶端的環境,理論上是越高配的性能就越好。
  3. 領域驅動設計,是架構模式,由此引申出來一套理論和方法,值對象像C#裏面的值類型也是不變性的,只是值對象是一個Class實例的對象,此Class 裏面有1個或多個屬性,為此我們必須覆寫它的Equal 和 GetHashCode 保證它的不可變,GetHashCode 是在用到 字典時進行驗證的,所以保險起見也要覆寫,實體對象 是唯一的,每次我們New 完都是使用的不同的對象實體,避免內存對象不一致的情況,所以需要給定一個唯一標識,即也要對Equal 和 GetHashCode 進行覆寫;聚合根是包含了值對象和實體對象,一個聚合根可以理解為一個領域內的模型,一般聚合根可以通過接口進行標註說明,而多個聚合根之間的會話通過領域事件來完成,領域事件的實現一般采用發布-訂閱模式,事件通過接口進行標註,發布者可以將訂閱者註入到容器中,容器的實現可以使用IOC框架如Unity,Autofac等,發布時根據不同的事件通知給不同的訂閱者進行觸發,由於事件通知的是另外一個聚合根,而很可能出現跨網絡邊界進行通知,依據CAP定律,會出現數據不一致的情況,所以使用Event Source 進行事件回放,可以理解為重試機制,Event Source 最好和當前聚合根在同一個事務中,進行事件回放可以另一個進程來完成,而另一個聚合根和Event Source (比如處理完成後修改其狀態)最好也是在一個事務中,這樣保證數據一致性,當然這裏面會存在事件重復發的可能,解決方法增加一個字段標識,當事件發出去後,即可將此標識標記為一個特定狀態比如發送中,下次調度就不再取發送中的事件即可;在高並發情況不建議使用事務所以另外一個Event Source 實現方法是 使用隊列;我們對操作進行抽象無非就是CRUD,而CUD 我們可以抽象成命令也就是寫操作,R 我們知道是查詢也就是讀操作,而CQRS就是命令查詢職責分離,一般用在讀寫分離的架構當中,寫操作一般使用的工作單元模式,而讀操作可以很靈活,可以使用原生的Ado.Net,而讀寫操作可能會存在一定的時間延遲,比如寫後異步處理,讀的時候可能還是臟數據,所以這個延遲或者要求不能延遲具體項目要具體設計和實現。
  • 多線程-結構圖

技術分享

(圖八)

  1. 為什麽會有線程,為了進程的穩定,一個進程死掉了,不能影響到其他進程,而為什麽會有進程,為了操作系統的穩定,不能在操作系統上操作一個記事本就把整個系統給搞崩潰了;線程是進程的邏輯單元,多個線程存在共用一個CPU的情況,會出現資源競爭,為了保證數據不錯亂,要對線程進行調度,每隔30毫秒進行一次切換,當然切換的過程存在性能磨損,也是為了用戶體驗和系統的可靠性犧牲一點性能,在我們多核時代這一點性能其實也是可以忽略的;
  2. 線程實現,Net 中關鍵字 Thread , 分前臺線程和後臺線程,通過IsBackgournd來標記,一般我們都使用後臺線程即異步,線程分優先級去處理,在調度過程中優先級高的優先處理,Net 中提供了比較豐富的線程API,比如Sleep,Join,Abort 就不再一一贅述了,線程池是為了解決線程來回切換損失的性能,它的主要作用就是 線程復用,當一個線程處理完成任務後重新回到線程池中,等待下次再用,有時我們異步處理數據時為了提高性能,對數據進行分組,然後分別放在不同的線程中去處理,比如 一個線程池中有5個線程,一批數據有 1000 條,則每一個線程處理200 條,當然可能會出現不整除的情況,(取模),當線程處理完成後重新回到池中等待下一個任務,線程池也有不足比如無法實時查看線程進度,不能反饋執行結果,這樣Task 就出現了,Task 是在線程池的基礎上進行了封裝,優化,Net 同樣也提供了豐富的API ,比如 Run, StartFactory, Wait,WaitAll,ContinueWith 等;
  3. 線程模式,有很多模式可以做為模板進行實現,Single Thread 即我們常見的 線程內加鎖的方式,多線程時通過加鎖來保證數據同步;Product-Customer 即我們發布訂閱的模式,生產者、消費者可以是多線程,而中間的數據容器可以使用內存列表,數據庫,或者隊列;Thread-Pre-Message , 即線程池模式,多個線程在池中進行生命周期的維護;Asyn和Asyn And CallBack 即是我們常用的Task 來異步處理和帶委托進行回調的方式,當然你也可以架構方面更高層面進行理解;

打的手有點疼了,就先寫到這吧,以上是自己的一點心得體會,如有不當還請指教,謝謝。

Net知識圖譜