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

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

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

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

目录

前置项目结构

一、服务注册和发现

1. 基本概念

2. Nacos注册中心

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

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

二、OpenFeign 

1. OpenFeign简介

2. 用法

3. OpenFeign基于连接池优化

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

1. 思路分析

2. 抽取Feign客户端

3. 扫描包

四、OpenFeign的日志输出

1. 定义日志级别

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中:

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命令:

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中添加依赖:

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

(2)配置Nacos地址

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

spring:
  cloud:
    nacos:
      server-addr: 192.168.150.101:8848 # nacos地址

(3)启动服务实例

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

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

点击详情查看:

 再添加一个实例:

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

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

(1)添加依赖

cart-servicepom.xml中添加依赖:

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

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

(2)配置Nacos地址

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

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依赖:

  <!--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客户端:

@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从容器中注入。

    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中引入依赖:

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

(2)配置中开启连接池

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

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模块:

  <!--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生效。

@EnableFeignClients(defaultConfiguration = DefaultFeignConfig.class)

  • 22
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值