微服务之服务搭建及远程调用
服务拆分注意事项
单一职责:不同微服务,不要重复开发相同业务
数据独立:不要访问其它微服务的数据库
面向服务:将服务暴露为接口,供其它微服务调用
简单项目
需求:查询订单信息时将用户信息也查询
微服务分析:
- 需要两个服务UserService(操作用户信息)、OrderService(操作订单信息)
- OrderService远程调用UserService提供查询用户信息的方法
搭建工程
数据库Sql语句
cloud-user数据库中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;
cloud-order数据库中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;
创建maven项目,父工程
pom配置
使用SpringBoot,需要添加父级:SpringBoot:2.3.9.RELEASE
依赖管理
- SpringCloud:Hoxton.SR10(注意版本)
- MySql、Mybatis
<packaging>pom</packaging> <!--父级节点--> <parent> <artifactId>spring-boot-starter-parent</artifactId> <groupId>org.springframework.boot</groupId> <version>2.3.9.RELEASE</version> </parent> <properties> <java.version>1.8</java.version> <spring-cloud.version>Hoxton.SR10</spring-cloud.version> <mysql.version>5.1.47</mysql.version> <mybatis.version>2.1.1</mybatis.version> </properties> <dependencyManagement> <dependencies> <!-- springCloud --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>${spring-cloud.version}</version> <type>pom</type> <scope>import</scope> </dependency> <!-- mysql驱动 --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>${mysql.version}</version> </dependency> <!--mybatis--> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>${mybatis.version}</version> </dependency> </dependencies> </dependencyManagement> <dependencies> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </dependency> </dependencies>
创建子模块user-service、order-service
项目结构如下
编写user-service
编写pom
添加MySQL和mybatis依赖、maven插件(打包成可直接运行的 JAR 文件)
<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency> <!--mybatis--> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> </dependency> </dependencies> <build> <!--jar包名称--> <finalName>user-service</finalName> <plugins> <!--打包成可以直接运行的 JAR 文件(使用“java -jar”命令就可以直接运行)--> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build>
配置application.yaml(配置服务器端口、数据库连接、mybatis、logging)
server: port: 8081 spring: datasource: url: jdbc:mysql://192.168.223.129:3306/cloud-user?useSSL=false username: root password: root driver-class-name: com.mysql.cj.jdbc.Driver mybatis: type-aliases-package: com.marw.pojo configuration: map-underscore-to-camel-case: true logging: level: com.marw: debug pattern: dateformat: MM-dd HH:mm:ss:SSS
启动类
@SpringBootApplication public class UserServiceApplication { public static void main(String[] args) { SpringApplication.run(UserServiceApplication.class,args); } }
pojo(Java Bean)
@Data public class User { private Long id; private String username; private String address; }
mapper(数据库操作)
public interface UserMapper { @Select("select * from tb_user where id = #{id}") User findById(@Param("id") Long id); }
service(业务逻辑)
@Service public class UserService { @Autowired private UserMapper userMapper; public User queryById(Long id){ return userMapper.findById(id); } }
controller(暴露服务)
@Slf4j @RestController @RequestMapping("/user") public class UserController { @Autowired private UserService userService; @GetMapping("/{id}") public User queryByid(@PathVariable("id") Long id) { return userService.queryById(id); } }
启动项目
错误:Field userMapper in com.marw.service.UserService required a bean of type 'com.marw.mapper.UserMapper' that could not be found
原因:没有将mapper放到spring容器中
解决:将mapper放到spring容器中
方式一:添加@Mapper注解
@Mapper public interface XxxMapper { ... ... }
方式二:通过扫描mapper所在的包
@SpringBootApplication @MapperScan("com.marw.mapper") public class UserServiceApplication { public static void main(String[] args) { SpringApplication.run(UserServiceApplication.class,args); } }
项目整体结构
测试
order-service和user-service类似,就不重复编写,直接查看效果
发现user的数据为空,也就是说order服务需要调用user服务提供的方法
微服务远程调用
远程调用方式分析
浏览器通过http(http://localhost:8081/user/1)请求访问user服务,如果order服务也能通过http(http://localhost:8081/user/1)请求访问user服务
RestTemplate
spring提供发送http请求的工具
注册RestTemplate
通过Bean注入(@Bean注解)将RestTemplate对象注册到Spring容器中,就可以使用依赖注入方式(@Autowired)从Spring容器中获取RestTemplate对象
Bean注入只能写入Java配置类(被@Configuration注解的类)
@SpringBootApplication @MapperScan("com.marw.mapper") public class OrderServiceApplication { public static void main(String[] args) { SpringApplication.run(OrderServiceApplication.class, args); } @Bean public RestTemplate restTemplate() { return new RestTemplate(); } }
使用RestTemplate
@Service public class OrderService { @Autowired private OrderMapper orderMapper; @Autowired private RestTemplate restTemplate; public Order queryById(Long id){ Order order = orderMapper.findById(id); String url = "http://localhost:8081/user/"+order.getUserId(); User user = restTemplate.getForObject(url, User.class); order.setUser(user); return order; } }
使用restTemplate方法getForObject还是postForObject等是根据请求方法所指定的请求方式决定的
测试
chorme的Json格式化工具下载地址:https://github.com/gildas-lormeau/JSONView-for-Chrome