三層架構一般包括:表示層、業務邏輯層以及數據訪問層。java
雖然傳統的三層架構幫助咱們將應用在邏輯上分紅了三層,但它並非物理上的分層。這也意味着,即使咱們將應用架構分紅了所謂的三層,通過開發團隊對不一樣層的代碼實現,經歷編譯(非靜態語言則忽略編譯階段)、打包、部署後,不考慮負載均衡以及水平擴展的狀況,最終仍是運行在同一臺機器的同一進程中。對於這種功能集中、代碼和數據中心化、一個發佈包、部署後運行在同一進程的應用程序,咱們一般稱之爲單塊架構應用。linux
提倡將單一應用程序劃分紅一組小的服務,服務之間互相協調、互相配合,爲用戶提供最終價值。每一個服務運行在其獨立的進程中,服務與服務之間採用輕量級的通訊機制互相溝通(一般是基於HTTP的RESTful 服務)。每一個服務都圍繞着具體的業務進行構建,而且可以被獨立地部署到生產環境、類生產環境中。另外,應儘可能避免統一的、集中式的服務管理機制,對具體的一個服務而言,應根據業務上下文,選擇合適的語言、工具對其進行構建。算法
(1)如何拆分微服務?多微纔夠微小緩存
代碼行數網絡
重寫時間架構
團隊以爲好纔是好:業務獨立性、團隊自主性負載均衡
(2)單一職責框架
單一職責(高內聚、低耦合,不一樣的服務經過「管道」的方式靈活組合,從而構建出龐大的系統。運維
(3)輕量級通訊dom
指的是與語言無關、平臺無關的交互方式。所熟知的REST是實現服務之間相互協做的輕量級通訊機制之一。團隊能夠選擇更適合的語言、工具或者平臺來開發服務自己。
(4)獨立性
獨立性是指在應用的交付過程當中,開發、測試以及部署的獨立性。
對於每一個服務,都有獨立的代碼庫。當對當前服務的代碼進行修改後,並不會影響其餘服務。從代碼庫的層面而言,服務與服務是個隔離的。
(5)進程隔離
微服務架構實際上是將單一的應用程序劃分紅一組小的服務,每一個服務都是具備業務屬性的獨立單元,同時可以被獨立開發、獨立運行、獨立測試以及獨立部署。
開源的應用容器引擎,容許開發者將他們的應用以及依賴包打包到一個可移植的容器中,而後發佈到任何裝有Docker的linux機器上。
Docker的出現,有效的解決了微服務架構下,服務粒度細、服務數量多所致使的開發環境搭建、部署以及運維成本高的問題。同時利用Docker的容器化技術,可以實如今一個節點上運行成百甚至上千的Docker容器,每一個容器都能獨立的運行一個服務,所以極大的下降了隨着微服務數量增多所致使的節點數量增多的成本。
(1)服務做爲組件
若是把微服務做爲組件,則同傳統使用組件方式最大的區別是,組件能夠被獨立部署。
顯著的優點在於:能以鬆散的服務方式,構建可獨立部署的模塊化應用。
(2)圍繞業務組織團隊
(3)關注產品而非項目
(4)技術多樣性
(5)業務數據獨立
(6)基礎設施自動化
(7)演進式架構
不斷調整軟件的架構,將不須要的服務(業務)拋棄,將須要的服務升級,並採用合適的技術或者工具不斷優化架構,保持其處於一個不斷演進的狀態。
(1)性能
分佈式系統因爲組件與組件的調用是跨進程、跨網絡的調用,所以必然要考慮網絡延遲以及帶寬的影響。
(2)可靠性
組件間的遠程調用可能失敗,還有可能出現潛在故障點。
(3)異步
跨網絡的調用,須要考慮異步的通訊機制。異步通訊大大增長了功能實現的複雜度。
(4)數據一致性
分佈式事務管理成本更高。
(5)工具
IDE等工具須要爲開發分佈式系統提供足夠的支持。
(6)運維成本
每一個服務都須要獨立的配置、部署、監控、日誌收集等,成本呈指數級增加。
(7)部署自動化
如何有效地構建自動化部署流水線,下降部署成本,提升部署頻率,是微服務架構下須要面臨的一個挑戰。
(8)DevOps與組織架構
(9)服務間的依賴測試
(10)服務間的依賴管理。
一段話道出微服務的本質:
微服務架構將一個應用拆分紅多個獨立的、具備業務屬性的服務,每一個服務運行在不一樣的進程中,服務與服務之間經過輕量級的通訊機制相互協做、相互配合,從而爲終端用戶提供業務價值。同時每一個服務能夠根據業務邏輯,採用不一樣的語言、框架、工具以及存儲技術來解決業務問題。所以微服務架構強調的是一種獨立開發、獨立測試、獨立部署、獨立運行的高度自治的架構模式,也是一種更靈活、更開放、更鬆散的演進式架構。
超時與重試是保護系統、下降微服務失敗率的重要手段(如Spring Cloud中參考CSDN-SpringCloud之Feign、Ribbon設置超時時間和重試機制的總結
),經過設置合理的超時時間,防止出現某服務的依賴服務超時時間過長且響應慢,慢請求累積引發連鎖反應,甚至形成應用雪崩,超時以後應該有相應的策略來處理,常見的策略有重試(等一下子再試、嘗試其它分組和機房服務,重試算法能夠採用如指數退避算法,由於網絡波動、系統資源分配的不肯定性、跨機房的請求等緣由,都會或多或少的致使一小部分請求的失敗,而這部分失敗的請求中,又有大部分請求其實只須要簡單重試幾回便可成功)、摘掉不存活節點(負載均衡、分佈式緩存場景)、降級託底(返回歷史/靜態/緩存數據)、等待頁或錯誤頁。讀服務自然適合重試,大多數寫服務重試就須要考慮冪等問題。
關於冪等分析與解決參考:冪等
降級、限流、熔斷、隔離
服務支持local、share、microservice三種工做模式,以及Api支持client和server兩種調用模式。
假設如今關注公共服務如登陸服務的版本,那麼本地起該服務便可,註冊中心會優先使用本地起的登陸服務。具體須要實現Eureka的getFilteredListOfServer接口。
/** * This interface allows for filtering the configured or dynamically obtained * List of candidate servers with desirable characteristics. * * @author stonse * * @param <T> */ public interface ServerListFilter<T extends Server> { public List<T> getFilteredListOfServers(List<T> servers); }
public class LocalDebugServerListFilter implements ServerListFilter { @Override public List getFilteredListOfServers(List servers) { boolean hasOwnService = false; Object ownServer = null; //獲取本地ip配置 List<String> ips = getLocalIPs(); for(Object server : servers){ DiscoveryEnabledServer domainExtractingServer = (DiscoveryEnabledServer)server; if(ips.contains(domainExtractingServer.getInstanceInfo().getHostName())){ hasOwnService = true; ownServer = server; break; } } if(!hasOwnService){ return servers; } else { List result = new ArrayList<>(); result.add(ownServer); return result; } } private List<String> getLocalIPs() { List<String> localIPs = new ArrayList<>(); try { Enumeration<NetworkInterface> netInterfaces = NetworkInterface.getNetworkInterfaces(); while (netInterfaces.hasMoreElements()) { NetworkInterface ni = netInterfaces.nextElement(); Enumeration<InetAddress> ia = ni.getInetAddresses(); while (ia.hasMoreElements()) { InetAddress ip = ia.nextElement(); if(validIPv4Address(ip.getHostAddress())){ localIPs.add(ip.getHostAddress()); } } } } catch (SocketException e) { e.printStackTrace(); } return localIPs; } private boolean validIPv4Address(String ip) { Pattern pattern = Pattern.compile("^\\d{1,3}.\\d{1,3}.\\d{1,3}.\\d{1,3}$"); Matcher matcher = pattern.matcher(ip); return matcher.matches(); } }
參考資料
《微服務架構與實踐》王磊