微服务架构实战:从服务拆分到RestTemplate远程调用
- [一 . 服务拆分](#一 . 服务拆分)
-
- [1.1 服务拆分注意事项](#1.1 服务拆分注意事项)
- [1.2 导入服务拆分 Demo](#1.2 导入服务拆分 Demo)
- [1.3 小结](#1.3 小结)
- [二 . 服务间调用](#二 . 服务间调用)
-
- [2.1 注册 RestTemplate](#2.1 注册 RestTemplate)
- [2.2 实现远程调用](#2.2 实现远程调用)
- [2.3 小结](#2.3 小结)
- [三 . 提供方和消费方](#三 . 提供方和消费方)
在分布式系统设计中,微服务架构因其灵活性、可扩展性成为主流方案。然而,如何合理拆分服务、实现高效服务间通信,是开发者面临的核心挑战。本文通过一个电商场景的订单-用户服务案例,演示如何基于单一职责原则拆分微服务,并通过RestTemplate实现服务间HTTP调用。读者将学习到:
服务拆分的三大核心原则(单一职责、数据独立、面向服务);
独立数据库设计与多服务协同开发;
使用Spring Boot的RestTemplate组件完成跨服务数据聚合。
本专栏的内容均来自于 B 站 UP 主黑马程序员的教学视频,感谢你们提供了优质的学习资料,让编程不再难懂。
专栏地址 : https://blog.csdn.net/m0_53117341/category_12835102.html
一 . 服务拆分
1.1 服务拆分注意事项
- 单一职责 : 不同微服务之间不要重复的开发相同业务
- 数据独立 : 不能访问其他微服务的数据库
- 面向服务 : 将自己的业务暴露出接口 , 供其他微服务调用
1.2 导入服务拆分 Demo
第一步 : 导入提供给大家的项目模板

那在父工程的 <dependencyManagement>
下 , 只负责版本的锁定 , 不负责版本的导入

然后我们来修改一下两个微服务的 application.yml 信息

第二步 : 了解项目架构
- order-service : 根据 ID 查询订单
- user-service : 根据 ID 查询用户
那这两个就是不同的微服务 , 也需要有自己独立的数据库
第三步 : 将两个微服务的 SQL 进行导入
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;
sql
-- 创建数据库
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 的启动类中创建一个模板对象

java
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);
}
@Bean
public RestTemplate restTemplate() {
return new RestTemplate();
}
}
2.2 实现远程调用
那接下来 , 我们就需要组装出要发送的 URL 地址了
首先 , 我们需要获取到 userId , 而正好 order 表中也存储了 userId , 所以我们可以直接获取

那接下来就需要拼接 URL 了

接下来 , 我们就可以调用 RestTemplate 来去发起请求获取用户信息
首先需要注入 RestTemplate 对象

然后我们调用它的 getForObject 方法 , 他有两个参数
- 要发起请求的 URL 地址
- 获取到的消息应该转化成哪种对象

最后我们把获取到的内容添加到 order 的 user 字段中

java
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 {
@Autowired
private OrderMapper orderMapper;
@Autowired
private RestTemplate restTemplate;
public Order queryOrderById(Long orderId) {
// 1. 查询订单
Order order = orderMapper.findById(orderId);
// 2. 查询用户 ID
Long userId = order.getUserId();
// 3. 拼接 URL
String 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 就是服务消费方
那提供方与消费方的角色其实是相对来说的 , 一个服务既可以是服务提供者 , 又可以是服务消费者
小结 :
