# 二、服务拆分及远程调用🎄
- 服务拆分
- 服务间调用
# 2.1、服务拆分注意事项🌳
- 不同微服务,不要重复开发相同业务
- 微服务数据独立,不要访问其它微服务的数据库
- 微服务可以将自己的业务暴漏为接口,供其它微服务调用
前往查看代码:
https://gitee.com/doukaixin/typora/tree/cloud-demo 代码演示 /
查询的结果:
user
order
总结:
- 微服务需要根据业务模块拆分,做到单一职责,不要重复开发相同业务
- 微服务可以将业务暴漏为接口,供其它微服务使用
- 不同微服务都应该有自己独立的数据库
# 2.2、微服务远程调用🌳
案例:根据订单 id 查询订单功能
需求:根据订单 id 查询订单的同时,把订单所属的用户信息一起返回
# 2.2.1、远程调用方式分析🌲
现在 user 服务对外暴漏了一个 RestFull 的接口,只要我们在 url 中输入对应的地址就一定能拿到用户信息
如何在 java 中发送 http 请求呢?
# 2.2.2、操作步骤演示:🌲
# 一、注册 RestTemplate🌴
在 order-service 的 OrderApplication ,SpringBoot 启动类中注册 RestTemplate
/** | |
* 创建 RestTemplate 并注入 Spring 容器 | |
*/ | |
@Bean | |
public RestTemplate restTemplate() | |
{ | |
return new RestTemplate(); | |
} |
# 二、修改方法🌴
修改 order-service 中的 OrderService 的 queryOrderById 方法
@Service | |
public class OrderService { | |
@Autowired | |
private RestTemplate restTemplate; | |
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 发送请求,实现远程调用 | |
// 默认返回 json 数据类型,我们可以指定返回的类型为 user 对象类型 | |
User user = restTemplate.getForObject(url, User.class); | |
// 3. 封装 User 对象到 Order | |
order.setUser(user); | |
// 4. 返回 | |
return order; | |
} | |
} |
总结:
- 微服务调用方式
- 基于 RestTemplate 发起的 http 请求实现远程调用
- http 请求做远程调用是与语言无关的调用,只要知道对方的 ip,端口,接口路径,请求参数即可
# 2.2.3、提供者与消费者🌳
- 服务提供者:一次业务中,被其它微服务调用的服务。(提供接口给其它微服务)
- 服务消费者:一次业务中,调用其它微服务的服务。(调用其它微服务提供的接口)
思考问题:
服务 A 调用服务 B,服务 B 调用服务 C,那么服务 B 是什么角色?
答:
服务 B 是什么角色要看相对于谁而言,如果相对于 A 调 B 那它就是提供者,相对 B 调 C 它又变成了消费者
因此我们可以认为一个服务它既可以是提供者也可以是消费者
总结:
- 服务调用关系
- 服务提供者:暴漏接口给其它微服务调用
- 服务消费者:调用其它微服务提供的接口
- 提供者与消费者 角色其实是相对的
- 一个服务可以同时服务提供者和服务消费者
# 2.3、Eureka 注册中心🌳
- 远程调用的问题
- eureka 原理
- 搭建 EurekaServer
- 服务注册
- 服务发现
服务调用出现的问题。
- 服务消费者该如何获取服务提供者的地址信息?
- 如果有多个服务提供者,消费者该如何选择?
- 消费者如何得知服务提供者的健康状态?
# 2.3.1、Eureka 的作用🌲
在 Eureka 的结构当中,分成了两个 (概念 / 角色)。第一个角色就是 Server 服务端它的名字叫做 “注册中心” 其作用是:记录和管理这些微服务.
而另外第二个角色就是 user-service 服务提供者和 order-service 服务消费者,不管是提供者还是消费者都是微服务所以统称为:Eureka 的客户端.
user-service 服务提供者启动时会将自己的信息注册给 Eureka (每一个服务启动时都会做这件事,只要是 Eureka 的客户端)
order-service 服务消费者找 Eureka 去查询一下有没有 user-service,然后 Eureka 查询到的话就返回给 order-service 关于 user-service 的地址信息.
现在我们拿到列表服务的信息了,这时就由负载均衡从三个地址信息里面挑出一个然后进行请求.
那么问题又来了,挑出来的这个会不会是 <font color='red'> 挂掉 </font > 的呢?
答:不会!因为 user-service 服务每隔 30 秒都会想 Eureka 发送一次心跳,来确认一下自己的状态,如果它不跳了 Eureka 就会将 user-service 从列表中移除掉.
eureka 的作用.
- 消费者该如何获取服务提供者具体信息?
- 服务提供者启动时向 eureka 注册自己的信息
- eureka 保存这些信息
- 消费者根据服务名称向 eureka 拉取提供者信息
- 如果有多个服务提供者,消费者该如何选择?
- 服务消费者利用负载均衡算法,从服务列表中挑选一个
- 消费者如何感知服务提供者健康状态?
- 服务提供者会每隔 30 秒向 EurekaServer 发送心跳请求,报告健康状态
- eureka 会更新记录服务列表信息,心跳不正常会被剔除
- 消费者就可以拉取到最新的信息
总结:
在 Eureka 架构中,微服务角色有两类:
- EurekaServer:服务端,注册中心
- 记录服务信息
- 心跳监控
- EurekaClient:客户端
- provider (提供者):服务提供者,例如案例中的 user-service
- 注册自己的信息到 EurekaServer
- 每隔 30 秒向 EurekaServer 发送心跳
- consumer (消费者):服务消费者,例如案例中的 order-service
- 根据服务名称从 EurekaServer 拉取服务列表
- 基于服务列表做负载均衡,选中一个微服务后发起远程调用
# 2.3.2、动手实践🌲
# 2.3.2.1 、搭建 EurekaServer🌴
搭建 EurekaServer 服务步骤如下:
1、创建项目,引入 spring-cloud-starter-netflix-eureka-server 的依赖
这里的版本号由父模块进行了统一管理,这里出错了可能是版本与 SpringBoot 不对应的问题需要降低某一方或升级某一方的版本号
<dependencies> | |
<!--eureka 服务端 --> | |
<dependency> | |
<groupId>org.springframework.cloud</groupId> | |
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId> | |
</dependency> | |
</dependencies> |
2、编写启动类,添加 @EnableEurekaServer 注解
import org.springframework.boot.SpringApplication; | |
import org.springframework.boot.autoconfigure.SpringBootApplication; | |
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer; | |
@EnableEurekaServer | |
@SpringBootApplication | |
public class EurekaServerApplication { | |
public static void main(String[] args) { | |
SpringApplication.run(EurekaServerApplication.class, args); | |
} | |
} |
3、添加 appliation.yml 文件,编写下面的配置:
server: | |
port: 10086 #服务端口 | |
# 为了做服务注册才配的信息 | |
spring: | |
application: | |
name: eurekaserver #服务名称 | |
eureka: | |
client: | |
service-url: #eureka 地址信息 | |
defaultZone: http://127.0.0.1:10086/eureka | |
# 自己就有 eureka 为什么还要配置地址信息呢? | |
# 原因:eurka 自己也是一个微服务,所以 eureka 在启动的时候会将自己注册到 eureka 中 | |
# 这是为了将来 eureka 集群之间通信去用的,比方说启动了三个 eureka 将来这三个 | |
# eureka 之间会相互注册,这样它们就可以做数据交流了。所以 defaultZone 应该 | |
# 配置的是 eureka 集群的地址如果有多个使用逗号隔开 |
启动进行访问测试是否正常
启动正常。这里面最重要的在于如下:
每个实例的状态
总结:
- 搭建 EurekaServer
- 引入 eureka-server 依赖
- 添加 @EnableEurekaServer 注解
- 在 application.yml 中配置 eureka 地址
# 2.3.2.2、注册 user-service🌴
将 user-service 服务注册到 EurekaServer 步骤如下:
1、在 user-service 项目引入 spring-cloud-starter-netflix-eureka-client 的依赖
<!--eureka 客户端依赖 --> | |
<dependency> | |
<groupId>org.springframework.cloud</groupId> | |
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> | |
</dependency> |
2、在 appliation.yml 文件,编写下面的配置:
server: | |
port: 8081 | |
spring: | |
datasource: | |
url: jdbc:mysql://localhost:3306/cloud_user?useSSL=false&characterEncoding=utf-8&serverTimezone=UTC | |
username: root | |
password: dkx. | |
driver-class-name: com.mysql.cj.jdbc.Driver | |
application: | |
name: userservice # user 服务名称 | |
mybatis: | |
type-aliases-package: cn.itcast.user.pojo | |
configuration: | |
map-underscore-to-camel-case: true | |
logging: | |
level: | |
cn.itcast: debug | |
pattern: | |
dateformat: MM-dd HH:mm:ss:SSS | |
eureka: | |
client: | |
service-url: # eureka 地址信息 | |
defaultZone: http://127.0.0.1:10086/eureka |
操作完成!
如果要将服务消费者注册到 eureka 中 将同样的方法配置到 order-service 中然后访问 eureka 地址
另外,我们可以将 user-service 多次启动,模拟多实例部署,但为了避免端口冲突,需要修改端口设置:-Dserver.port=8082
设置好后运行 user 的新服务再次访问就会出现两个实例
总结:
- 服务注册
- 引入 eureka-client 依赖
- 在 application.yml 中配置 eureka 地址
- 无论是消费者还是提供者,引入 eureka-client 依赖,知道 eureka 地址后,都可以完成服务注册.
# 2.3.2.3、在 order-service 完成服务拉取🌴
服务拉取是基于服务名称获取服务列表,然后在对服务列表负载均衡
1、修改 OrderService 的代码,修改访问的 url 路径,用服务名代替 ip,端口:
String url = "http://userservice/user/" + order.getUserId(); |
2、在 order-service 项目的启动类 OrderApplication 中的 RestTemplate 添加负载均衡注解:
@Bean | |
@LoadBalanced | |
public RestTemplate restTemplate() | |
{ | |
return new RestTemplate(); | |
} |
访问 order 地址
在 order 与 user 的控制台中就会打印查询的信息
总结:
- 搭建 EurekaServer
- 引入 eureka-server 依赖.
- 启动类中添加 @EnableEurekaServer 注解
- 在 application.yml 中配置 eureka 地址.
- 服务注册
- 引入 eureka-client 依赖.
- 在 application.yml 中配置 eureka 地址.
- 服务发现 / 拉取
- 引入 eureka-client 依赖.
- 在 application.yml 中配置 eureka 地址.
- 启动类中的 Bean。给 RestTemplate 添加 @LoadBalanced 注解
- 用服务提供者的服务名称远程调用