微服务:服务的注册与调用和OpenFiegn

续上篇: SpringBoot:将单体项目拆分成微服务项目-CSDN博客

参考:黑马程序员之微服务

💥 该系列属于【SpringBoot基础】专栏,如您需查看其他SpringBoot相关文章,请您点击左边的连接

目录

前置项目结构

一、服务注册和发现

[1. 基本概念](#1. 基本概念)

[2. Nacos注册中心](#2. Nacos注册中心)

[3. 服务注册【服务提供者在注册中心注册】](#3. 服务注册【服务提供者在注册中心注册】)

[4. 服务发现【服务消费者从注册中心订阅服务】](#4. 服务发现【服务消费者从注册中心订阅服务】)

二、OpenFeign

[1. OpenFeign简介](#1. OpenFeign简介)

[2. 用法](#2. 用法)

[3. OpenFeign基于连接池优化](#3. OpenFeign基于连接池优化)

三、工程项目结构最佳实践

[1. 思路分析](#1. 思路分析)

[2. 抽取Feign客户端](#2. 抽取Feign客户端)

[3. 扫描包](#3. 扫描包)

四、OpenFeign的日志输出

[1. 定义日志级别](#1. 定义日志级别)

[2. 配置](#2. 配置)


前置项目结构

里面有三个微服务,端口号如下:

一、服务注册和发现

在微服务远程调用的过程中,包括两个角色:

  • 服务提供者:提供接口供其它微服务访问,比如item-service

  • 服务消费者:调用其它微服务提供的接口,比如cart-service

在大型微服务项目中,服务提供者的数量会非常多,为了管理这些服务就引入了注册中心的概念。

1. 基本概念

(1)注册中心、服务提供者、服务消费者三者间关系

(2)调用服务的流程

  • 服务启动时就会注册自己的服务信息(服务名、IP、端口)到注册中心

  • 调用者可以从注册中心订阅想要的服务,获取服务对应的实例列表(1个服务可能多实例部署)

  • 调用者自己对实例列表负载均衡,挑选一个实例

  • 调用者向该实例发起远程调用

(3)服务提供者的实例宕机或启动新实例发生的情况

  • 服务提供者会定期向注册中心发送请求,报告自己的健康状态(心跳请求)

  • 当注册中心长时间收不到提供者的心跳时,会认为该实例宕机,将其从服务的实例列表中剔除

  • 当服务有新实例启动时,会发送注册服务请求,其信息会被记录在注册中心的服务实例列表

  • 当注册中心服务列表变更时,会主动通知微服务,更新本地服务列表

2. Nacos注册中心

目前开源的注册中心框架有很多,国内比较常见的有Nacos。Nacos为Alibaba公司出品,目前被集成在SpringCloudAlibaba中,一般用于Java应用。

下面基于Docker来部署Nacos的注册中心,首先要准备MySQL数据库表,用来存储Nacos的数据,然后将SQL文件导入到你Docker中的MySQL容器中,导入后如下:

然后编写nacos环境,保存到nacos/custom.env中:

bash 复制代码
PREFER_HOST_MODE=hostname
MODE=standalone
SPRING_DATASOURCE_PLATFORM=mysql
MYSQL_SERVICE_HOST=192.168.88.128
MYSQL_SERVICE_DB_NAME=nacos
MYSQL_SERVICE_PORT=3306
MYSQL_SERVICE_USER=root
MYSQL_SERVICE_PASSWORD=wangjx17
MYSQL_SERVICE_DB_PARAM=characterEncoding=utf8&connectTimeout=1000&socketTimeout=3000&autoReconnect=true&useSSL=false&allowPublicKeyRetrieval=true&serverTimezone=Asia/Shanghai

nacos目录上传至虚拟机的/root目录。进入root目录,然后执行下面的docker命令:

bash 复制代码
docker run -d \
--name nacos \
--env-file /root/nacos/custom.env \
-p 8848:8848 \
-p 9848:9848 \
-p 9849:9849 \
--restart=always \
nacos/nacos-server:v2.1.0-slim

启动完成后,访问下面地址:
http://192.168.88.128:8848/nacos/,将192.168.88.128替换为自己的虚拟机IP地址。 首次访问会跳转到登录页,账号密码都是nacos

3. 服务注册【服务提供者在注册中心注册】

(1)添加依赖

item-servicepom.xml中添加依赖:

XML 复制代码
<!--nacos 服务注册发现-->
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>

(2)配置Nacos地址

item-serviceapplication.yml中添加nacos地址配置:

bash 复制代码
spring:
  cloud:
    nacos:
      server-addr: 192.168.150.101:8848 # nacos地址

(3)启动服务实例

添加依赖并配置完Nacos地址后,可以启动ItemApplication的项目

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

点击详情查看:

再添加一个实例:

确定,然后启动该8083的springboot项目 ,然后访问nacos控制台:

4. 服务发现【服务消费者从注册中心订阅服务】

(1)添加依赖

cart-servicepom.xml中添加依赖:

XML 复制代码
<!--nacos 服务注册发现-->
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>

任何一个微服务都可以调用别人,也可以被别人调用,即可以是调用者,也可以是提供者。

(2)配置Nacos地址

cart-serviceapplication.yml中添加nacos地址配置:

bash 复制代码
spring:
  cloud:
    nacos:
      server-addr: 192.168.150.101:8848 # nacos地址

任何一个微服务都可以调用别人,也可以被别人调用,即可以是调用者,也可以是提供者。

(3)发现并调用服务

服务调用者cart-service就可以去订阅item-service服务了。不过item-service有多个实例,而真正发起调用时只需要知道一个实例的地址。

因此,服务调用者必须利用负载均衡的算法,从多个实例中挑选一个去访问。常见的负载均衡算法有:随机,轮询,IP的hash,最近最少访问等。这里可以选择最简单的随机负载均衡。

另外,服务发现需要用到一个工具,DiscoveryClient,SpringCloud已经帮我们自动装配,我们可以直接注入使用:

接下来,我们就可以对原来的远程调用做修改了,之前调用时我们需要写死服务提供者的IP和端口。但现在不需要了,我们通过DiscoveryClient发现服务实例列表,然后通过负载均衡算法,选择一个实例去调用:

Swagger测试:

当停止了8081的服务,注册中心可以感受到该服务的宕机,从实例列表剔除;

当8081又重新启动服务,注册中心又监测了该端口的启动,又增加到了实例列表。

二、OpenFeign

1. OpenFeign简介

OpenFeign是一个声明式的http客户端,是SpringCloud在Eureka公司开源的Feign基础上改造而来。其作用就是基于SpringMVC的常见注解,帮我们优雅的实现http请求的发送。

利用RestTemplate实现了服务的远程调用。但是远程调用的代码太复杂了,要写一堆复杂的代码,可读性变差。

以上是原始代码,代码很长!

其实远程调用的关键点就在于四个:

  • 请求方式

  • 请求路径

  • 请求参数

  • 返回值类型

所以,OpenFeign就利用SpringMVC的相关注解来声明上述4个参数,然后基于动态代理帮我们生成远程调用的代码,而无需我们手动再编写,非常方便。

2. 用法

(1)引入依赖【服务消费者一方】

cart-service服务的pom.xml中引入OpenFeign的依赖和loadBalancer依赖:

XML 复制代码
  <!--openFeign-->
  <dependency>
      <groupId>org.springframework.cloud</groupId>
      <artifactId>spring-cloud-starter-openfeign</artifactId>
  </dependency>
  <!--负载均衡器-->
  <dependency>
      <groupId>org.springframework.cloud</groupId>
      <artifactId>spring-cloud-starter-loadbalancer</artifactId>
  </dependency>

(2)启用OpenFeign

cart-serviceCartApplication启动类上添加注解@EnableFeignClients,启动OpenFeign功能:

(3)编写OpenFeign客户端

cart-service中,定义一个新的接口,编写Feign客户端:

XML 复制代码
@FeignClient("item-service") //从注册中心获取微服务的实例
public interface ItemClient {
    //该接口不用手动实现,已经封装好了实现的方法
    @GetMapping("/items")
    List<ItemDTO> queryItemByIds(@RequestParam("ids") Collection<Long> ids);
}

这里只需要声明接口,无需实现方法。接口中的几个关键信息:

  • @FeignClient("item-service") :声明服务名称

  • @GetMapping :声明请求方式

  • @GetMapping("/items") :声明请求路径

  • @RequestParam("ids") Collection<Long> ids :声明请求参数

  • List<ItemDTO> :返回值类型

有了上述信息,OpenFeign就可以利用动态代理帮我们实现这个方法,并且向http://item-service/items发送一个GET请求,携带ids为请求参数,并自动将返回值处理为List<ItemDTO>

我们只需要直接调用这个方法,即可实现远程调用了。

(4)使用FeignClient

cart-servicecom.hmall.cart.service.impl.CartServiceImpl中改造代码,直接调用ItemClient的方法:

仅需一行List<ItemDTO> items = itemClient.queryItemByIds(itemIds);即可查询,当然要先把itemClient从容器中注入。

java 复制代码
    private void handleCartItems(List<CartVO> vos) {
        // 1.获取商品id
        Set<Long> itemIds = vos.stream().map(CartVO::getItemId).collect(Collectors.toSet());

        // 2.查询商品
        List<ItemDTO> items = itemClient.queryItemByIds(itemIds);
        if (CollUtils.isEmpty(items)) {
            return;
        }
        // 3.转为 id 到 item的map
        Map<Long, ItemDTO> itemMap = items.stream().collect(Collectors.toMap(ItemDTO::getId, Function.identity()));
        // 4.写入vo
        for (CartVO v : vos) {
            ItemDTO item = itemMap.get(v.getItemId());
            if (item == null) {
                continue;
            }
            v.setNewPrice(item.getPrice());
            v.setStatus(item.getStatus());
            v.setStock(item.getStock());
        }
    }

3. OpenFeign基于连接池优化

Feign底层发起http请求,依赖于其它的框架。其底层支持的http客户端实现包括:

  • HttpURLConnection:默认实现不支持连接池

  • Apache HttpClient :支持连接池

  • OKHttp:支持连接池

因此我们通常会使用带有连接池的客户端来代替默认的HttpURLConnection。比如,我们使用OK Http.

(1)引入依赖

cart-servicepom.xml中引入依赖:

XML 复制代码
        <!--OK http 的依赖,支持连接池发起HTTP请求 -->
        <dependency>
            <groupId>io.github.openfeign</groupId>
            <artifactId>feign-okhttp</artifactId>
        </dependency>

(2)配置中开启连接池

cart-serviceapplication.yml配置文件中开启Feign的连接池功能:

bash 复制代码
feign:
  okhttp:
    enabled: true # 开启OKHttp功能

重启服务,连接池就生效了。调试后,可以看见使用了OKHTTP方式发送请求:

三、工程项目结构最佳实践

将来我们要把与下单有关的业务抽取为一个独立微服务:trade-service,而它也需要远程调用item-service中的根据id批量查询商品功能。这个需求与cart-service中是一样的。

因此,我们就需要在trade-service中再次定义ItemClient接口,这不是重复编码吗? 有什么办法能加避免重复编码呢?

1. 思路分析

避免重复编码的办法就是抽取。不过这里有两种抽取思路:

  • 思路1:抽取到微服务之外的公共module

  • 思路2:每个微服务自己抽取一个module

方案1抽取更加简单,工程结构也比较清晰,但缺点是整个项目耦合度偏高。

方案2抽取相对麻烦,工程结构相对更复杂,但服务之间耦合度降低。

由于item-service已经创建好,无法继续拆分,因此这里我们采用方案1.

2. 抽取Feign客户端

hmall下定义一个新的module,命名为hm-api

hm-api的依赖如下:

然后把ItemDTO和ItemClient都拷贝过来,最终结构如下:

现在,任何微服务要调用item-service中的接口,只需要引入hm-api模块依赖即可,无需自己编写Feign客户端了。

接下来,我们在cart-servicepom.xml中引入hm-api模块:

XML 复制代码
  <!--feign模块-->
  <dependency>
      <groupId>com.heima</groupId>
      <artifactId>hm-api</artifactId>
      <version>1.0.0</version>
  </dependency>

3. 扫描包

cart-service的启动类定义在com.hmall.cart包下,扫描不到ItemClient,在cart-service的启动类上添加声明即可,两种方式:

添加注解后,再次测试:

四、OpenFeign的日志输出

OpenFeign只会在FeignClient所在包的日志级别为DEBUG时,才会输出日志。而且其日志级别有4级:

  • NONE:不记录任何日志信息,这是默认值。

  • BASIC:仅记录请求的方法,URL以及响应状态码和执行时间

  • HEADERS:在BASIC的基础上,额外记录了请求和响应的头信息

  • FULL:记录所有请求和响应的明细,包括头信息、请求体、元数据。

Feign默认的日志级别就是NONE,所以默认我们看不到请求日志。

1. 定义日志级别

在hm-api模块下新建一个配置类,定义Feign的日志级别:

2. 配置

接下来,要让日志级别生效,还需要配置这个类。可以在启动类的@EnableFeignClients中配置,针对所有FeignClient生效。

java 复制代码
@EnableFeignClients(defaultConfiguration = DefaultFeignConfig.class)
相关推荐
落落落sss6 分钟前
sharding-jdbc分库分表
android·java·开发语言·数据库·servlet·oracle
码爸10 分钟前
flink doris批量sink
java·前端·flink
工业互联网专业43 分钟前
毕业设计选题:基于springboot+vue+uniapp的驾校报名小程序
vue.js·spring boot·小程序·uni-app·毕业设计·源码·课程设计
Monodye1 小时前
【Java】网络编程:TCP_IP协议详解(IP协议数据报文及如何解决IPv4不够的状况)
java·网络·数据结构·算法·系统架构
一丝晨光1 小时前
逻辑运算符
java·c++·python·kotlin·c#·c·逻辑运算符
沛沛老爹1 小时前
服务监控插件全览:提升微服务可观测性的利器
微服务·云原生·架构·datadog·influx·graphite
无名指的等待7121 小时前
SpringBoot中使用ElasticSearch
java·spring boot·后端
Tatakai252 小时前
Mybatis Plus分页查询返回total为0问题
java·spring·bug·mybatis
武子康2 小时前
大数据-133 - ClickHouse 基础概述 全面了解
java·大数据·分布式·clickhouse·flink·spark
.生产的驴2 小时前
SpringBoot 消息队列RabbitMQ 消费者确认机制 失败重试机制
java·spring boot·分布式·后端·rabbitmq·java-rabbitmq