微服务篇-深入了解使用 RestTemplate 远程调用、Nacos 注册中心基本原理与使用、OpenFeign 的基本使用

🔥博客主页: 【小扳_-CSDN博客】**
❤感谢大家点赞👍收藏⭐评论✍**

文章目录

[1.0 认识微服务](#1.0 认识微服务)

[1.1 单体架构](#1.1 单体架构)

[1.2 微服务](#1.2 微服务)

[1.3 SpringCloud 框架](#1.3 SpringCloud 框架)

[2.0 服务调用](#2.0 服务调用)

[2.1 RestTemplate 远程调用](#2.1 RestTemplate 远程调用)

[3.0 服务注册和发现](#3.0 服务注册和发现)

[3.1 注册中心原理](#3.1 注册中心原理)

[3.2 Nacos 注册中心](#3.2 Nacos 注册中心)

[3.3 服务注册](#3.3 服务注册)

[3.3.1 添加依赖](#3.3.1 添加依赖)

[3.3.2 配置 Nacos](#3.3.2 配置 Nacos)

[3.4 服务发现](#3.4 服务发现)

[3.4.1 引入依赖](#3.4.1 引入依赖)

[3.4.2 配置 Nacos 地址](#3.4.2 配置 Nacos 地址)

[3.4.3 发现并调用服务](#3.4.3 发现并调用服务)

[4.0 OpenFeign](#4.0 OpenFeign)

[4.1 引入依赖](#4.1 引入依赖)

[4.2 启动 OpenFeign](#4.2 启动 OpenFeign)

[4.3 使用 FeignClient](#4.3 使用 FeignClient)

[4.4 扫描包](#4.4 扫描包)

[5.0 连接池](#5.0 连接池)

[5.1 引入依赖](#5.1 引入依赖)

[5.2 开启连接池](#5.2 开启连接池)

[6.0 日志配置](#6.0 日志配置)

[6.1 定义日志级别](#6.1 定义日志级别)

[6.2 配置](#6.2 配置)


1.0 认识微服务

服务(Microservices)是一种软件架构风格,它将一个应用程序构建为一组小的、独立的服务,每个服务可以独立开发、部署和扩展。这种架构相较于传统的单体应用(Monolithic Architecture)具有许多优势,尤其是在灵活性和可维护性方面。

1.1 单体架构

单体架构(monolithic structure):顾名思义,整个项目中所有功能模块都在一个工程中开发;项目部署时需要对所有模块一起编译、打包;项目的架构设计、开发模式都非常简单。

当项目规模较小时,这种模式上手快,部署、运维也都很方便,因此早期很多小型项目都采用这种模式。

**但随着项目的业务规模越来越大,团队开发人员也不断增加,单体架构就呈现出越来越多的问题:

  • 团队协作成本高:试想一下,你们团队数十个人同时协作开发同一个项目,由于所有模块都在一个项目中,不同模块的代码之间物理边界越来越模糊。最终要把功能合并到一个分支,你绝对会陷入到解决冲突的泥潭之中。
  • 系统发布效率低:任何模块变更都需要发布整个系统,而系统发布过程中需要多个模块之间制约较多,需要对比各种文件,任何一处出现问题都会导致发布失败,往往一次发布需要数十分钟甚至数小时。
  • 系统可用性差:单体架构各个功能模块是作为一个服务部署,相互之间会互相影响,一些热点功能会耗尽系统资源,导致其它服务低可用。**

1.2 微服务

**微服务架构,首先是服务化,就是将单体架构中的功能模块从单体应用中拆分出来,独立部署为多个服务。同时要满足下面的一些特点:

  • 单一职责:一个微服务负责一部分业务功能,并且其核心数据不依赖于其它模块。
  • 团队自治:每个微服务都有自己独立的开发、测试、发布、运维人员,团队人员规模不超过10人(2张披萨能喂饱)
  • 服务自治:每个微服务都独立打包部署,访问自己独立的数据库。并且要做好服务隔离,避免对其它服务产生影响**

1.3 SpringCloud 框架

微服务拆分以后碰到的各种问题都有对应的解决方案和微服务组件,而 SpringCloud 框架可以说是目前 Java 领域最全面的微服务组件的集合了。

而且 SpringCloud 依托于 SpringBoot 的自动装配能力,大大降低了其项目搭建、组件使用的成本。对于没有自研微服务组件能力的中小型企业,使用 SpringCloud 全家桶来实现微服务开发可以说是最合适的选择了。

SpringCloud 官网:Spring Cloud

SpringCloud 相关的依赖:

XML 复制代码
            <!--spring cloud-->
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>${spring-cloud.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
            <!--spring cloud alibaba-->
            <dependency>
                <groupId>com.alibaba.cloud</groupId>
                <artifactId>spring-cloud-alibaba-dependencies</artifactId>
                <version>${spring-cloud-alibaba.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>

2.0 服务调用

在微服务中,每一个模块都是独立的,那么模块与模块之间就不能直接通过 Bean 对象注入的方式来访问不同模块的方法了,则需要通过网络来建立模块与模块之间的联系了。

前端向服务端查询数据,其实就是从浏览器远程查询服务端数据。比如刚才通过 Swagger 测试商品查询接口,而这种查询就是通过 http 请求的方式来完成的,不仅仅可以实现远程查询,还可以实现新增、删除等各种远程请求。

2.1 RestTemplate 远程调用

Spring 提供了一个 RestTemplate 的API,可以方便的实现 Http 请求的发送。

其中提供了大量的方法,方便发送 Http 请求,例如:

可以看到常见的 Get、Post、Put、Delete 请求都支持,如果请求参数比较复杂,还可以使用 exchange 方法来构造请求。

使用步骤:

1)先创建一个 IOC 容器对象:

java 复制代码
package com.hmall.cart.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;

@Configuration
public class RemoteCallConfig {

    @Bean
    public RestTemplate restTemplate() {
        return new RestTemplate();
    }
}

2)发送 HTTP 请求:

**可以看到,利用 RestTemplate 发送 http 请求与前端 ajax 发送请求非常相似,都包含四部分信息:

  • ① 请求方式
  • ② 请求路径
  • ③ 请求参数
  • ④ 返回值类型**

3.0 服务注册和发现

通过 Http 请求实现了跨微服务的远程调用。不过这种手动发送 Http 请求的方式存在一些问题。试想一下,假如商品微服务被调用较多,为了应对更高的并发,进行了多实例部署,如图:

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

  • item-service 这么多实例,cart-service 如何知道每一个实例的地址?
  • http 请求要写 url 地址,cart-service 服务到底该调用哪个实例呢?
  • 如果在运行过程中,某一个 item-service 实例宕机,cart-service 依然在调用该怎么办?
  • 如果并发太高,item-service 临时多部署了N台实例,cart-service 如何知道新实例的地址?**

为了解决上述问题,就必须引入注册中心的概念了。

3.1 注册中心原理

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

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

在大型微服务项目中,服务提供者的数量会非常多,为了管理这些服务就引入了注册中心的概念。注册中心、服务提供者、服务消费者三者间关系如下:

**流程如下:

  • 服务启动时就会注册自己的服务信息(服务名、IP、端口)到注册中心。
  • 调用者可以从注册中心订阅想要的服务,获取服务对应的实例列表(1个服务可能多实例部署)
  • 调用者自己对实例列表负载均衡,挑选一个实例。
  • 调用者向该实例发起远程调用。**

**当服务提供者的实例宕机或者启动新实例时,调用者如何得知呢?

  • 服务提供者会定期向注册中心发送请求,报告自己的健康状态(心跳请求),当注册中心长时间收不到提供者的心跳时,会认为该实例宕机,将其从服务的实例列表中剔除。
  • 当服务有新实例启动时,会发送注册服务请求,其信息会被记录在注册中心的服务实例列表。
  • 当注册中心服务列表变更时,会主动通知微服务,更新本地服务列表。**

3.2 Nacos 注册中心

**目前开源的注册中心框架有很多,国内比较常见的有:

  • Eureka:Netflix 公司出品,目前被集成在 SpringCloud 当中,一般用于 Java 应用。
  • Nacos:Alibaba 公司出品,目前被集成在 SpringCloudAlibaba 中,一般用于 Java 应用。
  • Consul:HashiCorp 公司出品,目前集成在 SpringCloud 中,不限制微服务语言。**

以上几种注册中心都遵循 SpringCloud 中的 API 规范,因此在业务开发使用上没有太大差异。由于 Nacos 是国内产品,中文文档比较丰富,而且同时具备配置管理功能,因此在国内使用较多。

基于 Docker 来部署 Nacos 的注册中心,首先要准备 MySQL 数据库表,用来存储 Nacos 的数据。

1)首先配置相关的数据库表信息:

Nacos 相关存储的数据库:

对应的数据库语句:

sql 复制代码
/******************************************/
/*   数据库全名 = nacos_config   */
/*   表名称 = config_info   */
/******************************************/
CREATE TABLE `config_info` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'id',
  `data_id` varchar(255) NOT NULL COMMENT 'data_id',
  `group_id` varchar(255) DEFAULT NULL,
  `content` longtext NOT NULL COMMENT 'content',
  `md5` varchar(32) DEFAULT NULL COMMENT 'md5',
  `gmt_create` datetime NOT NULL DEFAULT '2010-05-05 00:00:00' COMMENT '创建时间',
  `gmt_modified` datetime NOT NULL DEFAULT '2010-05-05 00:00:00' COMMENT '修改时间',
  `src_user` text COMMENT 'source user',
  `src_ip` varchar(20) DEFAULT NULL COMMENT 'source ip',
  `app_name` varchar(128) DEFAULT NULL,
  `tenant_id` varchar(128) DEFAULT '' COMMENT '租户字段',
  `c_desc` varchar(256) DEFAULT NULL,
  `c_use` varchar(64) DEFAULT NULL,
  `effect` varchar(64) DEFAULT NULL,
  `type` varchar(64) DEFAULT NULL,
  `c_schema` text,
  PRIMARY KEY (`id`),
  UNIQUE KEY `uk_configinfo_datagrouptenant` (`data_id`,`group_id`,`tenant_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='config_info';
 
/******************************************/
/*   数据库全名 = nacos_config   */
/*   表名称 = config_info_aggr   */
/******************************************/
CREATE TABLE `config_info_aggr` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'id',
  `data_id` varchar(255) NOT NULL COMMENT 'data_id',
  `group_id` varchar(255) NOT NULL COMMENT 'group_id',
  `datum_id` varchar(255) NOT NULL COMMENT 'datum_id',
  `content` longtext NOT NULL COMMENT '内容',
  `gmt_modified` datetime NOT NULL COMMENT '修改时间',
  `app_name` varchar(128) DEFAULT NULL,
  `tenant_id` varchar(128) DEFAULT '' COMMENT '租户字段',
  PRIMARY KEY (`id`),
  UNIQUE KEY `uk_configinfoaggr_datagrouptenantdatum` (`data_id`,`group_id`,`tenant_id`,`datum_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='增加租户字段';
 
 
/******************************************/
/*   数据库全名 = nacos_config   */
/*   表名称 = config_info_beta   */
/******************************************/
CREATE TABLE `config_info_beta` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'id',
  `data_id` varchar(255) NOT NULL COMMENT 'data_id',
  `group_id` varchar(128) NOT NULL COMMENT 'group_id',
  `app_name` varchar(128) DEFAULT NULL COMMENT 'app_name',
  `content` longtext NOT NULL COMMENT 'content',
  `beta_ips` varchar(1024) DEFAULT NULL COMMENT 'betaIps',
  `md5` varchar(32) DEFAULT NULL COMMENT 'md5',
  `gmt_create` datetime NOT NULL DEFAULT '2010-05-05 00:00:00' COMMENT '创建时间',
  `gmt_modified` datetime NOT NULL DEFAULT '2010-05-05 00:00:00' COMMENT '修改时间',
  `src_user` text COMMENT 'source user',
  `src_ip` varchar(20) DEFAULT NULL COMMENT 'source ip',
  `tenant_id` varchar(128) DEFAULT '' COMMENT '租户字段',
  PRIMARY KEY (`id`),
  UNIQUE KEY `uk_configinfobeta_datagrouptenant` (`data_id`,`group_id`,`tenant_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='config_info_beta';
 
/******************************************/
/*   数据库全名 = nacos_config   */
/*   表名称 = config_info_tag   */
/******************************************/
CREATE TABLE `config_info_tag` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'id',
  `data_id` varchar(255) NOT NULL COMMENT 'data_id',
  `group_id` varchar(128) NOT NULL COMMENT 'group_id',
  `tenant_id` varchar(128) DEFAULT '' COMMENT 'tenant_id',
  `tag_id` varchar(128) NOT NULL COMMENT 'tag_id',
  `app_name` varchar(128) DEFAULT NULL COMMENT 'app_name',
  `content` longtext NOT NULL COMMENT 'content',
  `md5` varchar(32) DEFAULT NULL COMMENT 'md5',
  `gmt_create` datetime NOT NULL DEFAULT '2010-05-05 00:00:00' COMMENT '创建时间',
  `gmt_modified` datetime NOT NULL DEFAULT '2010-05-05 00:00:00' COMMENT '修改时间',
  `src_user` text COMMENT 'source user',
  `src_ip` varchar(20) DEFAULT NULL COMMENT 'source ip',
  PRIMARY KEY (`id`),
  UNIQUE KEY `uk_configinfotag_datagrouptenanttag` (`data_id`,`group_id`,`tenant_id`,`tag_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='config_info_tag';
 
/******************************************/
/*   数据库全名 = nacos_config   */
/*   表名称 = config_tags_relation   */
/******************************************/
CREATE TABLE `config_tags_relation` (
  `id` bigint(20) NOT NULL COMMENT 'id',
  `tag_name` varchar(128) NOT NULL COMMENT 'tag_name',
  `tag_type` varchar(64) DEFAULT NULL COMMENT 'tag_type',
  `data_id` varchar(255) NOT NULL COMMENT 'data_id',
  `group_id` varchar(128) NOT NULL COMMENT 'group_id',
  `tenant_id` varchar(128) DEFAULT '' COMMENT 'tenant_id',
  `nid` bigint(20) NOT NULL AUTO_INCREMENT,
  PRIMARY KEY (`nid`),
  UNIQUE KEY `uk_configtagrelation_configidtag` (`id`,`tag_name`,`tag_type`),
  KEY `idx_tenant_id` (`tenant_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='config_tag_relation';
 
/******************************************/
/*   数据库全名 = nacos_config   */
/*   表名称 = group_capacity   */
/******************************************/
CREATE TABLE `group_capacity` (
  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键ID',
  `group_id` varchar(128) NOT NULL DEFAULT '' COMMENT 'Group ID,空字符表示整个集群',
  `quota` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '配额,0表示使用默认值',
  `usage` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '使用量',
  `max_size` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '单个配置大小上限,单位为字节,0表示使用默认值',
  `max_aggr_count` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '聚合子配置最大个数,,0表示使用默认值',
  `max_aggr_size` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '单个聚合数据的子配置大小上限,单位为字节,0表示使用默认值',
  `max_history_count` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '最大变更历史数量',
  `gmt_create` datetime NOT NULL DEFAULT '2010-05-05 00:00:00' COMMENT '创建时间',
  `gmt_modified` datetime NOT NULL DEFAULT '2010-05-05 00:00:00' COMMENT '修改时间',
  PRIMARY KEY (`id`),
  UNIQUE KEY `uk_group_id` (`group_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='集群、各Group容量信息表';
 
/******************************************/
/*   数据库全名 = nacos_config   */
/*   表名称 = his_config_info   */
/******************************************/
CREATE TABLE `his_config_info` (
  `id` bigint(64) unsigned NOT NULL,
  `nid` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
  `data_id` varchar(255) NOT NULL,
  `group_id` varchar(128) NOT NULL,
  `app_name` varchar(128) DEFAULT NULL COMMENT 'app_name',
  `content` longtext NOT NULL,
  `md5` varchar(32) DEFAULT NULL,
  `gmt_create` datetime NOT NULL DEFAULT '2010-05-05 00:00:00',
  `gmt_modified` datetime NOT NULL DEFAULT '2010-05-05 00:00:00',
  `src_user` text,
  `src_ip` varchar(20) DEFAULT NULL,
  `op_type` char(10) DEFAULT NULL,
  `tenant_id` varchar(128) DEFAULT '' COMMENT '租户字段',
  PRIMARY KEY (`nid`),
  KEY `idx_gmt_create` (`gmt_create`),
  KEY `idx_gmt_modified` (`gmt_modified`),
  KEY `idx_did` (`data_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='多租户改造';
 
 
/******************************************/
/*   数据库全名 = nacos_config   */
/*   表名称 = tenant_capacity   */
/******************************************/
CREATE TABLE `tenant_capacity` (
  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键ID',
  `tenant_id` varchar(128) NOT NULL DEFAULT '' COMMENT 'Tenant ID',
  `quota` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '配额,0表示使用默认值',
  `usage` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '使用量',
  `max_size` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '单个配置大小上限,单位为字节,0表示使用默认值',
  `max_aggr_count` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '聚合子配置最大个数',
  `max_aggr_size` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '单个聚合数据的子配置大小上限,单位为字节,0表示使用默认值',
  `max_history_count` int(10) unsigned NOT NULL DEFAULT '0' COMMENT '最大变更历史数量',
  `gmt_create` datetime NOT NULL DEFAULT '2010-05-05 00:00:00' COMMENT '创建时间',
  `gmt_modified` datetime NOT NULL DEFAULT '2010-05-05 00:00:00' COMMENT '修改时间',
  PRIMARY KEY (`id`),
  UNIQUE KEY `uk_tenant_id` (`tenant_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='租户容量信息表';
 
 
CREATE TABLE `tenant_info` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'id',
  `kp` varchar(128) NOT NULL COMMENT 'kp',
  `tenant_id` varchar(128) default '' COMMENT 'tenant_id',
  `tenant_name` varchar(128) default '' COMMENT 'tenant_name',
  `tenant_desc` varchar(256) DEFAULT NULL COMMENT 'tenant_desc',
  `create_source` varchar(32) DEFAULT NULL COMMENT 'create_source',
  `gmt_create` bigint(20) NOT NULL COMMENT '创建时间',
  `gmt_modified` bigint(20) NOT NULL COMMENT '修改时间',
  PRIMARY KEY (`id`),
  UNIQUE KEY `uk_tenant_info_kptenantid` (`kp`,`tenant_id`),
  KEY `idx_tenant_id` (`tenant_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='tenant_info';
 
CREATE TABLE users (
    username varchar(50) NOT NULL PRIMARY KEY,
    password varchar(500) NOT NULL,
    enabled boolean NOT NULL
);
 
CREATE TABLE roles (
    username varchar(50) NOT NULL,
    role varchar(50) NOT NULL,
    constraint uk_username_role UNIQUE (username,role)
);
 
CREATE TABLE permissions (
    role varchar(50) NOT NULL,
    resource varchar(512) NOT NULL,
    action varchar(8) NOT NULL,
    constraint uk_role_permission UNIQUE (role,resource,action)
);
 
INSERT INTO users (username, password, enabled) VALUES ('nacos', '$2a$10$EuWPZHzz32dJN7jexM34MOeYirDdFAZm2kuWj7VEOJhhZkDrxfvUu', TRUE);
 
INSERT INTO roles (username, role) VALUES ('nacos', 'ROLE_ADMIN');

2)配置 Nacos 信息:

nacos/custom.env 文件中,有一个 MYSQL_SERVICE_HOST 也就是 mysql 地址,需要修改为你自己的虚拟机 IP 地址:

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

将配置好的 nacos/custom.env 文件放到 /root 目录下。

接着,进入 root 目录,然后执行下面的 docker 命令:

XML 复制代码
docker run -d \
--name nacos \
--env-file ./nacos/custom.env \
-e JVM_XMS=256m -e JVM_XMX=256m \
-p 8848:8848 \
-p 9848:9848 \
-p 9849:9849 \
--restart=always \
--network hm-net\
nacos/nacos-server:v2.1.0-slim

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

3.3 服务注册

**接下来,把服务注册到 Nacos,步骤如下:

  • 引入依赖
  • 配置 Nacos 地址
  • 重启**

3.3.1 添加依赖

添加依赖:

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

3.3.2 配置 Nacos

添加 nacos 地址配置:

Matlab 复制代码
spring:
  application:
    name: item-service # 服务名称
  cloud:
    nacos:
      server-addr: 192.168.150.101:8848 # nacos地址

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

3.4 服务发现

**服务的消费者要去 nacos 订阅服务,这个过程就是服务发现,步骤如下:

  • 引入依赖
  • 配置 Nacos 地址
  • 发现并调用服务**

3.4.1 引入依赖

在 pom.xml 中添加下面的依赖:

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

可以发现,这里 Nacos 的依赖于服务注册时一致,这个依赖中同时包含了服务注册和发现的功能。因为任何一个微服务都可以调用别人,也可以被别人调用,即可以是调用者,也可以是提供者。

3.4.2 配置 Nacos 地址

添加 nacos 地址配置:

Matlab 复制代码
spring:
  cloud:
    nacos:
      server-addr: 192.168.150.101:8848

3.4.3 发现并调用服务

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

接下来,我们就可以对原来的远程调用做修改了,之前调用时我们需要写死服务提供者的 IP 和端口:

但现在不需要了,我们通过 DiscoveryClient 发现服务实例列表,然后通过负载均衡算法,选择一个实例去调用:

4.0 OpenFeign

利用 Nacos 实现了服务的治理,利用 RestTemplate 实现了服务的远程调用。但是远程调用的代码太复杂了:

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

4.1 引入依赖

在服务的 pom.xml 中引入 OpenFeign 的依赖和 loadBalancer 依赖:

Matlab 复制代码
  <!--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>

4.2 启动 OpenFeign

接下来,在启动类上添加注解,启动 OpenFeign 功能:

4.3 使用 FeignClient

在服务中,定义一个新的接口,编写 Feign 客户端:
其中代码如下:

Matlab 复制代码
import com.hmall.api.dto.ItemDTO;
import com.hmall.api.dto.OrderDetailDTO;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.*;

import java.util.Collection;
import java.util.List;

@FeignClient("item-service")
public interface ItemClient {

    @GetMapping("/items")
    List<ItemDTO> queryItemByIds(@RequestParam("ids") Collection<Long> ids);

    @PutMapping("/items/stock/deduct")
    void deductStock(@RequestBody Collection<OrderDetailDTO> items);
}

解析:

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

  • @FeignClient("item-service") :声明服务名称
  • @GetMapping :声明请求方式
  • @GetMapping("/items") :声明请求路径
  • @RequestParam("ids") Collection<Long> ids :声明请求参数
  • List<ItemDTO> :返回值类型**

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

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

feign 替我们完成了服务拉取、负载均衡、发送 http 请求的所有工作,是不是看起来优雅多了。而且,这里我们不再需要 RestTemplate 了,还省去了 RestTemplate 的注册。

4.4 扫描包

如果是使用了单独模块来管理全部的 FeignClient 接口,那么需要添加声明:

1)第一种方式:声明扫描包

2)第二种方式:声明要用的 FeignClient

5.0 连接池

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

  • HttpURLConnection:默认实现,不支持连接池
  • Apache HttpClient :支持连接池
  • OKHttp:支持连接池**

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

5.1 引入依赖

在 pom.xml 中引入依赖:

XML 复制代码
<!--OK http 的依赖 -->
<dependency>
  <groupId>io.github.openfeign</groupId>
  <artifactId>feign-okhttp</artifactId>
</dependency>

5.2 开启连接池

在 application.yml 配置文件中开启 Feign 的连接池功能:

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

重启服务,连接池就生效了。

6.0 日志配置

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

  • NONE:不记录任何日志信息,这是默认值。
  • BASIC:仅记录请求的方法,URL以及响应状态码和执行时间
  • HEADERS:在BASIC的基础上,额外记录了请求和响应的头信息
  • FULL:记录所有请求和响应的明细,包括头信息、请求体、元数据。
    Feign 默认的日志级别就是 NONE,所以默认我们看不到请求日志。**

6.1 定义日志级别

新建一个配置类,定义 Feign 的日志级别:

代码如下:

java 复制代码
package com.hmall.api.config;

import feign.Logger;
import org.springframework.context.annotation.Bean;

public class DefaultFeignConfig {
    @Bean
    public Logger.Level feignLogLevel(){
        return Logger.Level.FULL;
    }
}

6.2 配置

接下来,要让日志级别生效,还需要配置这个类。有两种方式:
1)局部生效:在某个 FeignClient 中配置,只对当前 FeignClient 生效:

java 复制代码
@FeignClient(value = "item-service", configuration = DefaultFeignConfig.class)

2)全局生效:在 @EnableFeignClients 中配置,针对所有 FeignClient 生效:

java 复制代码
@EnableFeignClients(defaultConfiguration = DefaultFeignConfig.class)

代码如下:

java 复制代码
@EnableFeignClients(basePackages = "com.hmall.api.client",defaultConfiguration = DefaultFeignConfig.class)
@MapperScan("com.hmall.item.mapper")
@SpringBootApplication
public class ItemApplication {
    public static void main(String[] args) {
        SpringApplication.run(ItemApplication.class, args);
    }
}
相关推荐
Danileaf_Guo3 小时前
256台H100服务器算力中心的带外管理网络建设方案
运维·服务器
章豪Mrrey nical3 小时前
前后端分离工作详解Detailed Explanation of Frontend-Backend Separation Work
后端·前端框架·状态模式
超级大只老咪4 小时前
数组相邻元素比较的循环条件(Java竞赛考点)
java
小浣熊熊熊熊熊熊熊丶4 小时前
《Effective Java》第25条:限制源文件为单个顶级类
java·开发语言·effective java
毕设源码-钟学长4 小时前
【开题答辩全过程】以 公交管理系统为例,包含答辩的问题和答案
java·eclipse
啃火龙果的兔子4 小时前
JDK 安装配置
java·开发语言
星哥说事4 小时前
应用程序监控:Java 与 Web 应用的实践
java·开发语言
派大鑫wink4 小时前
【JAVA学习日志】SpringBoot 参数配置:从基础到实战,解锁灵活配置新姿势
java·spring boot·后端
程序员爱钓鱼5 小时前
Node.js 编程实战:文件读写操作
前端·后端·node.js
xUxIAOrUIII5 小时前
【Spring Boot】控制器Controller方法
java·spring boot·后端