Part03 ☀️
Part03 ☀️
课程内容
- 服务的调用
- 购物车微服务远程调用商品服务 ✏️
- 服务的注册和发现 🍐
- 注册中心的原理 🍐 ✏️
- Nacos注册中心 🍐 ✏️
- 服务注册 🍐 ✏️
- 服务发现
1. 服务的调用
接下来解决: cart-service
服务中实现对 item-service
服务的调用🎯
服务调用
在拆分的时候,我们发现一个问题:
就是购物车业务中需要查询商品信息,但商品信息查询的逻辑全部迁移到了 item-service
服务,导致我们无法查询。
最终结果就是查询到的购物车数据不完整,因此要想解决这个问题,我们就必须改造其中的代码,把原本本地方法调用,改造成跨微服务的远程调用🎯(RPC,即 Remote Produce Call)。
因此,现在查询购物车列表的流程变成了这样:

那么问题来了 :我们该如何跨服务调用,准确的说,如何在 cart-service
中获取 item-service
服务中的提供的商品数据呢?
提示:使用RestTemplate可以方便的实现 Http 请求的发送
代码操作
核心步骤
- 在item-service的controller导入ItemController类,提供一个根据id查询商品的接口
- 熟悉RestTemplate,并初始化RestTemplate对象,并准备CartVo类
- 注入RestTemplate对象,并发起远程调用
- 获得远程的结果,处理数据(最新的价格,状态,库存),返回给前端
- 在item-service的controller导入ItemController类,提供一个根据id查询商品的接口
@RestController
@RequestMapping("/items")
public class ItemController {
@Autowired
private ItemService itemService;
/**
* 接收 http://localhost:端口/items?ids=1,2,3 请求
* @param ids
* @return
*/
@GetMapping
public List<Item> queryItemByIds(@RequestParam("ids") List<Long> ids) {
return itemService.listByIds(ids);
}
}

1.RestTemplate
Spring 给我们提供了一个 RestTemplate 的 API,可以方便的实现 Http 请求的发送。
常见的 Get、Post、Put、Delete 请求都支持。
我们在 cart-service
服务中的启动类中,定义一个RestTemplate对象:

从上图分析,返回的购物车信息中还需要(最新的价格,状态,库存)等字段,因此需要再domain.vo包中定义CartVO类
@Data
//购物车VO实体
public class CartVO {
//购物车条目id
private Long id;
//sku商品id
private Long itemId;
//购买数量
private Integer num;
//商品标题
private String name;
//商品动态属性键值集
private String spec;
//价格,单位:分
private Integer price;
//商品最新价格
private Integer newPrice;
//商品最新状态
private Integer status = 1;
//商品最新库存
private Integer stock = 10;
//商品图片
private String image;
//创建时间
private LocalDateTime createTime;
}

在CartController中注入RestTemplate对象,并加工数据,设置最新的价格和状态以及库存
@RestController
@Slf4j
public class CartController {
@Autowired
ICartService cartService;
@Autowired
RestTemplate restTemplate;
/**
* 访问路径:http://localhost:8081/carts
* @return
*/
@GetMapping("/carts")
public List<CartVO> getCarts() {
log.info("获得购物车数据 getCarts");
//查询出userId=1的购物车数据
List<Cart> carts = cartService.lambdaQuery().eq(Cart::getUserId, 1).list();
if (carts==null||carts.size() == 0){
//如果没有数据,返回一个空的list
return new ArrayList<>();
}
// 2.转换VO
List<CartVO> vos = BeanUtil.copyToList(carts, CartVO.class);
// 3.处理VO中的商品信息
handleCartItems(vos);
//返回数据
return vos;
}
private void handleCartItems(List<CartVO> vos) {
// 1.将vos列表中的对象,提取出itemId,组成字符串 11,12,33这样的格式,使用stream流实现
String joinItemIds = vos.stream()
.map(v -> v.getItemId().toString())
.collect(Collectors.joining(","));
log.info("商品joinItemIds:{}", joinItemIds);
// 2.todo 查询商品
Item[] itemarray = restTemplate
.getForObject("http://localhost:8080/items?ids=" + joinItemIds,
Item[].class);
// 4.判断集合是否为空
if (ArrayUtil.isEmpty(itemarray)) {
return;
}
//转为集合
List<Item> items = Arrays.asList(itemarray);
log.info("商品itemarray:{}", itemarray);
// 5.转为 id 到 item的map
Map<Long, Item> itemMap = items.stream().collect(Collectors.toMap(Item::getId, Function.identity()));
// id 商品
// 1 商品1
// 2 商品2
// 3 商品3
// 6.遍历集合,写入vo
for (CartVO v : vos) {
Item item = itemMap.get(v.getItemId());
if (item == null) {
continue;
}
v.setNewPrice(item.getPrice());// 新价格
v.setStatus(item.getStatus());// 状态
v.setStock(item.getStock());// 库存
}
}
}
好了,现在重启 cart-service
,并确保item-service
也是启动的,再次测试查询我的购物车列表接口:

可以发现,所有商品相关数据都已经查询到了。
在这个过程中,item-service
提供了查询接口,cart-service
利用 Http 请求调用该接口。因此 item-service
可以称为服务的提供者,而 cart-service
则称为服务的消费者或服务调用者。
2.服务注册和发现
2.1.注册中心原理
注册中心是服务治理中的重要组件,用于管理服务实例的数量和状态,提供服务注册和发现的功能。🎯
注册中心原理
在上一章我们实现了微服务拆分,目前有商品微服务和购物车微服务,并且通过 Http 请求实现了跨微服务的远程调用。不过这种手动发送 Http 请求的方式存在一些问题。
试想一下,假如商品微服务被调用较多,为了应对更高的并发,我们进行了多实例部署,如图:

此时,每个 item-service
的实例其 IP 或端口不同,问题来了 :

- item-service 这么多实例,cart-service 如何知道每一个实例的地址?
- http 请求要写 url 地址,
cart-service
服务到底该调用哪个实例呢? - 如果在运行过程中,某一个
item-service
实例宕机,cart-service
依然在调用该怎么办? - 如果并发太高,
item-service
临时多部署了N台实例,cart-service
如何知道新实例的地址?
为了解决上述问题,就必须引入注册中心的概念了,接下来我们就一起来分析下注册中心的原理。🎯
在微服务远程调用的过程中,包括两个角色:
- 服务提供者:提供接口供其它微服务访问,比如
item-service
- 服务消费者:调用其它微服务提供的接口,比如
cart-service
在大型微服务项目中,服务提供者的数量会非常多,为了管理这些服务就引入了注册中心的概念。注册中心、服务提供者、服务消费者三者间关系如下:

流程如下: 👇
- 服务启动时就会注册自己的服务信息(服务名、IP、端口)到注册中心
- 调用者可以从注册中心订阅想要的服务,获取服务对应的实例列表(1 个服务可能多实例部署)
- 调用者自己对实例列表负载均衡,挑选一个实例
- 调用者向该实例发起远程调用
当服务提供者的实例宕机或者启动新实例时,调用者如何得知呢? 👇
- 服务提供者会定期向注册中心发送请求,报告自己的健康状态(心跳请求)
- 当注册中心长时间收不到提供者的心跳时,会认为该实例宕机,将其从服务的实例列表中剔除
- 当服务有新实例启动时,会发送注册服务请求,其信息会被记录在注册中心的服务实例列表
- 当注册中心服务列表变更时,会主动通知微服务,更新本地服务列表
总结
- 服务治理中的三个角色分别是什么?
- 服务提供者:暴露服务接口,供其它服务调用
- 服务消费者:调用其它服务提供的接口
- 注册中心:记录并监控微服务各实例状态,推送服务变更信息
- 消费者如何知道提供者的地址?
- 服务提供者会在启动时注册自己信息到注册中心,消费者可以从注册中心订阅和拉取服务信息
- 消费者如何得知服务状态变更?
- 服务提供者通过心跳机制向注册中心报告自己的健康状态,当心跳异常时注册中心会将异常服务剔除,并通知订阅了该服务的消费者
- 当提供者有多个实例时,消费者该选择哪一个?
- 消费者可以通过负载均衡算法,从多个实例中选择一个
课堂作业
- 什么是注册中心?他有什么功能?🎤
- 服务的提供者向注册中心提供什么信息?🎤
- 服务的调用者从注册中心获取什么信息?🎤
- 点击图片,补充空白区域的内容
2.2.认识和安装Nacos ✏️
国内公司一般都推崇阿里巴巴的技术,比如注册中心,SpringCloudAlibaba也推出了一个名为Nacos的注册中心。
认识和安装Nacos
Nacos是阿里巴巴的产品,现在是SpringCloud中的一个组件。相比Eureka功能更加丰富,在国内受欢迎程度较高。

:::
安装教程码操作
1.1.下载安装包
在Nacos的GitHub页面,提供有下载链接,可以下载编译好的Nacos服务端或者源代码:
GitHub主页:https://github.com/alibaba/nacos
GitHub的Release下载页:https://github.com/alibaba/nacos/releases
如图:

本课程采用1.4.1.版本的Nacos,课前资料已经准备了安装包:

windows版本使用nacos-server-1.4.1.zip
包即可。
1.2.解压
将这个包解压到任意非中文目录下,如图:

启动非常简单,双击点击启动.bat即可
1.3.端口配置
Nacos的默认端口是8848,如果你电脑上的其它进程占用了8848端口,请先尝试关闭该进程。
如果无法关闭占用8848端口的进程,也可以进入nacos的conf目录,修改配置文件中的端口:

修改其中的内容:

1.4.访问
在浏览器输入地址:http://127.0.0.1:8848/nacos

默认的账号和密码都是nacos
,进入后:

2.3.服务注册
服务注册

接下来,我们把 item-service
和cart-service
注册到 Nacos,🎯 步骤如下:
- 引入依赖
- 配置 Nacos 地址
- 重启
代码操作
1.添加依赖
在 item-service
和cart-service
的 pom.xml
中都添加依赖:
<!--nacos 服务注册发现-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
2.配置 Nacos
在 item-service
和cart-service
的 application.yml
中添加 nacos 地址配置:
spring: # 顶格写
application:
name: item-service # 服务名称
cloud:
nacos:
server-addr: localhost:8848 # nacos地址
spring: # 顶格写
application:
name: cart-service # 服务名称
cloud:
nacos:
server-addr: localhost:8848 # nacos地址
配置后,启动2个微服务,并且访问http://127.0.0.1:8848/nacos
用户名和密码都是nacos

实例注册成功
3.启动服务实例
为了测试一个服务多个实例的情况,我们再配置一个 item-service
的部署实例:

然后配置启动项,注意重命名并且配置新的端口,避免冲突:

重启 item-service
的两个实例:

访问 nacos 控制台,可以发现服务注册成功:

点击详情,可以查看到 item-service
服务的两个实例信息:

🎉恭喜你🎉,掌握了nacos服务注册,距离微服务调用越来越近了
总结
课堂作业
- 为什么要进行服务注册?🎤
- 向注册中心注册服务,主要要配置哪些信息?🎤
- 思考一下cart-service远程调用item-service,道理调用哪台服务器尼??