在阅读本文前,请确保会使用Nacos的注册功能,如果不会,可以参考我的上一篇文章。链接
OpenFeign是一个声明式的Web服务客户端,拥有可插拔的注解支持,支持可插拔的HTTP编码器和解码器,LoadBalancer的负载均衡,HTTP请求和相应压缩等功能,是目前主流的SpringCloud服务接口调用技术。OpenFeign官方文档
在这篇文章中,我会使用一个消费者远程调用生产者的实际案例带大家了解Nacos整合OpenFeign
目录
1.4.5 restTemplate远程接口方式调用(了解)
1. 创建工程
1.1 父工程
用于控制各个子项目的版本依赖
依赖:
<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<lombok.version>1.18.26</lombok.version>
<spring.boot.test.version>3.1.5</spring.boot.test.version>
<spring.boot.version>3.1.7</spring.boot.version>
<spring.cloud.version>2022.0.4</spring.cloud.version>
<spring.cloud.alibaba.version>2023.0.1.2</spring.cloud.alibaba.version>
</properties>
<dependencyManagement>
<dependencies>
<!--springboot 3.2.0-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>${spring.boot.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<!--springcloud 2023.0.0-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring.cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<!--springcloud alibaba 2022.0.0.0-RC2-->
<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>
<!--lombok-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${lombok.version}</version>
<optional>true</optional>
</dependency>
<!-- spring-boot-starter-test -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<version>${spring.boot.test.version}</version>
<scope>test</scope>
</dependency>
</dependencies>
</dependencyManagement>
1.2 生产者
1.2.1 依赖
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<!--nacos-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<dependency>
<groupId>com.haishi</groupId>
<artifactId>cloud-commons</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
</dependencies>
1.2.2 application.yml文件
server:
port: 9001
spring:
application:
name: provider #注册进Nacos的服务名称
cloud:
nacos:
discovery:
server-addr: localhost:8848 #配置Nacos地址
1.2.3 主启动
@SpringBootApplication
@EnableDiscoveryClient
public class MainProvider9001 {
public static void main(String[] args) {
SpringApplication.run(MainProvider9001.class, args);
}
}
1.2.4 业务类
@RestController
public class TestController {
@Value("${server.port}")
private String serverPort;
@RequestMapping("/openFeign/test")
public String test() {
System.out.println("进入" + serverPort);
return "测试成功 生产者端口:"+serverPort+" ";
}
}
1.3 commons工具项目(无需启动)
创建一个通用的工具项目,用于在不同服务或模块之间共享的代码,存放公共配置文件,避免重复开发,减少配置的重复工作。
1.3.1 依赖
<dependencies>
<!--SpringBoot通用依赖模块-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--openfeign-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
<version>4.0.4</version>
</dependency>
</dependencies>
1.3.2 业务类
实现一个接口,声明客户端对生产者的调用,使得开发者可以像调用本地方法一样使用FeignApi.test(),
但是实际调用生产者的方法,并且不知道其中实现细节(比如一些真实数据库表结构),加强了安全性。
@FeignClient(value = "provider") //必须与生产者注册进Nacos的名称完全一致
public interface FeignApi {
@RequestMapping("/openFeign/test")
public String test();
}
1.4 消费者
1.4.1 依赖
由于远程调用原理是先向Nacos中心获取服务提供者列表,然后选择一个服务提供者调用,因此各个远程调用依赖(如openFeign,LoadBalancer)都是装在消费者这侧
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<!--nacos-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<!--openfeign-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<!-- feign-hc5-->
<dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-hc5</artifactId>
<version>13.1</version>
</dependency>
<!--loadbalancer-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-loadbalancer</artifactId>
</dependency>
<!--自定义的工具项目-->
<dependency>
<groupId>com.haishi</groupId>
<artifactId>cloud-commons</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
</dependencies>
1.4.2 application.yml文件
server:
port: 2001
spring:
application:
name: consumer
cloud:
nacos:
discovery:
server-addr: localhost:8848
1.4.3 主启动
@SpringBootApplication
@EnableDiscoveryClient
@EnableFeignClients //启用feign客户端,定义服务+绑定接口,以声明式的方法优雅而简单地实现服务调用
public class MainConsumer2001 {
public static void main(String[] args) {
SpringApplication.run(MainConsumer2001.class, args);
}
}
1.4.4 业务类(结合OpenFeign实现远程调用)
@RestController
public class TestController {
@Value("${server.port}")
private String port;
@Resource
private FeignApi feignApi; //commons里编写的接口
@GetMapping(value = "/consumer/openFeign/test")
public String test()
{
System.out.println("进入consumer");
return feignApi.test()+" 消费者端口:"+port;
}
}
1.4.5 restTemplate远程接口方式调用(了解)
如果使用rest的调用方式就没有必要编写commons模块中的FeignApi接口了
首先需要编写配置类
@Configuration
public class RestTemplateConfig
{
@Bean
@LoadBalanced //赋予RestTemplate负载均衡的能力
public RestTemplate restTemplate()
{
return new RestTemplate();
}
}
然后在业务类里通过restTemplate.getForObject(网址,生产者业务了返回值类型.class)调用
网址具体的组成方式为: http:// + 生产者 + 调用生产者业务类网址
生产者服务名称一般写在application.yml中,再通过@Value注解添加进来,我这里就直接字符串写死了。
@RestController
public class TestController {
@Value("${server.port}")
private String port;
@Resource
private RestTemplate restTemplate;
@GetMapping(value = "/consumer/rest/test")
public String test2()
{
return restTemplate.getForObject("http://provider/openFeign/test",String.class)+" 消费者端口:"+port;
}
}
如果使用一些自定义类,那么消费者和生产者端都需要有对应的代码。这里还只是一个消费者,如果有多个消费者呢?那代码的膨胀性将是巨大的。因此OpenFeign通过commons远程调用,实现代码复用,提升安全性,无疑是优于restTemplate的,个人也十分推荐使用OpenFeign。
2. 测试
启动项目后,首先检查Nacos中心是否注册成功,然后再开始测试
2.1 生产者端测试
访问localhost:9001/openFeign/test
2.2 消费者端测试
访问localhost:2001/consumer/openFeign/test测试OpenFeign远程调用
访问localhost:2001/consumer/rest/test测试restTemplate远程调用
2.3 负载均衡测试
OpenFeign天生支持SpringCloudLoadBalancer负载均衡
首先多复制几个生产者
如果不会IDEA快捷复制项目的可以参考这篇http://t.csdnimg.cn/YOobw
刷新一下,可以看到成功调用其他生产者的服务
因为生成的restTemplate带有@LoadBalancer注解,因此此时的restTemplate也带有负载均衡能力
3. OpenFeign高级特性
3.1 超时控制
在消费者模块中的application.yml中可以配置超时时间控制(每个版本默认值可能不同)
server:
port: 2001
spring:
application:
name: consumer
cloud:
nacos:
discovery:
server-addr: localhost:8848
openfeign:
client:
config:
# default 设置的全局超时时间
default:
connectTimeout: 4000 #连接超时时间(毫秒),默认是60s
readTimeout: 4000 #请求处理超时时间(毫秒)
# 为provider这个服务单独配置超时时间,单个配置的超时时间将会覆盖全局配置
provider:
connect-timeout: 2000 #连接超时时间(毫秒),默认是60s
read-timeout: 2000 #请求处理超时时间(毫秒)
3.2 OpenFeign重试机制
编写FeignConfig配置类
@Configuration
public class FeignConfig {
@Bean
public Retryer myRetryer()
{
return Retryer.NEVER_RETRY; //默认配置,不走重试策略
//最大请求次数为3(1+2),初始间隔时间为100ms,重试间最大间隔时间为1s
// return new Retryer.Default(100,1,3);
}
}
3.3 默认HttpClient修改
OpenFeign默认使用JDK自带的HttpURLConnection发送HTTP请求,由于其没有连接池,性能和效率上比较低,可以替代使用HttpClient5进行优化
在消费者模块中的application.yml中进行配置
server:
port: 2001
spring:
application:
name: consumer
cloud:
nacos:
discovery:
server-addr: localhost:8848
httpclient:
hc5:
enabled: true #使用Apache HttpComponents HttpClient 5 优化http
3.4 请求/响应压缩
对请求和响应进行GZIP压缩,以减少通信过程中的性能损耗
在消费者模块中的application.yml中进行配置
server:
port: 2001
spring:
application:
name: consumer
cloud:
nacos:
discovery:
server-addr: localhost:8848
compression:
request:
enabled: true #使用gzip压缩数据,提高性能
min-request-size: 2048 #最小触发压缩的大小
mime-types: text/xml,application/xml,application/json #触发压缩数据类型
3.5 日志打印功能
OpenFeign提供了对Feign接口的调用情况进行监控和输出的功能
首先在FeignConfig中添加
@Bean
Logger.Level feignLoggerLevel() {
// NONE 没有日志(默认)
// BASIC 仅记录请求方法、URL、响应状态码及执行时间
// HEADERS 新增监控请求和响应的头信息
// FULL 所有信息(新增请求和响应的正文及元数据)
return Logger.Level.FULL;
}
然后在消费者模块中的application.yml中进行配置日志级别
server:
port: 2001
spring:
application:
name: consumer
cloud:
nacos:
discovery:
server-addr: localhost:8848
# feign日志以什么级别监控哪个接口
logging:
level:
com: #包名
haishi: #包名
apis: #包名
FeignApi: debug #Feign接口名称:日志级别,只有在debug模式下会被打印
3.6 结合Sentinel服务降级
如果在Sentinel中为每个消费者微服务的每个业务类单独配置一个服务降级方法过于冗余,可以结合OpenFeign进行统一配置
在commons模块中新增FeignSentinelApiFailBack类
public class FeignSentinelApiFallBack implements FeignApi{
@Override
public String test() {
return "系统繁忙,请稍后再试o(╥﹏╥)o";
}
}
修改FeignApi的注解
@FeignClient(value = "provider",fallback =FeignSentinelApiFallBack.class )
OpenFeign可以进行的高级设置远不止这些,这里仅作一些常用设置的分享,更多的可参考官方文档
4.总结
OpenFeign作为SpringCloud中的主流服务调用技术,能够以声明式的方式简化微服务间的通信。通过整合Nacos,OpenFeign不仅可以实现负载均衡,还能通过简单的注解和配置增强代码复用性、可维护性,并且隐藏了底层实现细节,提升了系统安全性。相比传统的RestTemplate
方式,OpenFeign更加优雅和简洁,并且提供了高级特性进行灵活配置。