目錄
1 單體架構
2?集群與分布式
3 微服務架構
4 Spring Cloud
5?Spring Cloud環境和工程搭建
5.1 服務拆分
5.2 示例
5.2.1 數據庫配置
5.2.2 父子項目創建
5.2.3 order_service子項目結構配置
5.2.4 product_service子項目結構配置
5.2.5 服務之間的遠程調用
5.3 RestTemplate
5.3.1 REST
5.3.2 RESTful
1 單體架構
????????單體架構的系統是指所有功能實現全在一個項目中,該項目部署在一臺主機上。但是隨著項目規模增大,就會暴露出許多問題:
????????1.服務器壓力變大,負載變高,因為用戶數增多,所有的訪問均在一個機器上。
????????2.項目功能之間耦合程度高,業務邏輯復雜,任何一個修改都會導致項目重構、重新打包、部署。
????????3.任何一個小問題都會導致整個項目崩潰。
????????要解決這些問題,從兩方面考慮:
2?集群與分布式
????????橫向:增加機器,多個機器分擔壓力。又稱為集群架構。
????????縱向:項目解耦合,即分割為多個子項目。又稱為垂直架構或分布式架構。
????????上述描述也發現集群和分布式存在一些不同和聯系:
????????1.集群是每個機器都做同樣的事情;分布式是每個機器都做不同的事情。
????????2.集群是每個節點功能相同,可以相互替代;分布式是每個節點業務不同,無法相互替代,某個節點宕機,其負責的業務就會無法訪問。
????????3.集群和分布式往往相互配合使用,分布式的子系統可以部署在集群上,因此二者不做特別嚴格的區分。
3 微服務架構
????????微服務就是在分布式的基礎上,再把子系統拆成更加細粒度、更微小的服務。這里的服務通常只專注做一個功能,因此微服務是經過良好架構設計的分布式架構方案(反之不成立)。
????????由于微服務是在分布式基礎上演化的,因此微服務通常是用來解決分布式架構的一些問題的。
優點:
????????1.易于開發和維護,每個服務體量小,功能清晰,開發和維護成本低。
????????2.易于擴展,由于整個系統被分成多個獨立的系統,因此擴展其它服務更方便。
????????3.容錯性高,單個服務出現問題,不會影響到整體服務。
????????4.技術更靈活,不同服務可以采用不同的技術方案,比如不同的語言、框架等等。
缺點:
????????1.服務之間關系復雜,一個服務的修改需要考慮對其它服務的影響。
????????2.運營成本高,不同服務需要的運行環境不同,不同服務需要的打包、構建等流程也不同,同時因為集群可用性的保證,使得運營成本更高。
????????3.服務監控更困難,單體架構只需對整個項目進行監控,而微服務不僅需要對整個服務鏈路監控,更需要監控每個服務實例。
????????4.開發和測試更困難,由于不同服務的技術選型不一樣,加上服務之間依靠網絡通信,因此對于各個服務的聯合開發和測試更困難。
????????5.服務可用性的保證,由于服務數量多,再加上集群環境的復雜,流量高的情況下容易導致某個節點宕機,因此需要使用負載均衡和有效的服務發現來進行流量控制。
4 Spring Cloud
????????Spring Cloud是分布式微服務的一站式解決方案,并不是Spring官方開發的,而是集成了眾多開源好用的組件,并基于SpringBoot風格對組件進行封裝方便使用。其主要用來解決如下問題:
????????1.Distributed/versioned configuration分布式版本配置
????????2.Service registration and discovery服務注冊和發現
????????3.Routing路由
????????4.Service-to-service calls服務調用
????????5.Load balancing負載均衡
????????6.Circuit Breakers斷路器
????????7.Distributed messaging分布式消息
Spring Cloud使用需要兼容SpringBoot的版本:
????????在Spring Cloud的規范下,有兩個著名的實現:Spring Cloud Netflix和Spring Cloud Alibaba。目前,Spring Cloud Netflix已經逐漸進入維護,因此Spring Cloud Alibaba就變得主流:
Spring Cloud官方 | Spring Cloud Netflix | Spring Cloud Alibaba | |
服務注冊/發現 | Eureka | Eureka | Nacos |
服務調用 | OpenFeign | Feign | Dubbo |
配置中心 | SpringCloudConfig | Archaius | Nacos |
服務網關 | SpringCloudGateway | Zuul | SpringCloudGateway |
負載均衡 | SpringCloudLoadBalance | Ribbon | Dubbo |
5?Spring Cloud環境和工程搭建
5.1 服務拆分
????????一個大的服務如何拆分為微服務,需要遵循三個原則:
????????1.單一職責原則:一個微服務專注于單一的功能。
????????2.服務自治:微服務可以獨立開發、測試、構建、部署、運行。
????????3.單向依賴:微服務之間要是單向依賴,盡量避免循環依賴、雙向依賴(如果必須要,可以使用消息隊列MQ)。
5.2 示例
????????假設此時要實現電商系統的訂單服務和商品服務,訂單服務提供訂單ID,獲取訂單詳細信息;商品服務根據商品ID,返回商品詳細信息。
5.2.1 數據庫配置
????????以下是相關數據庫、表創建和初始化代碼:
-- 建庫create database if not exists cloud_order charset utf8mb4;use cloud_order;-- 訂單表DROP TABLE IF EXISTS order_detail;CREATE TABLE order_detail (`id` INT NOT NULL AUTO_INCREMENT COMMENT '訂單id',`user_id` BIGINT ( 20 ) NOT NULL COMMENT '用戶id',`product_id` BIGINT ( 20 ) NULL COMMENT '產品id',`num` INT ( 10 ) NULL DEFAULT 0 COMMENT '下單數量',`price` BIGINT ( 20 ) NOT NULL COMMENT '實付款',`delete_flag` TINYINT ( 4 ) NULL DEFAULT 0,`create_time` DATETIME DEFAULT now(),`update_time` DATETIME DEFAULT now(),PRIMARY KEY ( id )) ENGINE = INNODB DEFAULT CHARACTERSET = utf8mb4 COMMENT = '訂單表';-- 數據初始化insert into order_detail (user_id,product_id,num,price)values(2001, 1001,1,99), (2002, 1002,1,30), (2001, 1003,1,40),(2003, 1004,3,58), (2004, 1005,7,85), (2005, 1006,7,94);create database if not exists cloud_product charset utf8mb4;use cloud_product;-- 產品表DROP TABLE IF EXISTS product_detail;CREATE TABLE product_detail (`id` INT NOT NULL AUTO_INCREMENT COMMENT '產品id',`product_name` varchar ( 128 ) NULL COMMENT '產品名稱',`product_price` BIGINT ( 20 ) NOT NULL COMMENT '產品價格',`state` TINYINT ( 4 ) NULL DEFAULT 0 COMMENT '產品狀態 0-有效 1-下架',`create_time` DATETIME DEFAULT now(),`update_time` DATETIME DEFAULT now(),PRIMARY KEY ( id )) ENGINE = INNODB DEFAULT CHARACTERSET = utf8mb4 COMMENT = '產品表';-- 數據初始化insert into product_detail (id, product_name,product_price,state)values(1001,"T恤", 101, 0), (1002, "短袖",30, 0), (1003, "短褲",44, 0),(1004, "衛衣",58, 0), (1005, "馬甲",98, 0),(1006,"羽絨服", 101, 0),(1007, "沖鋒衣",30, 0), (1008, "襪子",44, 0), (1009, "鞋子",58, 0),(10010, "毛衣",98, 0);
5.2.2 父子項目創建
????????采用父子項目創建方式創建Spring Cloud項目,先創建空的Maven項目springcloud_order,再在該項目上創建兩個子項目module:
????????父項目pom文件:
<?xml version="1.0" encoding="UTF-8"?><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>org.example</groupId><artifactId>springcloud_order</artifactId><version>1.0-SNAPSHOT</version><packaging>pom</packaging><modules><module>order_service</module><module>product_service</module></modules><parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>3.1.6</version><relativePath/> <!-- lookup parent from repository --></parent><properties><maven.compiler.source>18</maven.compiler.source><maven.compiler.target>18</maven.compiler.target><java.version>18</java.version><mybatis.version>3.0.3</mybatis.version><mysql.version>8.0.31</mysql.version><spring-cloud.version>2022.0.3</spring-cloud.version></properties><!-- 此處的依賴是實際引入到項目的,父子項目都可以用 --><dependencies><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><optional>true</optional></dependency></dependencies><!-- 聲明子項目相關可能用到的依賴和版本號(并沒有引入依賴) --><dependencyManagement><dependencies><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-dependencies</artifactId><version>${spring-cloud.version}</version><type>pom</type><scope>import</scope></dependency><dependency><groupId>org.mybatis.spring.boot</groupId><artifactId>mybatis-spring-boot-starter</artifactId><version>${mybatis.version}</version></dependency><dependency><groupId>com.mysql</groupId><artifactId>mysql-connector-j</artifactId><version>${mysql.version}</version></dependency><dependency><groupId>org.mybatis.spring.boot</groupId><artifactId>mybatis-spring-boot-starter-test</artifactId><version>${mybatis.version}</version><scope>test</scope></dependency></dependencies></dependencyManagement></project>
????????注意:父項目的打包方式要修改為pom,即<packaging>pom</packaging>。
????????上述pom文件用到了和依賴dependency相關的兩個標簽<dependencyManagement>和<dependencies>,二者區別如下:
????????<dependencies>:直接將依賴引入項目中,如果是父項目的pom文件,則引入的依賴子項目也會繼承。
????????<dependencyManagement>:聲明依賴,并沒有引入依賴到項目中。如果子項目需要用到聲明的依賴,需要顯示引入。如果引入依賴時沒有聲明版本號,默認使用父項目聲明依賴的版本號。如果引入依賴指定了具體版本,則就使用子項目版本號的依賴。
????????使用SpringBoot開發時之所以沒有使用<dependencyManagement>進行依賴的版本管理,就是因為SpringBoot框架的pom文件已經做了默認的版本控制了。
5.2.3 order_service子項目結構配置
????????由于創建的都是空項目,因此需要手動配置啟動類、pom文件、SpringBoot配置文件(yml)等內容:
????????啟動類:
@SpringBootApplicationpublic class OrderServiceApplication {public static void main(String[] args) {SpringApplication.run(OrderServiceApplication.class, args);}}
????????pom文件:
????<dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>com.mysql</groupId><artifactId>mysql-connector-j</artifactId></dependency><!--mybatis--><dependency><groupId>org.mybatis.spring.boot</groupId><artifactId>mybatis-spring-boot-starter</artifactId></dependency></dependencies><build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId></plugin></plugins></build>
????????SpringBoot配置文件:
server:port: 8080spring:datasource:url: jdbc:mysql://127.0.0.1:3306/cloud_order?characterEncoding=utf8&useSSL=falseusername: rootpassword: rootdriver-class-name: com.mysql.cj.jdbc.Drivermybatis:configuration: # 配置打印 MyBatis日志log-impl: org.apache.ibatis.logging.stdout.StdOutImplmap-underscore-to-camel-case: true #配置駝峰自動轉換
????????model層:
@Datapublic class OrderInfo {private Integer id;private Integer userId;private Integer productId;private Integer num;private Integer price;private Integer deleteFlag;private Date createTime;private Date updateTime;}
????????service層:
@Servicepublic class OrderService {@Autowiredprivate OrderMapper orderMapper;public OrderInfo selectOrderById(Integer orderId) {OrderInfo orderInfo = orderMapper.selectOrderById(orderId);return orderInfo;}}
????????mapper層:
@Mapperpublic interface OrderMapper {@Select("select * from order_detail where id=#{orderId}")OrderInfo selectOrderById(Integer orderId);}
????????controller層:
@RequestMapping("/order")@RestControllerpublic class OrderController {@Autowiredprivate OrderService orderService;@RequestMapping("/{orderId}")public OrderInfo getOrderById(@PathVariable("orderId") Integer orderId) {return orderService.selectOrderById(orderId);}}
????????運行代碼結果如下:
5.2.4 product_service子項目結構配置
????????由于創建的都是空項目,因此需要手動配置啟動類、pom文件、SpringBoot配置文件(yml)等內容:
????????啟動類:
@SpringBootApplicationpublic class ProductServiceApplication {public static void main(String[] args) {SpringApplication.run(ProductServiceApplication.class, args);}}
????????pom文件:
????<dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>com.mysql</groupId><artifactId>mysql-connector-j</artifactId></dependency><!--mybatis--><dependency><groupId>org.mybatis.spring.boot</groupId><artifactId>mybatis-spring-boot-starter</artifactId></dependency></dependencies><build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId></plugin></plugins></build>
????????SpringBoot配置文件:
server:port: 8081spring:datasource:url: jdbc:mysql://127.0.0.1:3306/cloud_product?characterEncoding=utf8&useSSL=falseusername: rootpassword: rootdriver-class-name: com.mysql.cj.jdbc.Drivermybatis:configuration: # 配置打印 MyBatis日志log-impl: org.apache.ibatis.logging.stdout.StdOutImplmap-underscore-to-camel-case: true #配置駝峰自動轉換
????????model層:
@Datapublic class OrderInfo {private Integer id;private Integer userId;private Integer productId;private Integer num;private Integer price;private Integer deleteFlag;private Date createTime;private Date updateTime;}
????????service層:
@Servicepublic class OrderService {@Autowiredprivate OrderMapper orderMapper;public OrderInfo selectOrderById(Integer orderId) {OrderInfo orderInfo = orderMapper.selectOrderById(orderId);return orderInfo;}}
????????mapper層:
@Mapperpublic interface OrderMapper {@Select("select * from order_detail where id=#{orderId}")OrderInfo selectOrderById(Integer orderId);}
????????controller層:
@RequestMapping("/order")@RestControllerpublic class OrderController {@Autowiredprivate OrderService orderService;@RequestMapping("/{orderId}")public OrderInfo getOrderById(@PathVariable("orderId") Integer orderId) {return orderService.selectOrderById(orderId);}}
????????運行代碼結果如下:
5.2.5 服務之間的遠程調用
????????現在兩個服務可以各自獨立運行,但是訂單信息上是需要顯示商品信息的,因此訂單服務需要訪問商品服務根據訂單上的商品id查詢訂單對應的商品。
????????首先在order_service項目中引入商品信息類,同時在訂單信息類添加該屬性:
@Datapublic class OrderInfo {private Integer id;private Integer userId;private Integer productId;private Integer num;private Integer price;private Integer deleteFlag;private Date createTime;private Date updateTime;private ProductInfo productInfo;}
????????這里使用RestTemplate來實現HTTP服務:
@Configurationpublic class BeanConfig {@Beanpublic RestTemplate restTemplate() {return new RestTemplate();}}
????????修改業務層代碼:
@Servicepublic class OrderService {@Autowiredprivate OrderMapper orderMapper;@Autowiredprivate RestTemplate restTemplate;public OrderInfo selectOrderById(Integer orderId) {OrderInfo orderInfo = orderMapper.selectOrderById(orderId);String url = "http://127.0.0.1:8081/product/" + orderInfo.getProductId();ProductInfo productInfo = restTemplate.getForObject(url, ProductInfo.class);orderInfo.setProductInfo(productInfo);return orderInfo;}}
????????這里使用restTemplate對象的getForObject()方法來遠程調用product_service服務的接口,返回類型是ProductInfo,把返回的對象設置到orderInfo的屬性中,即可實現遠程調用并返回訂單信息和詳細商品信息。
{"id":1,"userId":2001,"productId":1001,"num":1,"price":99,"deleteFlag":0,"createTime":"2025-07-14T07:44:48.000+00:00","updateTime":"2025-07-14T07:44:48.000+00:00","productInfo":{"id":1001,"productName":"T恤","productPrice":101,"state":0,"createTime":"2025-07-14T07:44:48.000+00:00","updateTime":"2025-07-14T07:44:48.000+00:00"}}
5.3 RestTemplate
5.3.1 REST
????????REST全稱是Representational State Transfer(表現層資源狀態轉移),是Roy Fielding在2000年提出的一種軟件架構風格,指資源在網絡中以某種表現形式進行狀態轉移。
????????資源:網絡中所有事物都可以稱為資源,每個資源都有唯一的資源標識符(URL)。
????????表現層:資源的表現形式,比如文本txt、圖片png、甚至JSON、HTML等等都是表現形式。
????????狀態轉移:客戶端通過訪問URL,引起資源在網絡傳輸中的狀態變化(比如增刪改查)。
5.3.2 RESTful
????????REST只是一種架構,并沒有具體的實現。而RESTful就是具有REST架構的設計風格,接口可以滿足這種風格。
????????RESTful基于HTTP協議,定義了兩種特征:資源和統一接口。資源可以是圖片、文本、二進制等等,統一接口是對資源的操作(增刪改查),對應HTTP協議的POST、DELETE、PUT、GET方法。
????????RestTemplate就是Spring對HTTP封裝并使用RESTful風格的對象,內部實現了連接的自動開啟和關閉,調用者只需提供URL和參數即可。
缺點:
????????1.操作繁瑣且請求方法不可見:實現RESTful風格的接口通常需要根據請求方法區分操作,但是URL并不能直接顯示請求方法,需要抓包工具才能得知。并且一些瀏覽器不支持其它請求,需要額外處理。
????????2.過分強調資源,簡單的增刪改查請求并不能滿足復雜的業務需求,增加開發難度。
下篇文章:
Spring Cloud系列—Eureka服務注冊/發現https://blog.csdn.net/sniper_fandc/article/details/149937589?fromshare=blogdetail&sharetype=blogdetail&sharerId=149937589&sharerefer=PC&sharesource=sniper_fandc&sharefrom=from_link