程序员scholar 程序员scholar
首页
  • Java 基础

    • JavaSE
    • JavaIO
    • JavaAPI速查
  • Java 高级

    • JUC
    • JVM
    • Java新特性
    • 设计模式
  • Web 开发

    • Servlet
    • Java网络编程
  • Web 标准

    • HTML
    • CSS
    • JavaScript
  • 前端框架

    • Vue2
    • Vue3
    • Vue3 + TS
    • 微信小程序
    • uni-app
  • 工具与库

    • jQuery
    • Ajax
    • Axios
    • Webpack
    • Vuex
    • WebSocket
    • 第三方登录
  • 后端与语言扩展

    • ES6
    • Typescript
    • node.js
  • Element-UI
  • Apache ECharts
  • 数据结构
  • HTTP协议
  • HTTPS协议
  • 计算机网络
  • Linux常用命令
  • Windows常用命令
  • SQL数据库

    • MySQL
    • MySQL速查
  • NoSQL数据库

    • Redis
    • ElasticSearch
  • 数据库

    • MyBatis
    • MyBatis-Plus
  • 消息中间件

    • RabbitMQ
  • 服务器

    • Nginx
  • Spring框架

    • Spring6
    • SpringMVC
    • SpringBoot
    • SpringSecurity
  • SpringCould微服务

    • SpringCloud基础
    • 微服务之DDD架构思想
  • 日常必备

    • 开发常用工具包
    • Hutoll工具包
    • IDEA常用配置
    • 开发笔记
    • 日常记录
    • 项目部署
    • 网站导航
    • 产品学习
    • 英语学习
  • 代码管理

    • Maven
    • Git教程
    • Git小乌龟教程
  • 运维工具

    • Docker
    • Jenkins
    • Kubernetes
  • 算法笔记

    • 算法思想
    • 刷题笔记
  • 面试问题常见

    • 十大经典排序算法
    • 面试常见问题集锦
关于
GitHub (opens new window)
首页
  • Java 基础

    • JavaSE
    • JavaIO
    • JavaAPI速查
  • Java 高级

    • JUC
    • JVM
    • Java新特性
    • 设计模式
  • Web 开发

    • Servlet
    • Java网络编程
  • Web 标准

    • HTML
    • CSS
    • JavaScript
  • 前端框架

    • Vue2
    • Vue3
    • Vue3 + TS
    • 微信小程序
    • uni-app
  • 工具与库

    • jQuery
    • Ajax
    • Axios
    • Webpack
    • Vuex
    • WebSocket
    • 第三方登录
  • 后端与语言扩展

    • ES6
    • Typescript
    • node.js
  • Element-UI
  • Apache ECharts
  • 数据结构
  • HTTP协议
  • HTTPS协议
  • 计算机网络
  • Linux常用命令
  • Windows常用命令
  • SQL数据库

    • MySQL
    • MySQL速查
  • NoSQL数据库

    • Redis
    • ElasticSearch
  • 数据库

    • MyBatis
    • MyBatis-Plus
  • 消息中间件

    • RabbitMQ
  • 服务器

    • Nginx
  • Spring框架

    • Spring6
    • SpringMVC
    • SpringBoot
    • SpringSecurity
  • SpringCould微服务

    • SpringCloud基础
    • 微服务之DDD架构思想
  • 日常必备

    • 开发常用工具包
    • Hutoll工具包
    • IDEA常用配置
    • 开发笔记
    • 日常记录
    • 项目部署
    • 网站导航
    • 产品学习
    • 英语学习
  • 代码管理

    • Maven
    • Git教程
    • Git小乌龟教程
  • 运维工具

    • Docker
    • Jenkins
    • Kubernetes
  • 算法笔记

    • 算法思想
    • 刷题笔记
  • 面试问题常见

    • 十大经典排序算法
    • 面试常见问题集锦
关于
GitHub (opens new window)
npm

(进入注册为作者充电)

  • 微服务基础

    • 了解SpringCloud微服务架构
    • 启动多个微服务项目
    • Eureka 服务注册
    • Ribbon 负载均衡
    • Nacos 下载与安装
    • Nacos 服务注册
    • Nacos 配置管理
    • openFeign 远程调用
      • 1. 问题分析
      • 2. 了解 Feign
      • 3. 项目整合 Feign
        • 3.1 引入依赖
        • 3.2 添加注解
        • 3.3 编写 Feign 客户端接口
        • 3.4 使用 Feign 客户端
        • 3.5 Feign 的工作原理
        • 3.6 Feign使用小结
      • 4. 自定义配置
        • 4.1 配置文件方式
        • 针对单个服务的配置
        • 针对所有服务的配置
        • 日志级别说明
        • 4.2 基于 Java 代码的配置方式
        • 创建自定义配置类
        • 配置全局生效
        • 配置局部生效
        • 4.3 配置日志级别的使用场景
        • 4.4 配置方式总结
      • 5. Feign使用优化
        • 5. Feign 使用优化
        • 5.1 引入依赖
        • 5.2 配置连接池
        • 5.3 验证优化结果
        • 5.4 Feign 优化总结
    • OpenFeign 最佳实践
    • Gateway 网关
    • Gateway 进阶使用
    • Sentinel 服务保护
    • Sentinel 整合 Feign
    • 分布式事务 Seata
    • 什么是分布式
    • 什么是RPC框架
  • 微服务之DDD架构思想

  • 微服务
  • 微服务基础
scholar
2024-08-24
目录

openFeign 远程调用

# OpenFeign 远程调用

# 1. 问题分析

在传统的微服务架构中,我们通常使用 RestTemplate 进行服务间的 HTTP 调用。来看一下之前使用 RestTemplate 发起远程调用的代码:

image-20210714174814204

存在的问题:

  1. URL 地址被硬编码到代码中,后期维护和修改不方便。
  2. 服务消费者无法直接感知服务提供者的状态,例如健康状态、异常等信息。
  3. 若服务提供者出现问题,服务消费者会抛出异常,导致用户看到异常页面,影响用户体验。
  4. RestTemplate 方式的代码显得不够优雅,是否有更好的解决方案可以提高代码的可读性和可维护性?

# 2. 了解 Feign

Feign 是一个声明式的 HTTP 客户端,GitHub 地址:https://github.com/OpenFeign/feign (opens new window)。

它可以帮助我们通过简单的接口声明方式,实现服务间的 HTTP 请求调用,解决上述问题。

image-20210714174918088

# 3. 项目整合 Feign

# 3.1 引入依赖

在 order-service 服务的 pom.xml 文件中引入 Feign 的依赖:

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
1
2
3
4

说明:引入的 Feign 依赖为我们提供了声明式的 HTTP 客户端,简化了微服务之间的远程调用。

# 3.2 添加注解

在 order-service 的启动类中添加 @EnableFeignClients 注解以开启 Feign 的功能:

@SpringBootApplication
@EnableFeignClients // 启用 Feign 客户端
public class OrderApplication {
    public static void main(String[] args) {
        SpringApplication.run(OrderApplication.class, args);
    }
}
1
2
3
4
5
6
7

说明:@EnableFeignClients 注解用于启用 Feign 客户端,它会扫描并注册当前包及其子包下的所有使用了 @FeignClient 注解的接口。

# 3.3 编写 Feign 客户端接口

创建一个 Feign 客户端接口,用于声明远程调用的服务:

package com.cisyam.order.client;

import com.cisyam.pojo.User;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;

/**
 * 用户服务的 Feign 客户端接口
 */
@FeignClient("userservice") // 指定要调用的服务名称,Feign 会自动根据服务名称调用对应的服务
public interface UserClient {

    /**
     * 通过用户 ID 获取用户信息
     * @param id 用户 ID
     * @return 用户信息
     */
    @GetMapping("/user/{id}")
    User findById(@PathVariable("id") Long id);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

说明:

  • @FeignClient("userservice"):表示这个接口是一个 Feign 客户端,它负责调用名为 userservice 的微服务,Feign 会自动去注册中心(如 Nacos)查找这个服务。
  • @GetMapping("/user/{id}"):Feign 接口的声明方式与 SpringMVC 类似,指定请求的路径、请求方式以及路径参数。
  • Feign 通过这种声明式接口的方式,将远程服务调用简化为本地方法调用,提升了代码的可读性和可维护性。

# 3.4 使用 Feign 客户端

修改 order-service 中的 OrderService 类,将原先使用 RestTemplate 进行远程调用的代码替换为 Feign 客户端:

@Service
public class OrderService {

    @Autowired
    private OrderMapper orderMapper;

    @Autowired
    private UserClient userClient; // 注入 Feign 客户端

    public Order queryOrderById(Long orderId) {
        // 1. 查询订单
        Order order = orderMapper.findById(orderId);

        // 2. 通过 Feign 客户端调用用户服务,获取用户信息
        User user = userClient.findById(order.getUserId());

        // 3. 封装用户信息到订单中
        order.setUser(user);

        // 4. 返回订单信息
        return order;
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

重点说明

  • Feign 客户端 userClient 会根据我们定义的接口声明,自动生成对应的实现,在调用 findById 方法时,它会发起 HTTP 请求,调用远程的 userservice 服务的 /user/{id} 接口。
  • 这样,我们不需要自己去构造 URL,也不需要自己发起 HTTP 请求,Feign 已经帮我们完成了这些操作,代码更加简洁、可维护。

# 3.5 Feign 的工作原理

当我们使用 Feign 时,UserClient 只是一个接口,本身并没有实现类。Feign 在运行时会动态生成这个接口的实现类,具体的调用逻辑由 Feign 负责。

当我们调用 userClient.findById(id) 时,Feign 会:

  1. 根据 @FeignClient 中指定的服务名称(userservice),从注册中心获取该服务的地址。
  2. 构造 HTTP 请求,根据接口方法中的注解(如 @GetMapping("/user/{id}"))确定请求的路径和参数。
  3. 发起 HTTP 请求,并将返回结果封装为接口中定义的返回值类型。

具体的业务逻辑(如查询数据库)是在被调用的服务(如 userservice)中实现的。例如,在 userservice 中可能有一个 UserController,负责处理这个请求并从数据库中查询用户信息。

@RestController
@RequestMapping("/user")
public class UserController {

    @Autowired
    private UserService userService;

    @GetMapping("/{id}")
    public User findById(@PathVariable Long id) {
        return userService.findById(id);
    }
}
1
2
3
4
5
6
7
8
9
10
11
12

# 3.6 Feign使用小结

使用 Feign 的步骤如下:

  1. 引入依赖:在 pom.xml 中添加 Feign 相关依赖。
  2. 添加注解:在启动类中添加 @EnableFeignClients 注解,启用 Feign。
  3. 编写 Feign 客户端接口:通过 @FeignClient 注解声明远程调用的服务。
  4. 使用 FeignClient 中定义的方法代替 RestTemplate:在业务逻辑中调用 Feign 接口,实现远程调用。

通过 Feign,我们可以将微服务之间的 HTTP 调用简化为本地方法调用,减少了硬编码的 URL,使代码更加优雅易懂。

提示

  • 下面这种写法只适合于当前包下的远程调用,也就是说被调用的服务必须在当前包下。

  • 默认情况下,@EnableFeignClients 会扫描当前包及其子包下的 @FeignClient 接口。如果远程调用的接口在其他包下,你可以通过指定 basePackages 属性来指定需要扫描的包。

    @EnableFeignClients(basePackages = "com.example.otherpackage")
    
    1

    这样就可以扫描其他包下的 Feign 客户端接口。

# 4. 自定义配置

Feign 提供了丰富的自定义配置选项,常见的配置类型如下表所示:

类型 作用 说明
feign.Logger.Level 修改日志级别 包含四种不同的日志级别:NONE、BASIC、HEADERS、FULL
feign.codec.Decoder 响应结果解析器 HTTP 远程调用的结果解析器,例如解析 JSON 字符串为 Java 对象
feign.codec.Encoder 请求参数编码 将请求参数编码,便于通过 HTTP 请求发送
feign.Contract 支持的注解格式 默认支持 SpringMVC 的注解
feign.Retryer 失败重试机制 请求失败的重试机制,默认不启用,不过会使用 Ribbon 的重试

一般情况下,Feign 默认的配置已经能够满足大部分需求。但在需要自定义时,只需创建自定义的 @Bean,覆盖默认的 Bean 即可。

# 4.1 配置文件方式

可以通过配置文件来修改 Feign 的日志级别,支持针对单个服务或所有服务进行配置。

# 针对单个服务的配置

在配置文件 application.yml 中,可以指定某个服务的 Feign 日志级别:

feign:  
  client:
    config: 
      userservice: # 针对某个微服务的配置
        loggerLevel: FULL #  日志级别为 FULL
1
2
3
4
5

# 针对所有服务的配置

如果需要全局生效,可以使用 default 配置:

feign:  
  client:
    config: 
      default: # 全局配置,作用于所有服务
        loggerLevel: FULL # 日志级别为 FULL
1
2
3
4
5

# 日志级别说明

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

# 4.2 基于 Java 代码的配置方式

除了在配置文件中设置外,还可以基于 Java 代码修改 Feign 的日志级别。

# 创建自定义配置类

首先,声明一个配置类,并在其中配置 Feign 的日志级别:

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

@Configuration // 声明为配置类
public class DefaultFeignConfiguration {
    
    @Bean
    public Logger.Level feignLogLevel() {
        return Logger.Level.BASIC; // 设置 Feign 日志级别为 BASIC
    }
}
1
2
3
4
5
6
7
8
9
10
11
12

# 配置全局生效

如果希望配置全局生效,可以将其放到启动类的 @EnableFeignClients 注解中:

@SpringBootApplication
@EnableFeignClients(defaultConfiguration = DefaultFeignConfiguration.class) // 全局生效的配置
public class OrderApplication {
    public static void main(String[] args) {
        SpringApplication.run(OrderApplication.class, args);
    }
}
1
2
3
4
5
6
7

# 配置局部生效

如果只希望在某个 Feign 客户端中使用自定义配置,可以将配置类放到对应的 @FeignClient 注解中:

@FeignClient(value = "userservice", configuration = DefaultFeignConfiguration.class) // 局部生效的配置
public interface UserClient {
    @GetMapping("/user/{id}")
    User findById(@PathVariable("id") Long id);
}
1
2
3
4
5

说明:

  • 全局生效:将配置类放到 @EnableFeignClients 注解中,适用于所有 Feign 客户端。
  • 局部生效:将配置类放到 @FeignClient 注解中,仅对指定的 Feign 客户端生效。

# 4.3 配置日志级别的使用场景

通过自定义日志级别,可以灵活调试远程调用的细节。在开发阶段,通常使用 FULL 级别,以便查看详细的请求和响应信息;在生产环境中,可以使用 BASIC 级别,以减少日志量。

# 4.4 配置方式总结

  • 配置文件方式:适合大范围的统一配置,可以通过 application.yml 设置全局或特定服务的配置。
  • Java 代码方式:适合更加精细化的配置,可以选择全局或局部生效,并能结合复杂的逻辑进行动态调整。

# 5. Feign使用优化

# 5. Feign 使用优化

在 Feign 的默认实现中,底层发起 HTTP 请求使用的是 URLConnection,这种实现不支持连接池,因此性能较低。为了提升 Feign 的性能,可以通过替换底层 HTTP 客户端来实现连接池功能。常用的底层客户端实现包括:

  1. URLConnection(默认):不支持连接池,性能较低。
  2. Apache HttpClient:支持连接池,性能更佳。
  3. OKHttp:支持连接池,且在高并发下表现良好。

在这里,我们将使用 Apache HttpClient 进行优化。

# 5.1 引入依赖

首先,在 order-service 的 pom.xml 文件中引入 Apache HttpClient 依赖:

<!--引入 Apache HttpClient 的依赖 -->
<dependency>
    <groupId>io.github.openfeign</groupId>
    <artifactId>feign-httpclient</artifactId>
</dependency>
1
2
3
4
5

说明:feign-httpclient 依赖是 Feign 与 Apache HttpClient 的集成包,提供了支持连接池的 HTTP 客户端。

# 5.2 配置连接池

在 order-service 的 application.yml 文件中添加连接池相关配置:

feign:
  client:
    config:
      default: # 全局配置
        loggerLevel: BASIC # 日志级别,BASIC 表示记录基本的请求和响应信息
  httpclient:
    enabled: true # 开启 Feign 对 HttpClient 的支持
    max-connections: 200 # 最大的连接数,用于限制整个应用可同时打开的 HTTP 连接数量
    max-connections-per-route: 50 # 每个路径(服务)的最大连接数,用于限制单个服务的最大连接数
1
2
3
4
5
6
7
8
9

重点说明:

  • max-connections:限制整个应用可以同时保持的最大 HTTP 连接数。设置合理的值可以避免系统资源耗尽。
  • max-connections-per-route:限制每个服务(或路径)的最大连接数,可以防止单个服务占用过多资源,保证应用的稳定性。

# 5.3 验证优化结果

为了验证 Feign 是否正确使用了 Apache HttpClient,可以在 FeignClientFactoryBean 的 loadBalance 方法中设置断点进行调试:

image-20210714185925910

以 Debug 方式启动 order-service 服务,查看调试信息,可以看到 Feign 底层使用的客户端已经替换为 Apache HttpClient:

image-20210714190041542

# 5.4 Feign 优化总结

通过以上步骤,我们可以有效提升 Feign 的性能,关键优化点包括:

  1. 日志级别设置:

    • 日志级别尽量设置为 BASIC,记录基本的请求和响应信息,避免记录过多的冗余信息,影响性能。
  2. 替换 HTTP 客户端:

    • 使用 Apache HttpClient 或 OKHttp 代替默认的 URLConnection,可以启用连接池,提升高并发场景下的性能。
    • 步骤:
      1. 引入 feign-httpclient 依赖。
      2. 在配置文件中开启 httpclient 功能,并设置连接池参数。

公众号封面

编辑此页 (opens new window)
上次更新: 2024/12/28, 18:32:08
Nacos 配置管理
OpenFeign 最佳实践

← Nacos 配置管理 OpenFeign 最佳实践→

Theme by Vdoing | Copyright © 2019-2025 程序员scholar
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式