【微服务】SpringCloud之Feign远程调用

🏡浩泽学编程个人主页
🔥 推荐专栏《深入浅出SpringBoot》《java对AI的调用开发》
《RabbitMQ》《Spring》《SpringMVC》《项目实战》

🛸学无止境,不骄不躁,知行合一

文章目录


前言

使用Feign远程调用代替RestTemplate远程调用。


一、Feign的优势

使用RestTemplate发起远程调用:

java 复制代码
String url = "http://userservice/user/"+order.getUserId();
User user = restTemplate.getForObject(url, User.class);

虽然在引入注册中心后,使用RestTemplate发起请求可以使用服务名代替主机地址,减少了主机地址修改带来的问题,实现根据服务名去注册中心拉取。但是仍然存在问题:

  • 代码可读性差,编程体验不统一
  • 参数复杂URL难以维护

而Feign是一个声明式的http客户端,可以帮助我们优雅的实现http请求的发送,解决这些问题。

二、Feign使用

初步搭建

下面以orderserver调用userserver服务搭建步骤如下:

(1)引入依赖

xml 复制代码
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>

(2)添加注解

在服务调用者的启动类添加注解(@EnableFeignClients)开启Feign功能,例如:

java 复制代码
@EnableFeignClients
@MapperScan("cn.itcast.order.mapper")
@SpringBootApplication
public class OrderApplication {
    public static void main(String[] args) {
        SpringApplication.run(OrderApplication.class, args);
    }
}

(3)客户端编写

java 复制代码
import cn.itcast.order.config.DefaultFeignConfiguration;
import cn.itcast.order.pojo.User;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;

/**
 * @version 1.0.0
 * @Author: dragon_王
 * @Date: 2024/3/28 14:12:39
 */
@FeignClient("userservice")
public interface UserClient {
    @GetMapping("/user/{id}")
    User findById(@PathVariable("id") Long id);
}

这个客户端主要是基于SpringMVC的注解来声明远程调用的信息,比如:

  • 服务名称:userservice
  • 请求方式:GET
  • 请求路径:/user/{id}
  • 请求参数:Long id
  • 返回值类型:User

这样,Feign就可以帮助我们发送http请求,无需自己使用RestTemplate来发送了。

(4)调用代码

java 复制代码
@Service
public class OrderService {
    @Autowired
    private OrderMapper orderMapper;
    //注入客户端调用
    @Autowired
    private UserClient userClient;
    public Order queryOrderById(Long orderId) {
        Order order = orderMapper.findById(orderId);
        //用Fegin远程调用
        User user = userClient.findById(order.getUserId());
        order.setUser(user);
        return order;
    }
}
    

(5)小结

使用Feign的步骤:

① 引入依赖

② 添加@EnableFeignClients注解

③ 编写FeignClient接口

④ 使用FeignClient中定义的方法代替RestTemplate

自定义配置

Feign可以支持很多的自定义配置,如下表所示:

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

一般情况下,默认值就能满足我们使用,如果要自定义时,只需要创建自定义的@Bean覆盖默认Bean即可。

下面以日志为例来演示如何自定义配置。

配置文件方式

基于配置文件修改feign的日志级别可以针对单个服务:

yaml 复制代码
feign:  
  client:
    config: 
      userservice: # 针对某个微服务的配置
        loggerLevel: FULL #  日志级别 

也可以针对所有服务:

yaml 复制代码
feign:  
  client:
    config: 
      default: # 这里用default就是全局配置,如果是写服务名称,则是针对某个微服务的配置
        loggerLevel: FULL #  日志级别 

而日志的级别分为四种:

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

java代码方式

也可以基于Java代码来修改日志级别,先声明一个类,然后声明一个Logger.Level的对象:

java 复制代码
public class DefaultFeignConfiguration  {
    @Bean
    public Logger.Level feignLogLevel(){
        return Logger.Level.BASIC; // 日志级别为BASIC
    }
}

如果要全局生效,将其放到启动类的@EnableFeignClients这个注解中:

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

如果是局部生效,则把它放到对应的@FeignClient这个注解中:

java 复制代码
@FeignClient(value = "userservice", configuration = DefaultFeignConfiguration .class) 

Feign优化使用

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

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

因此提高Feign的性能主要手段就是使用连接池代替默认的URLConnection。

这里我们用Apache的HttpClient来演示。

1)引入依赖

在服务调用方的pom文件中引入Apache的HttpClient依赖:

xml 复制代码
<!--httpClient的依赖 -->
<dependency>
    <groupId>io.github.openfeign</groupId>
    <artifactId>feign-httpclient</artifactId>
</dependency>

2)配置连接池

在服务调用方的application.yml中添加配置:

yml 复制代码
feign:
  client:
    config:
      default: # default全局的配置
        loggerLevel: BASIC # 日志级别,BASIC就是基本的请求和响应信息
  httpclient:
    enabled: true # 开启feign对HttpClient的支持
    max-connections: 200 # 最大的连接数
    max-connections-per-route: 50 # 每个路径的最大连接数

3)小结

Feign的优化:

  • 日志级别尽量用basic
  • 使用HttpClient或OKHttp代替URLConnection
    • 引入feign-httpClient依赖
    • 配置文件开启httpClient功能,设置连接池参数

三、Feign最佳实践

可以发现,Feign的客户端与服务提供者的controller代码非常相似,

feign客户端:

java 复制代码
@FeignClient("userservice")
public interface UserClient {
    @GetMapping("/user/{id}")
    User findById(@PathVariable("id") Long id);
}

UserController:

java 复制代码
@RestController
public class UserController {
@GetMapping("/user/{id}")
    public User queryById(@PathVariable("id") Long id) {
        return userService.queryById(id);
    }

有没有一种办法简化这种重复的代码编写呢?

继承方式

一样的代码可以通过继承来共享:

1)定义一个API接口,利用定义方法,并基于SpringMVC注解做声明。

2)Feign客户端和Controller都集成改接口

优点:

  • 简单
  • 实现了代码共享

缺点:

  • 服务提供方、服务消费方紧耦合
  • 参数列表中的注解映射并不会继承,因此Controller中必须再次声明方法、参数列表、注解

抽取方式

将Feign的Client抽取为独立模块,并且把接口有关的POJO、默认的Feign配置都放到这个模块中,提供给所有消费者使用。

例如,将UserClient、User、Feign的默认配置都抽取到一个feign-api包中,所有微服务引用该依赖包,即可直接使用。

1)抽取

首先创建一个module,命名为feign-api:

项目结构:

在feign-api中然后引入feign的starter依赖:

xml 复制代码
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>

然后,order-service(服务调用者)中编写的UserClient(Feign客户端)、User、DefaultFeignConfiguration(java代码实现的日志配置,上面讲诉过)都复制到feign-api项目中

2)在order-service(服务调用方)中使用feign-api

首先,删除原先order-service中的UserClient、User、DefaultFeignConfiguration等类或接口。

在order-service的pom文件中中引入feign-api的依赖(刚才创建的feign-api):

这段代码就是在feign-api模块的pom文件里

xml 复制代码
<dependency>
    <groupId>cn.itcast.demo</groupId>
    <artifactId>feign-api</artifactId>
    <version>1.0</version>
</dependency>

修改order-service中的所有与上述三个组件有关的导包部分,改成导入feign-api中的包。

其实就是将原先服务调用者里有关的feign配置已经被提出来作为单独的模块,所以以前的客户端,对象,配置类什么的都要导入提出来的模块中的,要不然找不到啊。

尤其是现在扫描包肯定是要变的,现在服务调用方没有这些类和接口了,都被提出去了,肯定扫描不到。

4)解决扫描包问题

方式一:

指定Feign应该扫描的包:

java 复制代码
@EnableFeignClients(basePackages = "cn.itcast.feign.clients")

方式二:

指定需要加载的Client接口:

java 复制代码
@EnableFeignClients(clients = {UserClient.class})

总结

以上就是Feign的讲解。

相关推荐
苍何22 分钟前
30分钟用 Agent 搓出一家跨境网店,疯了
后端
ssshooter36 分钟前
Tauri 2 iOS 开发避坑指南:文件保存、Dialog 和 Documents 目录的那些坑
前端·后端·ios
追逐时光者1 小时前
一个基于 .NET Core + Vue3 构建的开源全栈平台 Admin 系统
后端·.net
程序员飞哥1 小时前
90后大龄程序员失业4个月终于上岸了
后端·面试·程序员
zs宝来了1 小时前
Playwright 自动发布 CSDN 的完整实践
java
吴声子夜歌3 小时前
TypeScript——基础类型(三)
java·linux·typescript
GetcharZp3 小时前
Git 命令行太痛苦?这款 75k Star 的神级工具,让你告别“合并冲突”恐惧症!
后端
freewlt3 小时前
深入理解 OpenClaw:打造安全可控的本地 AI 助理架构
人工智能·安全·架构·openclaw
Victor3564 小时前
MongoDB(69)如何进行增量备份?
后端