2.1 服务拆分原则
- 不同微服务,不要重复开发相同业务;
- 微服务数据独立,不要访问其它微服务的数据库;
- 微服务可以将自己的业务暴露为接口,供其它微服务调用。
2.2 服务拆分案例
这里以黑马的微服务cloud-demo为例,进行项目复用。
cloud-demo:父工程,管理依赖
- order-service:订单微服务,负责订单相关业务;
- user-service:用户微服务,负责用户相关业务;
要求:
- 订单微服务和用户微服务必须有各自的数据库,相互独立;
- 订单服务和用户服务都对外暴露Restful的接口;
- 订单服务如果要查询用户信息,只能调用用户服务的Restful接口,不能查询用户数据库。
2.2.1 从零开始搭建SpringCloud项目
创建项目:
首先在Idea中new一个Maven项目,在GroupId中填入包名,ArtifastId填写项目名,然后选择电脑中一个文件夹来创建该项目。
创建好的项目如下:
由于主项目中,我们是不写代码的,因此将该项目中的src包直接删除,再导入order-service和user-service模块。
导入项目依赖:
导入父项目依赖:
<?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>com.ku</groupId>
<artifactId>cloud-demo</artifactId>
<packaging>pom</packaging>
<version>1.0</version>
<modules>
<module>order-service</module>
<module>user-service</module>
</modules>
<!--父依赖-->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.9.RELEASE</version>
<relativePath/>
</parent>
<!--依赖属性-->
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<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>
<!--nacos的管理依赖-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>2.2.5.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<!-- mysql驱动 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.17</version>
</dependency>
<!--mybatis-->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.1.0</version>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.20</version>
</dependency>
</dependencies>
</project>
父级依赖
导入order-service项目依赖:
<?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">
<parent>
<artifactId>cloud-demo</artifactId>
<groupId>com.ku</groupId>
<version>1.0</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>order-service</artifactId>
<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>
</dependencies>
<!--打包-->
<build>
<finalName>order-app</finalName>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
order-service服务依赖
导入user-service项目依赖:
<?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">
<parent>
<artifactId>cloud-demo</artifactId>
<groupId>com.ku</groupId>
<version>1.0</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>user-service</artifactId>
<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>
</dependencies>
<!--打包-->
<build>
<finalName>user-app</finalName>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
user-service服务依赖
创建两个服务模块结构流程:
- 分别创建OrderServiceApplication和UserServiceApplciation两个启动类
- 创建order-service和user-service的四层结构(pojo、mapper、service、controller,其中Order类中引入User类,后面会改进)
- 首先创建两个模块的数据库,然后执行两个模块的SQL语句,生成对应的数据表
- 分别配置两个模块的application.yml文件
#order-service配置文件
server:
port: 8080
spring:
datasource:
url: jdbc:mysql://localhost:3306/cloud_order?useSSL=false
username: root
password: root
driver-class-name: com.mysql.jdbc.Driver
mybatis:
type-aliases-package: com.ku.order.pojo
configuration:
map-underscore-to-camel-case: true
logging:
level:
com.ku.order: debug
order-service的application.xml配置文件
#user-service配置文件
server:
port: 8081
spring:
datasource:
url: jdbc:mysql://localhost:3306/cloud_user?useSSL=false
username: root
password: root
driver-class-name: com.mysql.jdbc.Driver
mybatis:
type-aliases-package: cn.ku.user.pojo
configuration:
map-underscore-to-camel-case: true
logging:
level:
com.ku.user: debug
pattern:
dateformat: MM-dd HH:mm:ss:SSS
user-service的application.yaml配置文件
在启动多模块项目的时候,会出现以下弹窗,我们点集弹窗,选择第一个,就会出现多模块运行窗口了
2.3 实现远程调用案例
2.3.1 Order-service查询测试
当我们通过订单号区查询订单信息的时候,由于我们的订单数据库中只存储了用户ID,且每个服务之间是相互独立的,因此无法查询到用户相关信息。
2.3.2 RestTenplate远程调用流程
在上一节中,我们由于服务模块之间相互独立,通过order模块无法查询到用户模块的用户信息,本节我们将通过远程调用,通过user-service暴露的接口查询出订单实体类中的是所有属性信息。
我们需要在order-service中向user-service发起一个http请求,调用http://localhost:8081/user/{userId}这个接口。
- 注册一个RestTemplate的实例到Spring容器中,即向OrderServiceApplication注册一个RestTemplate的bean;
- 修改order-service服务中的queryOrderById方法,根据Order对象中的uerId查询User;
- 将查询的User填充到Order对象中,一起返回。
2.3.3 注册 RestTenplate
我们需要在order-service的OrderServiceApplication启动类中,注册RestTemplate实例
@MapperScan("com.ku.order.mapper")
@SpringBootApplication
public class OrderServiceApplication {
public static void main(String[] args) {
SpringApplication.run(OrderServiceApplication.class, args);
}
//远程调用
@Bean
public RestTemplate restTemplate(){
return new RestTemplate();
}
}
2.3.4 实现远程调用
修改order-service服务中的com.ku.order.service中OrderService类中的queryOrderById()方法
public Order queryOrderById(Long orderId) {
// 1.查询订单
Order order = orderMapper.findById(orderId);
// 2.利用RestTemplate发起http请求,查询用户
// 2.1.url路径
String url = "http://localhost:8081/user/" + order.getUserId();
// 2.2.发送http请求,实现远程调用
User user = restTemplate.getForObject(url, User.class);
// 3.封装user到Order
order.setUser(user);
// 4.返回
return order;
}
RestTemplate远程调用
上述代码重点在于RestTemplate对象通过传入的url和希望获取的实体类到getForObject()方法从而获取用户模块的用户信息。
测试结果:
小结:
- 基于RestTemplate发起的Http请求实现远程调用;
- http请求做远程调用是与语言无关的调用,只要知道对方的ip、端口、接口路径、请求参数即可。
2.4 提供者与消费者
在服务调用关系中,会有两个不同的角色:
服务提供者:一次业务中,被其它微服务调用的服务。(提供接口给其它微服务)
服务消费者:一次业务中,调用其它微服务的服务。(调用其它微服务提供的接口)
但是,服务提供者与服务消费者的角色并不是绝对的,而是相对于业务而言。
思考:如果服务A调用了服务B,而服务B又调用了服务C,服务B的角色是什么?
对于A调用B的业务而言:A是服务消费者,B是服务提供者对于B调用C的业务而言:B是服务消费者,C是服务提供者因此,服务B既可以是服务提供者,也可以是服务消费者。
服务调用关系:
- 服务提供者:暴露接口给其他服务调用
- 服务消费者:调用其他微服务提供的接口
- 提供者与消费者是相对的
- 一个服务可以同时是服务提供者和服务消费者