微服務架構實戰:從服務拆分到RestTemplate遠程調用
- 一 . 服務拆分
- 1.1 服務拆分注意事項
- 1.2 導入服務拆分 Demo
- 1.3 小結
- 二 . 服務間調用
- 2.1 注冊 RestTemplate
- 2.2 實現遠程調用
- 2.3 小結
- 三 . 提供方和消費方
在分布式系統設計中,微服務架構因其靈活性、可擴展性成為主流方案。然而,如何合理拆分服務、實現高效服務間通信,是開發者面臨的核心挑戰。本文通過一個電商場景的訂單-用戶服務案例,演示如何基于單一職責原則拆分微服務,并通過RestTemplate實現服務間HTTP調用。讀者將學習到:
- 服務拆分的三大核心原則(單一職責、數據獨立、面向服務);
- 獨立數據庫設計與多服務協同開發;
- 使用Spring Boot的RestTemplate組件完成跨服務數據聚合。
本專欄的內容均來自于 B 站 UP 主黑馬程序員的教學視頻,感謝你們提供了優質的學習資料,讓編程不再難懂。
專欄地址 : https://blog.csdn.net/m0_53117341/category_12835102.html
一 . 服務拆分
1.1 服務拆分注意事項
- 單一職責 : 不同微服務之間不要重復的開發相同業務
- 數據獨立 : 不能訪問其他微服務的數據庫
- 面向服務 : 將自己的業務暴露出接口 , 供其他微服務調用
1.2 導入服務拆分 Demo
第一步 : 導入提供給大家的項目模板
cloud-demo.zip
那在父工程的 <dependencyManagement>
下 , 只負責版本的鎖定 , 不負責版本的導入
然后我們來修改一下兩個微服務的 application.yml 信息
第二步 : 了解項目架構
- order-service : 根據 ID 查詢訂單
- user-service : 根據 ID 查詢用戶
那這兩個就是不同的微服務 , 也需要有自己獨立的數據庫
第三步 : 將兩個微服務的 SQL 進行導入
-- 創建數據庫
create database `cloud_user` character set utf8mb4;-- 使用數據庫
use `cloud_user`;SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;-- ----------------------------
-- Table structure for tb_user
-- ----------------------------
DROP TABLE IF EXISTS `tb_user`;
CREATE TABLE `tb_user` (`id` bigint(20) NOT NULL AUTO_INCREMENT,`username` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '收件人',`address` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '地址',PRIMARY KEY (`id`) USING BTREE,UNIQUE INDEX `username`(`username`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 109 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Compact;-- ----------------------------
-- Records of tb_user
-- ----------------------------
INSERT INTO `tb_user` VALUES (1, '柳巖', '湖南省衡陽市');
INSERT INTO `tb_user` VALUES (2, '文二狗', '陜西省西安市');
INSERT INTO `tb_user` VALUES (3, '華沉魚', '湖北省十堰市');
INSERT INTO `tb_user` VALUES (4, '張必沉', '天津市');
INSERT INTO `tb_user` VALUES (5, '鄭爽爽', '遼寧省沈陽市大東區');
INSERT INTO `tb_user` VALUES (6, '范兵兵', '山東省青島市');SET FOREIGN_KEY_CHECKS = 1;
-- 創建數據庫
create database `cloud_order` character set utf8mb4;-- 使用數據庫
use `cloud_order`;SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;-- ----------------------------
-- Table structure for tb_order
-- ----------------------------
DROP TABLE IF EXISTS `tb_order`;
CREATE TABLE `tb_order` (`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '訂單id',`user_id` bigint(20) NOT NULL COMMENT '用戶id',`name` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '商品名稱',`price` bigint(20) NOT NULL COMMENT '商品價格',`num` int(10) NULL DEFAULT 0 COMMENT '商品數量',PRIMARY KEY (`id`) USING BTREE,UNIQUE INDEX `username`(`name`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 109 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Compact;-- ----------------------------
-- Records of tb_order
-- ----------------------------
INSERT INTO `tb_order` VALUES (101, 1, 'Apple 蘋果 iPhone 12 ', 699900, 1);
INSERT INTO `tb_order` VALUES (102, 2, '雅迪 yadea 新國標電動車', 209900, 1);
INSERT INTO `tb_order` VALUES (103, 3, '駱駝(CAMEL)休閑運動鞋女', 43900, 1);
INSERT INTO `tb_order` VALUES (104, 4, '小米10 雙模5G 驍龍865', 359900, 1);
INSERT INTO `tb_order` VALUES (105, 5, 'OPPO Reno3 Pro 雙模5G 視頻雙防抖', 299900, 1);
INSERT INTO `tb_order` VALUES (106, 6, '美的(Midea) 新能效 冷靜星II ', 544900, 1);
INSERT INTO `tb_order` VALUES (107, 2, '西昊/SIHOO 人體工學電腦椅子', 79900, 1);
INSERT INTO `tb_order` VALUES (108, 3, '梵班(FAMDBANN)休閑男鞋', 31900, 1);SET FOREIGN_KEY_CHECKS = 1;
那將來微服務的模塊有可能會很多 , 啟動起來會很麻煩 , 就給大家介紹一種新的啟動方式
之后我們想要啟動誰 , 選中誰即可
1.3 小結
- 微服務需要根據業務模塊拆分 , 做到單一職責 , 不要重復開發相同業務
- 微服務可以將業務暴漏為接口 , 供其他微服務使用
- 不同微服務都應該有自己獨立的數據庫
二 . 服務間調用
我們先訪問這兩個服務
訪問 http://127.0.0.1:8081/user/1 就可以獲取到 ID 為 1 的用戶信息
訪問 http://127.0.0.1:8080/order/101 就可以獲取到 ID 為 101 的訂單信息
那接下來 , 我們想實現一個案例 : 根據訂單 ID 查詢訂單的同時 , 把訂單所屬的用戶信息一起返回
那這樣的話 , 我們就需要將上面兩個 URL 所獲取的內容合并 , 然后進行返回
那我們用戶是通過 URL 來獲取到數據的 , 所以我們也可以通過代碼的方式來去模擬 URL 來去獲取數據
那接下來 , 我們就在 order-service 中向 user-service 發起一個 HTTP 請求 , 調用 http://127.0.0.1:8081/user/{userId} 這個接口
2.1 注冊 RestTemplate
我們在 order-service 的啟動類中創建一個模板對象
package com.example.order;import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestTemplate;@MapperScan("com.example.order.mapper")
@SpringBootApplication
public class OrderApplication {public static void main(String[] args) {SpringApplication.run(OrderApplication.class, args);}@Beanpublic RestTemplate restTemplate() {return new RestTemplate();}
}
2.2 實現遠程調用
那接下來 , 我們就需要組裝出要發送的 URL 地址了
首先 , 我們需要獲取到 userId , 而正好 order 表中也存儲了 userId , 所以我們可以直接獲取
那接下來就需要拼接 URL 了
接下來 , 我們就可以調用 RestTemplate 來去發起請求獲取用戶信息
首先需要注入 RestTemplate 對象
然后我們調用它的 getForObject 方法 , 他有兩個參數
- 要發起請求的 URL 地址
- 獲取到的消息應該轉化成哪種對象
最后我們把獲取到的內容添加到 order 的 user 字段中
package com.example.order.service;import com.example.order.mapper.OrderMapper;
import com.example.order.pojo.Order;
import com.example.order.pojo.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;@Service
public class OrderService {@Autowiredprivate OrderMapper orderMapper;@Autowiredprivate RestTemplate restTemplate;public Order queryOrderById(Long orderId) {// 1. 查詢訂單Order order = orderMapper.findById(orderId);// 2. 查詢用戶 IDLong userId = order.getUserId();// 3. 拼接 URLString url = "http://127.0.0.1:8081/user/" + userId;// 4. 調用 RestTemplate 發起請求獲取用戶信息// 第一個參數: 要請求的 URL 地址// 第二個參數: 獲取到的數據要轉換成哪種對象User user = restTemplate.getForObject(url, User.class);// 5. 將獲取到的用戶信息添加到 order 實體類的 user 字段中order.setUser(user);// 6. 返回return order;}
}
接下來 , 我們重啟 order-service 服務 , 來觀察一下前后變化
2.3 小結
微服務調用方式
- 基于 RestTemplate 發起的 HTTP 請求實現遠程調用
- HTTP 請求做遠程調用是與語言無關的調用 , 只需要知道對方的 IP、端口、接口路徑、請求參數即可 .
三 . 提供方和消費方
服務提供方 : 被其他微服務調用的服務 (提供接口給其他微服務)
服務消費方 : 調用其他微服務的服務 (調用其他微服務提供的接口)
那我們之前的案例中 , user-service 就是服務提供方 , order-service 就是服務消費方
那提供方與消費方的角色其實是相對來說的 , 一個服務既可以是服務提供者 , 又可以是服務消費者
小結 :