文章目录

1. 基于 Feign 的远程调用2. Feign 自定义配置3. Feign 性能优化4. Feign 的最佳实践4.1 继承4.2 抽取

1. 基于 Feign 的远程调用

Feign 是一个声明式的 http 客户端,它可以帮助我们优雅地发送 http 请求。

在学习 Feign 之前先来看一下我们以前利用 RestTemplate 是如何发起远程调用请求的。

String url = "http://userserver/user/" + order.getUserId();

User user = restTemplate.getForObject(url, User.class);

存在下面的问题: ① 代码可读性差,编程体验不统一; ② 对于参数复杂的 URL 难以维护。

使用 Feign 的步骤如下:

① 引入依赖

org.springframework.cloud

spring-cloud-starter-openfeign

② 在启动类上添加注解 @EnableFeignClients,开启自动装配功能

③ 编写 user 客户端做接口声明,我们就可以通过调用该接口里面的方法,来向 user 服务端(userserver)发起 http 请求

package com.zxe.orderserver.clients;

import com.zxe.orderserver.pojo.User;

import org.springframework.cloud.openfeign.FeignClient;

import org.springframework.web.bind.annotation.GetMapping;

import org.springframework.web.bind.annotation.PathVariable;

//指定请求的服务名称

@FeignClient("userserver")

public interface UserClient {

//指定请求路径及请求方式

@GetMapping("/user/{id}")

//方法的返回值就是最终请求到的结果

User findById(@PathVariable("id") Long id);

}

④ 使用客户端,直接调用 UserClient 中的方法完成远程调用

⑥ 添加负载均衡依赖,Feign 内部已经集成了 Ribbon 负载均衡规则,可以直接实现负载均衡

⑦ 启动项目测试一下,可以看到我们已经实现了远程调用

2. Feign 自定义配置

Feign 运行自定义配置来覆盖默认配置,一般我们需要配置的就是日志级别。

配置 Feign 日志有两种方式:

① 配置文件方式

default代表全局配置,也可以把 default 换成具体的服务名,就是局部配置!

#配置Feign日志

feign:

client:

config:

default:

logger-level: Full

启动项目,可以看到完整的日志信息:

② Java 代码方式

需要先声明一个 Bean:

package com.zxe.orderserver.config;

import feign.Logger;

import org.springframework.context.annotation.Bean;

public class DefaultFeignConfiguration {

@Bean

public Logger.Level logLevel() {

return Logger.Level.BASIC;

}

}

如果是全局配置,则把它放到启动类的 @EnableFeignClients 注解中;如果是局部配置,就把它放到 @FeignClient 这个注解中。

日志记录还是需要消耗一些性能的,所以日志级别我们一般用 BASIC 就可以了!

3. Feign 性能优化

Feign 是一个声明式客户端,它只是把我们的声明变成一个 http 请求,而最终发起 http 请求的其实是其它的客户端,默认用的是 URLConnection。

Feign 底层的客户端实现: ① URLConnection:默认实现,不支持连接池; ② Apache HttpClient:支持连接池; ③ OKHttp:支持连接池。

URLConnection 不支持连接池,那么每次连接与断开都需要经历三次握手和四次挥手,对性能的损耗是比较大的,因此我们可以使用另外两种客户端来代替默认的 URLConnection。

因此 Feign 的性能优化主要包括以下两点: ① 使用连接池代替默认的 URLConnection; ② 日志级别,最好用 basic 或 none。

① 引入 HttpClient 依赖

Spring 已经管理了 HttpClient,所以不需要加版本号!

io.github.openfeign

feign-httpclient

② 开启 Feign 对 HttpClient 的支持,并配置连接池

最大连接数的设置要根据真实情况去设置!

feign:

httpclient:

enabled: true #开启Feign对HttpClient的支持

max-connections: 200 #最大连接数

max-connections-per-route: 50 #单个路径的最大连接数

4. Feign 的最佳实践

4.1 继承

仔细观察你会发现,UserClient 中的 findById 和 UserController 的 findById 方法是一模一样的,因为一个是客户端一个是服务端,想要建立连接,访问路径肯定是完全一样的。既然是重复的东西,写两遍会不会有点冗余?

解决办法就是给消费者的 FeignClient 和提供者的 controller 定义统一的父接口作为标准。

但是在两个独立的微服务之间共享接口,肯定是不合理的,因为这会造成紧耦合。而且方法参数是无法继承的,也就是说 controller 接口还得自己实现 findById。虽然它有这么多的缺点,但是它遵循的是面向契约的思想,使用的还是比较多的。

4.2 抽取

比如 A 服务会用到 userserver,B 服务也会用到 userserver,这时候 A 和 B 服务中都需要编写 FeignClient 代码,且这个代码是完全一样的,那么就没必要写两次了。

所以我们可以将 FeignClient 抽取为独立模块(feign-api),并且把接口相关的 POJO、默认的 Feign 配置也都放到这个模块中,提供给所有消费者使用,消费者之间引依赖远程调用即可。

抽取方式的耦合度就很低了,但是它也有自己的缺点。其实 feign-api 里面不止放 userserver 的客户端,还会放大量的其它微服务的客户端,这时我们只想使用某一个,却把所有的都给引进来了,当然没有一个解决办法是完美的,我们需要根据实际情况选择合适的解决办法。

① 创建一个 module,命名为 feign-api,然后引入 feign 的 starter 依赖

org.springframework.cloud

spring-cloud-starter-openfeign

② 将 orderserver 中编写的 UserClient、User、DefaultFeignConfiguration 都复制到 feign-api 项目中,记得把包名也改成 feignapi

③ 在 orderserver 中引入 feign-api 的依赖,并把刚刚复制到 feign-api 的内容都删掉

com.zxe

feign-api

0.0.1-SNAPSHOT

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

⑤ 单独指定需要加载的客户端,因为启动 orderserver 的时候并不会加载 feign-api 下的包,会报错

⑥ 重启测试

参考阅读

评论可见,请评论后查看内容,谢谢!!!
 您阅读本篇文章共花了: