一、基本概述
在實際項目中,選擇哪種架構需要根據具體的需求、團隊能力和技術棧等因素綜合考慮。
單體架構(Monolithic Architecture)
????????單體架構是一種傳統的軟件架構風格,將整個應用程序構建為一個單一的、不可分割的單元。在這種架構中,所有的功能模塊(如用戶管理、訂單處理、支付等)都打包在一個大型的、統一的代碼庫中,并且部署為一個單獨的進程。
微服務架構(Microservices Architecture)
????????微服務架構是一種將復雜應用程序分解為一組小型、獨立服務的架構風格。每個微服務都圍繞特定的業務功能構建,運行在其獨立的進程中,并通過輕量級的通信機制(通常是HTTP/RESTful API、消息隊列等)協同工作。例如,一個電商系統可以被拆分為用戶服務(處理用戶注冊、登錄等功能)、訂單服務(負責訂單創建、查詢等)、庫存服務(管理商品庫存)等多個微服務。
總結
-
單體架構適用于小型或簡單的應用程序,開發團隊規模較小,且對系統的擴展性和靈活性要求不高。
-
微服務架構適用于復雜、大型的應用程序,特別是需要高可擴展性、靈活性和快速迭代的場景。開發團隊需要具備分布式系統的開發和運維能力。
一、單體架構(Monolithic Architecture)
(一)定義
單體架構是一種傳統的軟件架構風格,將整個應用程序構建為一個單一的、不可分割的單元。在這種架構中,所有的功能模塊(如用戶管理、訂單處理、支付等)都打包在一個大型的、統一的代碼庫中,并且部署為一個單獨的進程。
(二)特點
-
集中式開發
單體架構的代碼通常集中在一個大型的代碼庫中,所有功能模塊共享相同的代碼庫。開發團隊需要在同一個代碼庫中協作,進行功能開發、修改和維護。 -
統一部署
整個應用程序作為一個整體進行部署。每次更新或修復任何功能模塊時,都需要重新打包并部署整個應用程序。例如,如果只是修復了一個小的用戶界面問題,也需要重新部署整個系統。 -
緊密耦合
功能模塊之間通常存在緊密的依賴關系。一個模塊的變更可能會影響到其他模塊,因此在修改代碼時需要非常謹慎,以避免引入新的問題。 -
可擴展性有限
單體架構的擴展通常是通過增加服務器的性能(如CPU、內存)來實現的,這種擴展方式稱為垂直擴展。當系統負載增加時,垂直擴展的代價會越來越高,且存在硬件資源的瓶頸。
(三)優點
-
簡單易理解
對于小型或簡單的應用程序,單體架構的結構相對簡單,開發和部署過程也較為直觀。開發人員可以快速上手,不需要復雜的架構設計和分布式系統的知識。 -
開發工具友好
大多數開發工具(如IDE)對單體架構的支持較好,調試和測試也相對容易。開發人員可以在一個統一的環境中進行開發,不需要考慮跨服務的調試問題。 -
事務管理簡單
在單體架構中,事務管理相對簡單,因為所有的功能模塊都在同一個進程中運行,可以通過傳統的數據庫事務來保證數據的一致性。
(四)缺點
-
可維護性差
隨著應用程序的規模增大,代碼庫會變得龐大且復雜,開發和維護成本會急劇上升。新功能的添加或現有功能的修改可能會引入新的問題,影響整個系統的穩定性。 -
部署困難
由于每次更新都需要重新部署整個應用程序,部署過程可能會變得繁瑣且耗時。同時,頻繁的部署也可能對系統的穩定性產生影響。 -
技術棧受限
單體架構通常使用單一的技術棧,很難引入新的技術或框架。一旦選擇了某種技術,后續很難進行技術的替換或升級。
二、微服務架構(Microservices Architecture)
(一)定義
微服務架構是一種將復雜應用程序分解為一組小型、獨立服務的架構風格。每個微服務都圍繞特定的業務功能構建,運行在其獨立的進程中,并通過輕量級的通信機制(通常是HTTP/RESTful API、消息隊列等)協同工作。
(二)特點
-
獨立性
????????獨立開發:每個微服務可以由不同的團隊獨立開發,團隊之間只需要通過定義好的接口進行協作。例如,用戶服務團隊和訂單服務團隊可以分別開發和維護自己的服務,而不需要相互干擾。
????????獨立部署:每個微服務可以獨立部署,更新或擴展一個微服務不會影響到其他微服務。例如,當訂單服務需要更新訂單處理邏輯時,只需要重新部署訂單服務相關的代碼和配置。
????????獨立擴展:可以根據每個微服務的負載情況獨立擴展。如果用戶服務的訪問量突然增加,可以單獨增加用戶服務的實例數量,而不需要對其他服務進行擴展。
-
容錯性
單個微服務的故障不會導致整個系統崩潰。例如,如果庫存服務暫時不可用,訂單服務仍然可以正常接收訂單,只是可能無法實時更新庫存信息,但系統可以設計為在這種情況下記錄訂單并等待庫存服務恢復后再處理庫存更新。 -
可維護性高
由于每個微服務都相對獨立,代碼庫較小,開發和維護成本相對較低。新功能的添加或現有功能的修改不會對整個系統產生太大的影響。
(三)優點
-
敏捷開發
微服務架構支持敏捷開發,不同的團隊可以并行開發不同的微服務,加快開發速度。同時,獨立部署的特點也使得新功能可以快速上線。 -
可擴展性強
微服務架構支持水平擴展,可以根據每個微服務的負載情況獨立擴展。例如,通過增加微服務實例的數量來應對高并發場景。 -
技術靈活性
開發團隊可以自由選擇最適合的技術棧來開發每個微服務,便于引入新技術和框架。 -
容錯性好
單個微服務的故障不會導致整個系統崩潰,系統的整體可用性更高。
(四)缺點
-
分布式系統復雜性
微服務架構本質上是一個分布式系統,需要處理分布式事務、服務間通信、數據一致性等問題。例如,一個業務流程可能涉及多個微服務的調用,需要解決分布式事務問題。 -
部署和運維復雜
微服務架構需要管理多個獨立的服務,部署和運維的復雜度會增加。例如,需要管理每個微服務的配置、監控、日志等。 -
性能開銷
由于微服務之間通過網絡通信(如HTTP/RESTful API、消息隊列),可能會引入額外的性能開銷。例如,服務間的調用延遲可能會比單體架構中的方法調用延遲更高。 -
數據一致性挑戰
微服務架構中,數據通常分散在不同的微服務中,需要解決數據一致性問題。例如,訂單服務和庫存服務可能分別存儲訂單信息和庫存信息,需要通過分布式事務或事件驅動的方式來保證數據的一致性。
三、單體架構與微服務架構的對比
特點 | 單體架構 | 微服務架構 |
開發方式 | 集中式開發,所有功能模塊在同一個代碼庫中 | 分布式開發,每個微服務獨立開發 |
部署方式 | 統一部署,更新需要重新部署整個應用程序 | 獨立部署,更新或擴展一個微服務不影響其他微服務 |
技術棧 | 通常使用單一技術棧 | 可以使用多種技術棧 |
可維護性 | 小型應用簡單,大型應用復雜且難以維護 | 可維護性高,每個微服務相對獨立 |
擴展性 | 垂直擴展,受限于硬件資源 | 水平擴展,可以根據負載獨立擴展每個微服務 |
容錯性 | 一個模塊的故障可能導致整個系統崩潰 | 單個微服務的故障不會影響整個系統 |
事務管理 | 傳統數據庫事務管理簡單 | 需要分布式事務管理 |
性能開銷 | 方法調用性能高,沒有額外的網絡通信開銷 | 服務間通信可能引入額外的性能開銷 |
開發工具 | 開發工具友好,調試和測試簡單 | 開發工具支持有限,調試和測試復雜 |
適用場景 | 小型或簡單的應用程序 | 復雜、大型的應用程序,需要高可擴展性和靈活性 |
SpringClound
?官網地址:??Spring Cloud
SpringCloud是目前國內使用最廣泛的微服務框架。集成了各種微服務功能組件,并基于SpringBoot實現了這些組件的自動裝配,從而提供了良好的開箱即用體驗。
微服務拆分
原則:
微服務通信
當我們將微服務拆分后,各自的資源會存在獨立管理的狀態,當某個服務需要調用另一個服務的資源的時候。我們可以通過 網絡通信 的方式,進行各自的資源交互。
Spring RestTemplate工具
Spring給我們提供了一個RestTemplate工具,可以方便的實現Http請求的發送。使用步驟如下:
1、注入RestTemplate到Spring容器
@Beanpublic RestTemplate restTemplate() {return new RestTemplate();}
2、簡單的Get引用獲取交換資源
@RequiredArgsConstructor
public class demoServiceImpl extends ServiceImpl<demoMapper, demo> implements IdemoService {private final RestTemplate restTemplate;private void demo(){// 2.1 . 利用 RestTemplate 發起 http 請求,得到 http 的響應ResponseEntity<List<ItemDTO>> response = restTemplate.exchange("http://localhost:8081/items?ids={ids}",HttpMethod.GET,null,new ParameterizedTypeReference<List<ItemDTO>>() {},Map.of("ids", CollUtil.join(itemIds, ",")));// 2.2 . 解析響應if(!response.getStatusCode().is2xxSuccessful()){
// 響應失敗,直接結束return ;}List<ItemDTO> items = response.getBody();if (CollUtils.isEmpty(items)) {return;}// 3.轉為 id 到 item的mapMap<Long, ItemDTO> itemMap = items.stream().collect(Collectors.toMap(ItemDTO::getId, Function.identity()));// 4.寫入vofor (CartVO v : vos) {ItemDTO item = itemMap.get(v.getItemId());if (item == null) {continue;}v.setNewPrice(item.getPrice());v.setStatus(item.getStatus());v.setStock(item.getStock());}}
}
服務治理-nacos
微服務遠程通信問題
????????當我們多個服務運行后,其中某個服務掛機,而我們寫的請求URL寫死了,導致獲取不到資源。又亦或重啟服務后,端口方面有變動,我們又得要從源碼中更改,這是不理想的情況的。
注冊中心原理
什么是注冊中心
????????在分布式系統中,服務會有很多實例,這些實例分布在不同的機器上。注冊中心就像是一個“服務中心”,它記錄了所有服務實例的信息,包括服務名稱、實例的地址(IP和端口)等。當一個服務(客戶端)需要調用另一個服務(服務提供者)時,它會先去注冊中心查詢服務提供者的地址,然后才能進行通信。
Nacos 注冊中心
Nacos是目前國內企業中占比最多的注冊中心組件。它是阿里巴巴的產品,目前已經加入SpringCloud Alibaba中。(官網:Nacos官網| Nacos 配置中心 | Nacos 下載| Nacos 官方社區 | Nacos 官網)
Nacos搭建步驟
1、配置MySQL表
我們基于Docker來部署Nacos的注冊中心,首先我們要準備MySQL數據庫表,用來存儲Nacos的數據。官方Nacos-SQL表(mysql-schema.sql)
這里注意執行腳本前,自定義一個自己的數據庫,同時添加用戶信息(密碼:nacos)
CREATE DATABASE nacos;
USE nacos;
-- 最后添加用戶信息
INSERT INTO users (username, password, enabled) VALUES ('nacos', '$2a$10$EuWPZHzz32dJN7jexM34MOeYirDdFAZm2kuWj7VEOJhhZkDrxfvUu', TRUE);
INSERT INTO roles (username, role) VALUES ('nacos', 'ROLE_ADMIN');
2、dockers部署
- 拉取鏡像
docker pull nacos/nacos-server:v2.5.1-slim
- 通過 SSL 執行生成隨機base64的?NACOS_AUTH_TOKEN
openssl rand -base64 32
- 一鍵命令創建并啟動鏡像(系統參數 | Nacos 官網)
docker run -d \
--name nacos \
-e MODE=standalone \
-e PREFER_HOST_MODE=hostname \
-e NACOS_AUTH_ENABLE=true \
-e SPRING_DATASOURCE_PLATFORM=mysql \
-e MYSQL_SERVICE_HOST=localhost \
-e MYSQL_SERVICE_PORT=3306 \
-e MYSQL_SERVICE_USER=root \
-e MYSQL_SERVICE_DB_NAME=nacos \
-e MYSQL_SERVICE_PASSWORD=123456 \
-e MYSQL_SERVICE_DB_PARAM='characterEncoding=utf8&connectTimeout=10000&socketTimeout=30000&autoReconnect=true&useSSL=false&allowPublicKeyRetrieval=true&serverTimezone=Asia/Shanghai' \
-e NACOS_AUTH_ENABLE=true \
-e NACOS_AUTH_TOKEN='eZCEZeAxiQvbsTJMtc518ocS4vtiEwBTDqGVvk3FPww=' \
-e NACOS_AUTH_TOKEN_EXPIRE_SECONDS=18000 \
-e NACOS_AUTH_IDENTITY_KEY=nacos \
-e NACOS_AUTH_IDENTITY_VALUE=123456 \
-p 8848:8848 \
-p 9848:9848 \
-p 9849:9849 \
nacos/nacos-server:v2.5.1-slim
啟動成功后,通過 logs 可以看到
訪問??http://localhost:8848/nacos/,就進入到需要登錄的頁面。
根據我們配置的MySQL默認賬號密碼都為:Nacos。登錄即可看到頁面
服務注冊
1、引入依賴
父工程:
<!--spring cloud-->
<dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-dependencies</artifactId><version>${spring-cloud.version}</version><type>pom</type><scope>import</scope>
</dependency>
<!--spring cloud alibaba-->
<dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-alibaba-dependencies</artifactId><version>${spring-cloud-alibaba.version}</version><type>pom</type><scope>import</scope>
</dependency>
子工程:
<!--nacos 服務注冊發現-->
<dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
2、配置 yaml 文件
spring:cloud:nacos:discovery:server-addr: localhost:8848username: nacospassword: nacos
3、啟動服務
可以看到服務注冊成功。
服務發現
@Autowired
private RestTemplate restTemplate;
@Autowired
private DiscoveryClient discoveryClient;private void DemoItems(){// 根據服務名稱獲取服務的實例列表List<ServiceInstance> instances = discoveryClient.getInstances("item-service");if(CollUtils.isEmpty(instances)){
// 沒有可用的服務,直接結束return ;}// 使用 cn.hutool.core.util.RandomUtil; 工具包隨機獲取一個實例ServiceInstance serviceInstance = instances.get(RandomUtil.randomInt(instances.size()));
// 獲取服務的 URI : http://localhost:8081URI uri = serviceInstance.getUri();// 2.1 . 利用 RestTemplate 發起 http 請求,得到 http 的響應ResponseEntity<List<ItemDTO>> response = restTemplate.exchange(uri + "/items?ids={ids}",HttpMethod.GET,null,new ParameterizedTypeReference<List<ItemDTO>>() {},Map.of("ids", CollUtil.join(itemIds, ",")));// 2.2 . 解析響應if(!response.getStatusCode().is2xxSuccessful()){
// 響應失敗,直接結束return ;}List<ItemDTO> items = response.getBody();
}
通過測試可以發現,實現了負載均衡。
OpenFeign
Openfeign是一個聲明式的http客戶端,是SpringCloud在Eureka公司開源的Feign基礎上改造而來。官方地址: https://github.com/openFeign/feign
其作用就是基于SpringMVC的常見注解,幫我們優雅的實現http請求的發送。
快速入門
微服務通信刨析
引入依賴
<!--openFeign--><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-openfeign</artifactId></dependency><!--負載均衡器--><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-loadbalancer</artifactId></dependency>
通過@EnableFeignClients注解,啟用OpenFeign功能
@EnableFeignClients // 開啟 openFeign 請求工具
@MapperScan("com.angindem.mapper")
@SpringBootApplication
public class CartApplication {public static void main(String[] args) {SpringApplication.run(CartApplication.class, args);}@Beanpublic RestTemplate restTemplate() {return new RestTemplate();}
}
編寫FeignClient
package com.angindem.clien;
import com.angindem.domain.dto.ItemDTO;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import java.util.Collection;
import java.util.List;
@FeignClient("item-service")
public interface ItemClient {@GetMapping("/items")List<ItemDTO> queryItemByIds(@RequestParam("ids") Collection<Long> ids);
}
調用
@Autowired
private ItemClient itemClient;
private void DemoItems(){List<ItemDTO> items = response.getBody();
}
連接池
OpenFeign對Http請求做了優雅的偽裝,不過其底層發起http請求,依賴于其它的框架。這些框架可以自己選擇,包括以下三種:
- HttpURLconnection:默認實現,不支持連接池
- Apache HttpClient:支持連接池
- OKHttp:支持連接池
具體源碼可以參考FeignBlockingLoadBalancerClient類中的delegate成員變量。
引入依賴
這里使用 OKhttp
<!--OK http 的依賴 -->
<dependency><groupId>io.github.openfeign</groupId><artifactId>feign-okhttp</artifactId>
</dependency>
啟動連接池
yaml配置
feign:okhttp:enabled: true
最佳實踐
方案一:
優點:
-
模塊化:每個服務模塊獨立開發、部署和擴展,便于維護和升級。
-
解耦:服務之間通過定義好的接口進行通信,降低了耦合度。
-
可擴展性:可以根據需要獨立擴展各個服務模塊,提高系統的可擴展性。
-
靈活性:可以根據業務需求靈活組合不同的服務模塊,實現復雜的業務邏輯。
缺點:
-
復雜性:
-
管理多個服務增加了系統的復雜性。
-
需要額外的工具和服務來管理服務間的通信和數據一致性。
-
這種微服務架構適用于大型分布式系統,可以提高系統的可維護性、可擴展性和靈活性。
方案二:
優點:
- ??? 集中化API管理
??????? hm-api作為API網關,統一管理對外的API接口,簡化了客戶端與后端服務的交互。
- ??? 解耦:
??????? 服務之間通過API網關進行通信,降低了服務之間的直接依賴,提高了系統的靈活性和可維護性。
缺點:
- ??? 復雜性增加:
??????? 引入API網關增加了系統的復雜性,需要額外的配置和管理。
- ??? 性能開銷:
??????? API網關可能會引入額外的網絡延遲,影響系統性能。
- ??? 單點故障:
??????? 如果API網關設計不當,可能會成為系統的單點故障,影響整體可用性。
????????總的來說,引入API網關可以帶來許多好處,如集中化管理、解耦,但也需要注意其帶來的復雜性和性能開銷。在設計和實施API網關時,需要權衡這些因素,確保系統的穩定性和可維護性。