單體與微服務
單體架構:項目所有功能都在一個 war 包 /jar 包里,像商城的訂單、庫存、會員、支付等服務,都打包在一起,部署在 Tomcat 服務器,數據存在 MySQL。
- 優點:開發簡單,易于理解和維護;部署方便,只需部署一個應用包;測試容易,在一個環境就能完成。
- 缺點:可維護性差,代碼量增大后,模塊間耦合度高,修改一處可能影響多處;擴展性受限,無法針對特定功能單獨擴展;可靠性低,一處故障就可能導致整個應用不可用 。
整個電商平臺的所有功能,像用戶瀏覽商品的前端界面、下單的訂單服務、查詢庫存的庫存服務、會員管理服務、支付服務,都被打包在一個 WAR 包里,部署在一臺 Tomcat 服務器上,數據都存在一個 MySQL 數據庫里。就好比開了一家小雜貨店,所有商品、收銀、庫存記錄都在一個小店鋪里,老板一個人能管過來,但要是店里某一處(比如收銀臺)出問題,整個店可能就沒法正常營業了,而且要是節日期間人特別多(訂單量暴增),想只增加收銀的 “能力” 也做不到,得把整個店鋪都擴大,成本高還麻煩。
集群級垂直化(業務系統級的拆分):為解決單體的問題,
- 橫向加服務器,變成集群;
- 縱向按業務拆,訂單、庫存、會員、支付各成一個 WAR 包,分別部署到不同的服務器,數據庫也對應拆成訂單庫、庫存庫等。這樣業務耦合度降低,單個業務出問題,不影響其他,也方便針對熱門業務(比如訂單量暴增)單獨擴展服務器。
- 優點:業務耦合度降低,單個業務系統故障對其他系統影響小;可以針對不同業務需求,獨立擴展資源,提高資源利用率;便于不同業務模塊的并行開發 。
- 缺點:系統間調用復雜度增加,需要考慮分布式通信和數據一致性問題;整體維護成本上升,要管理多個獨立部署的系統 。
為了應對業務增長,橫向增加了 Tomcat 服務器,變成多臺服務器的集群;縱向按業務把訂單、庫存、會員、支付這些業務拆分開,每個業務都有自己的 WAR 包,分別部署在不同的 Tomcat 服務器上,數據庫也對應拆成訂單庫、庫存庫、會員庫、支付庫。這就像把雜貨店變成了有多個分區的大超市,訂單區、庫存區、會員服務區、支付區各自獨立,一個區(比如訂單區)人多了,就可以單獨給這個區增加人手和場地(擴展服務器),一個區出問題,也不會影響其他區正常運作,但每個區內部還是有很多流程和功能混在一起。
SOA:核心是把通用、會被多個上層服務調用的業務,抽成獨立基礎服務,用 ESB(企業服務總線)連接。比如電商、運營系統都可能用訂單、庫存等服務。這樣解決了 “信息孤島”,讓共享業務能復用,像會員服務,電商和運營系統都能調用。[即電商、運營都能調用訂單、庫存服務】
- 優點:解決信息孤島問題,實現業務共享和復用,提高開發效率;業務靈活性高,能快速響應業務變化,便于新業務集成;提高系統的可擴展性,可獨立擴展單個服務 。
- 缺點:架構復雜,建設和維護成本高,需要專業的 ESB 等技術支撐;服務治理難度大,需要管理服務間的依賴、版本、安全等 。
電商平臺和運營系統(比如用于平臺運營管理的系統)都需要用到訂單、庫存、會員等服務,于是把這些通用的、會被多個上層系統調用的業務,抽取成獨立的基礎服務,通過 ESB(企業服務總線)來連接各個服務。比如電商系統要下單,運營系統要統計會員數據,都可以調用對應的訂單服務、會員服務。這樣就解決了不同系統之間的 “信息孤島” 問題,讓訂單、會員這些共享業務能被多個系統復用,不過服務之間的調用還是通過比較重的 ESB 來管理。
微服務:SOA 拆分的服務,還能按更細的業務功能拆,獨立部署,降低耦合、提升容錯。
比如訂單服務還能拆得更細。微服務用 API 網關統一管理服務,服務間用 REST API 通信。但服務拆得太細,數量變多,構建、發布、運維就更復雜了。可以說 SOA 是微服務的 “超集”,多個微服務能組成一個 SOA 服務。
- 優點:高內聚低耦合,每個微服務專注單一功能,開發、測試、維護更方便;擴展性強,可針對不同微服務進行單獨的資源擴展;技術選型靈活,不同微服務可根據需求選擇不同技術棧。
- 缺點:
* 服務數量多,管理和監控復雜,運維難度大;
* 分布式系統帶來的數據一致性、網絡延遲等問題,增加開發復雜性;部署成本高,要管理多個微服務的部署和運行 。
SOA 拆分出來的服務,又按照更細的業務功能進行拆分和獨立部署。比如訂單服務,還能拆分成訂單創建、訂單查詢等更細的微服務,每個微服務都有自己的數據庫(或數據存儲),通過 API 網關來統一管理服務,服務之間用 REST API 進行通信。就像把大超市的每個區又拆成了更小的獨立小店,訂單創建店、訂單查詢店等,各自獨立運營,一家店出問題,不影響其他店,而且能根據具體需求(比如訂單創建需求猛增),只給訂單創建的微服務增加資源。但這樣一來,服務數量變多,管理、發布、運維這些工作的復雜度也大大增加了。
微服務技術棧
- 配置管理:借助 Nacos、Config 等工具,統一管理微服務的配置信息,讓所有微服務能便捷獲取和使用配置,方便配置的集中維護與動態更新。
- 服務注冊列表:通過注冊中心,把所有微服務都注冊進去,形成一個服務清單,這樣其他微服務要調用服務時,能輕松找到目標服務。
- 網關處理請求:網關作為所有請求的入口,對進來的請求進行處理,可進行權限控制,判斷請求是否有權限;也能設置白名單,白名單內的請求可通行;還能對請求進行路由,決定哪些請求轉發到新系統。
- 熔斷限流:當某個微服務請求量過大,負載過高時,熔斷器會從關閉狀態轉為打開狀態,此時請求會被攔截,返回 “系統繁忙,請稍等” 的提示。之后熔斷器會每隔一段時間發送測試請求,查看服務是否恢復,此時處于半開狀態,若服務負載正常了,熔斷器就轉回關閉狀態,允許請求正常訪問。
- 負載均衡策略:運用 Ribbon 等工具,制定規則,決定請求分發到哪臺運行服務的機器上,讓多臺機器合理分擔請求壓力,避免單臺機器負載過高。
- 流處理與消息總線:在消息隊列之上添加了一層數據處理邏輯,原本是想實現用戶能無縫切換不同的消息隊列,但實際效果并不理想。
- 日志跟蹤:利用生成的 Trace ID,對微服務之間的調用鏈路進行跟蹤,像從訂單服務到物流服務等不同微服務的調用關系,都能通過這個 Trace ID 清晰呈現,便于排查問題。
CAP&BASE
名稱 | 詳細說明 |
---|---|
Consistency(一致性) | 在分布式系統中的所有數據備份,在同一時刻是否同樣的值。(等同于所有節點訪問同一份最新的數據副本) |
Availability(可用性) | 在集群中一部分節點故障后,集群整體是否還能響應客戶端的讀寫請求。(對數據更新具備高可用性) |
Partition tolerance(分區容忍性) | 一個分布式系統里面,節點組成的網絡本來應該是連通的。然而可能因為一些故障,使得有些節點之間不連通了,整個網絡就分成了幾塊區域,數據就散布在了這些不連通的區域中,這就叫分區。 當一個數據項只在一個節點中保存,那么分區出現后,和這個節點不連通的部分就訪問不到這個數據了,這時分區就是無法容忍的。 提高分區容忍性的辦法就是把數據項復制到多個節點上,容忍性就提高了。然而,要把數據復制到多個節點,就會帶來一致性的問題,而要保證一致,每次寫操作就都要等待全部節點寫成功,而這等待又會帶來可用性的問題 【復制多個副本,使得分區時保證該分區內部有足夠多的數據,但是多副本會導致數據一致性問題(副本越多數據一致性問題越大,比如一個副本必然沒有數據一致性問題) 解決:等待所有節點寫入數據成功,但是這個又會導致可用性問題(即等待時候集群不可用) 】 ![]() |
高可用性:如果說某個節點故障了但是集群整體還可以運行 ,則說明保持了高可用性
nacos-服務注冊與發現
服務發現
1.通過 nameServer 去調用某服務,而不是通過傳統的固定 ip,每個服務都自動將 ip 和端口發送到服務列表,要調取的服務去拉取該服務列表即可,然后通過 name 去調用,這樣子就可以做到自動刷新ip
客戶端服務發現
服務端服務發現
這里的話要考慮load balancer的高可用性(因為所有的請求都經過這個,所以可能會導致大量的請求同時打過來)
核心思想
無論是 OpenFeign 還是 Dubbo,在 Nacos 的幫助下,實現服務調用的核心思想都是一樣的:
- 服務注冊 (Provider):服務提供者啟動時,將自己的服務地址、接口信息等“注冊”到 Nacos 服務中心。就像在黃頁上登記自己的電話號碼和業務范圍。
- 服務發現 (Consumer):服務消費者啟動時,向 Nacos “訂閱”某個服務。Nacos 會將那個服務所有提供者的地址列表拉取到消費者本地。
- 服務調用 (Consumer -> Provider):消費者從本地的地址列表中,通過負載均衡算法(如輪詢)選擇一個提供者的地址,然后發起遠程調用。
OpenFeign + Nacos 的流程與原理
OpenFeign 是 聲明式 HTTP 客戶端,基于 RESTful 風格通信,需依賴 Spring Cloud 生態;Nacos 作為注冊中心,負責服務地址的動態管理。
1. Provider 端:服務注冊到 Nacos
- 步驟1:引入依賴
在 Spring Boot 項目的pom.xml
中添加 Nacos 服務發現依賴:
<dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
- 步驟2:配置 Nacos 地址
在application.yml
中指定服務名稱和 Nacos 地址:
spring:application:name: provider-service # 服務名(Consumer 端通過該名稱發現服務)cloud:nacos:discovery:server-addr: localhost:8848 # Nacos 服務端地址
- 步驟3:暴露服務接口
通過 Spring MVC 注解(如@RestController
)定義 HTTP 接口:
@RestController
@RequestMapping("/provider")
public class ProviderController {@GetMapping("/hello")public String hello() {return "Hello from Provider!";}
}
- 原理:服務啟動時,Nacos 客戶端會自動將服務名、IP、端口等元數據注冊到 Nacos Server,并定期發送心跳包維持“在線”狀態。
2. Consumer 端:通過 OpenFeign 調用 Provider
- 步驟1:引入依賴
除 Nacos 依賴外,需添加 OpenFeign 依賴:
<dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
- 步驟2:啟用 Feign 客戶端
在啟動類上添加@EnableFeignClients
注解:
@SpringBootApplication
@EnableDiscoveryClient // 啟用服務發現
@EnableFeignClients // 啟用 OpenFeign
public class ConsumerApplication {public static void main(String[] args) {SpringApplication.run(ConsumerApplication.class, args);}
}
- 步驟3:定義 Feign 客戶端接口
通過@FeignClient
聲明要調用的服務名,并映射 HTTP 路徑:
@FeignClient(name = "provider-service") // name 對應 Provider 的服務名
public interface ProviderFeignClient {@GetMapping("/provider/hello") // 映射 Provider 的 HTTP 接口【這里即是去調用provider服務的接口】String callHello();
}
- 步驟4:注入并調用 Feign 接口
在 Controller 中注入 Feign 接口,直接調用方法:
@RestController
@RequestMapping("/consumer")
public class ConsumerController {@Autowiredprivate ProviderFeignClient feignClient;@GetMapping("/call")public String callProvider() {return feignClient.callHello(); // 遠程調用 Provider 的 /provider/hello}
}
- 原理:
- 服務發現:Consumer 啟動時,Nacos 客戶端會拉取
<font style="color:#DF2A3F;">provider-service</font>
對應的所有實例地址(IP:端口)。 - 負載均衡:OpenFeign 底層集成了 Spring Cloud LoadBalancer(或 Ribbon),自動從實例列表中選擇一個(默認輪詢策略)。
- 動態代理:Feign 通過 JDK 動態代理為接口生成 HTTP 客戶端實現類,將方法調用轉化為 HTTP 請求(如
<font style="color:#DF2A3F;">GET http://{ip}:{port}/provider/hello</font>
),并解析響應。
- 服務發現:Consumer 啟動時,Nacos 客戶端會拉取
Dubbo + Nacos 的流程與原理
Dubbo 是 高性能 RPC 框架,基于二進制協議(如 Dubbo 協議)通信,天生支持服務治理;Nacos 作為注冊中心,負責服務地址的注冊與推送。
1. Provider 端:服務注冊到 Nacos
- 步驟1:引入依賴
在pom.xml
中添加 Dubbo 和 Nacos 整合依賴:
<dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-dubbo</artifactId>
</dependency>
<dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
- 步驟2:配置 Dubbo + Nacos
在application.yml
中指定 Dubbo 協議、Nacos 地址,并暴露服務:
spring:application:name: dubbo-providercloud:nacos:discovery:server-addr: localhost:8848
dubbo:protocol:name: dubbo # 通信協議(默認 Dubbo 協議,高性能二進制協議)port: -1 # 自動分配端口registry:address: nacos://localhost:8848 # 注冊中心地址scan:base-packages: com.example.provider.service # 掃描服務實現類的包
- 步驟3:定義并實現服務接口
服務接口需單獨定義(通常抽成公共模塊供給 Consumer 依賴):
// 公共接口模塊
public interface HelloService {String sayHello(String name);
}
// Provider 實現類
@Service(interfaceClass = HelloService.class) // Dubbo 注解,暴露服務
public class HelloServiceImpl implements HelloService {@Overridepublic String sayHello(String name) {return "Hello, " + name + "!";}
}
- 原理:服務啟動時,Dubbo 會**將接口的全限定名(如
com.example.HelloService
)與服務地址**注冊到 Nacos,并通過心跳維持存活狀態。
2. Consumer 端:通過 Dubbo 調用 Provider
- 步驟1:引入依賴
需添加 Dubbo、Nacos 依賴,以及 Provider 的接口依賴:
<dependency><groupId>com.example</groupId><artifactId>provider-interface</artifactId> <!-- 引入 Provider 的接口模塊 --><version>1.0.0</version>
</dependency>
<dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-dubbo</artifactId>
</dependency>
<dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
- 步驟2:配置 Dubbo + Nacos
在application.yml
中指定注冊中心和引用服務:
spring:application:name: dubbo-consumercloud:nacos:discovery:server-addr: localhost:8848
dubbo:registry:address: nacos://localhost:8848 # 注冊中心地址
- 步驟3:引用遠程服務
通過@Reference
注解注入 Provider 的接口
@RestController
@RequestMapping("/consumer")
public class ConsumerController {@Reference // Dubbo 注解,自動發現并引用服務private HelloService helloService;@GetMapping("/call")public String callProvider() {return helloService.sayHello("Dubbo"); // 遠程調用 Provider 的 sayHello 方法}
}
- 原理:
- 服務發現:Consumer 啟動時,Dubbo 客戶端從 Nacos 訂閱
<font style="color:#DF2A3F;">HelloService</font>
對應的所有 Provider 地址,并緩存到本地。 - 負載均衡:Dubbo 內置多種負載均衡策略(如隨機、輪詢、一致性哈希),從地址列表中選擇一個 Provider。
- RPC 調用:Dubbo 通過動態代理為
HelloService
生成客戶端 Stub,將方法調用轉化為二進制 RPC 請求(通過 Netty 發送),Provider 端的 Stub 接收請求后反射調用實際方法,再將結果返回。
- 服務發現:Consumer 啟動時,Dubbo 客戶端從 Nacos 訂閱
dubbo的工作流程
1. 項目整體結構
provider-interface
模塊:存放服務接口定義。provider
模塊:實現服務接口,并注冊服務到 Nacos。consumer
模塊:引入接口模塊,調用 Provider 提供的服務。
2. provider-interface
/provider-api 模塊
定義服務接口
在 src/main/java/com/example/service
目錄下創建 HelloService
接口:
package com.example.service;public interface HelloService {String sayHello(String name);
}
使用 Maven 構建該模塊,生成 Jar 包供其他模塊依賴。pom.xml
文件內容如下:
<project xmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><groupId>com.example</groupId><artifactId>provider-interface</artifactId><version>1.0.0</version><dependencies><!-- 可以根據需要添加其他公共依賴 --></dependencies>
</project>
3. provider
/provider-application 模塊
引入依賴
在 pom.xml
中引入 provider-interface
模塊以及 Dubbo 和 Nacos 相關依賴:
<project xmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><groupId>com.example</groupId><artifactId>provider</artifactId><version>1.0.0</version><dependencies><dependency><groupId>com.example</groupId><artifactId>provider-interface</artifactId><version>1.0.0</version></dependency><dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-dubbo</artifactId></dependency><dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency></dependencies>
</project>
配置文件
在 src/main/resources/application.yml
中進行配置:
spring:application:name: dubbo-providercloud:nacos:discovery:server-addr: localhost:8848 # Nacos 服務地址
dubbo:protocol:name: dubboport: -1 # 自動分配端口registry:address: nacos://localhost:8848 # 注冊到 Nacosscan:base-packages: com.example.provider.service # 掃描服務實現類的包
實現服務接口
在 src/main/java/com/example/provider/service
目錄下創建 HelloServiceImpl
類,實現 HelloService
接口:
package com.example.provider.service;import com.alibaba.dubbo.config.annotation.Service;
import com.example.service.HelloService;@Service(interfaceClass = HelloService.class)
public class HelloServiceImpl implements HelloService {@Overridepublic String sayHello(String name) {return "Hello, " + name + "! From Dubbo Provider";}
}
啟動類
在 src/main/java/com/example/provider
目錄下創建 ProviderApplication
啟動類:
package com.example.provider;import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;@SpringBootApplication
@EnableDiscoveryClient
public class ProviderApplication {public static void main(String[] args) {SpringApplication.run(ProviderApplication.class, args);}
}
4. consumer
模塊
引入依賴
在 pom.xml
中引入 provider-interface
模塊以及 Dubbo 和 Nacos 相關依賴:
<project xmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><groupId>com.example</groupId><artifactId>consumer</artifactId><version>1.0.0</version><dependencies><dependency><groupId>com.example</groupId><artifactId>provider-interface</artifactId><version>1.0.0</version></dependency><dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-dubbo</artifactId></dependency><dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency></dependencies>
</project>
配置文件
在 src/main/resources/application.yml
中進行配置:
spring:application:name: dubbo-consumercloud:nacos:discovery:server-addr: localhost:8848 # Nacos 服務地址
dubbo:registry:address: nacos://localhost:8848 # 從 Nacos 發現服務
調用服務
在 src/main/java/com/example/consumer/controller
目錄下創建 ConsumerController
類,通過 @Reference
注解注入 HelloService
接口:
package com.example.consumer.controller;import com.alibaba.dubbo.config.annotation.Reference;
import com.example.service.HelloService;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;@RestController
public class ConsumerController {@Referenceprivate HelloService helloService;@GetMapping("/hello/{name}")public String sayHello(@PathVariable String name) {return helloService.sayHello(name);}
}
啟動類
在 src/main/java/com/example/consumer
目錄下創建 ConsumerApplication
啟動類:
package com.example.consumer;import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;@SpringBootApplication
@EnableDiscoveryClient
public class ConsumerApplication {public static void main(String[] args) {SpringApplication.run(ConsumerApplication.class, args);}
}
5. 運行與測試
- 啟動 Nacos 服務。
- 啟動
provider
模塊的ProviderApplication
類,服務會注冊到 Nacos。 - 啟動
consumer
模塊的ConsumerApplication
類,訪問http://localhost:8080/hello/John
(假設 Consumer 應用端口是 8080 ,根據實際配置而定),會返回Hello, John! From Dubbo Provider
,說明 Consumer 成功通過接口模塊調用到了 Provider 提供的服務。
通過上述代碼結構和實現,可以清晰看到 Dubbo 中 Provider 和 Consumer 基于接口模塊的設計與調用流程。
總結
- OpenFeign:通過“Nacos 注冊發現 + HTTP 協議 + 動態代理”實現聲明式遠程調用,適合簡單場景,整合成本低。
- Dubbo:通過“Nacos 注冊發現 + RPC 協議 + 服務治理”實現高性能調用,適合復雜微服務場景,需關注接口定義和二進制協議。
兩者的核心思路一致:服務注冊到 Nacos → 消費者發現服務 → 動態代理封裝網絡通信,只是在“通信協議”和“框架封裝程度”上有差異。