一、OpenFeign介绍
OpenFeign是Netfix开发的一款声明式,模板化的Http服务调用客户端。使用在服务调用者工程端。OpenFeign的负载均衡也为客户端负载均衡。一下简称Feign。
- Feign可以更加便捷,优雅的调用Http API接口。
- SpringCloud中,使用Feign非常简单,创建一个接口,并在接口上添加一些注解,即调用类代码开发完成。
- Feign支持多种注解,例如Feign自带的注解或者JAX-RS注解
- SpringCloud 对Feign进行了增强,使Feign支持了SpringMVC注解,并整合了Ribbon和Eureka从而让Feign使用更加方便
Feign 与 OpenFeign的区别:Feign是Springcloud组件中的一个轻量级Restful的HTTP服务客户端,Feign内置了Ribbon,用来做客户端负载均衡,去调用服务注册中心的服务。Feign的使用方式是:使用Feign的注解定义接口,调用这个接口,就可以调用服务注册中心的服务;OpenFeign是springcloud在Feign的基础上支持了SpringMVC的注解,如@RequestMapping等等。OpenFeign的@FeignClient可以解析SpringMVC的@RequestMapping注解下的接口,并通过动态代理的方式产生实现类,实现类中做负载均衡并调用其他服务。
二、基本配置使用
1,添加openfen依赖:
<!-- springCloud 整合的openFeign -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
2,启动类添加激活OpenFeign注解@EnableFeignClients
@SpringBootApplication
@EnableFeignClients
public class OrderApplication {
public static void main(String[] args) {
SpringApplication.run(OrderApplication.class,args);
}
}
3,编写http远程调用接口类
@FeignClient(name="CLOUD-PAYMENT-SERVICE")
public interface ProductFeignClient {
@RequestMapping(value="/payment/get/{id}",method = RequestMethod.GET)
CommonResult getPayment(@PathVariable("id") Long id);
@GetMapping("/payment/get/timeout")
CommonResult getPaymentTimeout();
}
注意接口类需要添加@FeignClient(name="CLOUD-PAYMENT-SERVICE") 注解。注解中name值为服务提供者服务名称。 接口中方法需要添加SpringMVC注解来声明调用的远程方法路径。方法参数要与远程接口提供方参数一致。
4,在Controller中注入并使用调用
@RestController
public class OrderController {
@Autowired
private ProductFeignClient productFeignClient;
@GetMapping("/consumer/payment/get/{id}")
public CommonResult<Payment> getPayment(@PathVariable("id") Long id){
return productFeignClient.getPayment(id);
}
@GetMapping("/consumer/payment/get/timeout")
public CommonResult getPaymentTimeout(){
return productFeignClient.getPaymentTimeout();
}
}
在Controller中使用比较简单,注解注入接口类,然后使用接口类方法直接调用即可。和我们之前单机版的Controller调用Service结构比较一致。
测试验证:我们启动Eureka注册中心,并分别启动服务提供者工程2个,和当前feign工程。访问feign工程的controller中方法地址,相关展示信息如下:
三、负载均衡配置
由于OpenFeign对ribbon的支持所以,OpenFeign的负载均衡也是客户端负载均衡,配置方式和ribbon的也一样。最*的为服务名称。默认的为轮询策略,如下配置的为随机策略。
CLOUD-PAYMENT-SERVICE:
ribbon:
NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule
负载均衡测试:我们启动两个生产者工程端口分别为8002 和 8003,并将调用执行的节点端口进行返回。首先不配置负载均衡相关配置进行测试
如下为配置了随机请求后的访问效果。
四、超时及日志级别配置
在开发或这运行时,如果需要看到Feign的请求过程可以通过Feign的日志进行查看。默认情况下Feign日志没有开启。如果需要开启,则需要在yml中添加如下feign.client.config中设置即可。
其中loggerLevel中枚举包含:
- NONE:性能最佳,不包含任何日志,默认值;
- BASIC:仅记录请求方法、URL、相应状态码以及执行时间;
- HEADERS:在BASIC的基础上记录请求和相应的Header信息;
- FULL:记录请求和相应的header、body、和元数据。
超时时间可以在connectTimeout、和readTimeout中配置。
feign:
client:
config:
CLOUD-PAYMENT-SERVICE:
loggerLevel: FULL
connectTimeout: 5000
readTimeout: 5000
logging:
level:
com.xiaohui.springcloud.service.ProductFeignClient: debug
配置为FULL时的打印信息:
2020-11-27 20:48:38.893 DEBUG 5832 --- [nio-9001-exec-8] c.x.s.service.ProductFeignClient : [ProductFeignClient#getPayment] ---> GET http://CLOUD-PAYMENT-SERVICE/payment/get/6203063 HTTP/1.1
2020-11-27 20:48:38.893 DEBUG 5832 --- [nio-9001-exec-8] c.x.s.service.ProductFeignClient : [ProductFeignClient#getPayment] ---> END HTTP (0-byte body)
2020-11-27 20:48:38.914 DEBUG 5832 --- [nio-9001-exec-8] c.x.s.service.ProductFeignClient : [ProductFeignClient#getPayment] <--- HTTP/1.1 200 (20ms)
2020-11-27 20:48:38.914 DEBUG 5832 --- [nio-9001-exec-8] c.x.s.service.ProductFeignClient : [ProductFeignClient#getPayment] connection: keep-alive
2020-11-27 20:48:38.914 DEBUG 5832 --- [nio-9001-exec-8] c.x.s.service.ProductFeignClient : [ProductFeignClient#getPayment] content-type: application/json
2020-11-27 20:48:38.914 DEBUG 5832 --- [nio-9001-exec-8] c.x.s.service.ProductFeignClient : [ProductFeignClient#getPayment] date: Fri, 27 Nov 2020 12:48:38 GMT
2020-11-27 20:48:38.914 DEBUG 5832 --- [nio-9001-exec-8] c.x.s.service.ProductFeignClient : [ProductFeignClient#getPayment] keep-alive: timeout=60
2020-11-27 20:48:38.914 DEBUG 5832 --- [nio-9001-exec-8] c.x.s.service.ProductFeignClient : [ProductFeignClient#getPayment] transfer-encoding: chunked
2020-11-27 20:48:38.915 DEBUG 5832 --- [nio-9001-exec-8] c.x.s.service.ProductFeignClient : [ProductFeignClient#getPayment]
2020-11-27 20:48:38.915 DEBUG 5832 --- [nio-9001-exec-8] c.x.s.service.ProductFeignClient : [ProductFeignClient#getPayment] {"code":0,"message":"查询成功 server node:172.18.58.92:8002","data":{"id":6203063,"serial":"Hello SpringCloud"}}
2020-11-27 20:48:38.915 DEBUG 5832 --- [nio-9001-exec-8] c.x.s.service.ProductFeignClient : [ProductFeignClient#getPayment] <--- END HTTP (116-byte body)
五、项目完整文件及目录
父工程pom文件:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.xiaohui.springCloud</groupId>
<artifactId>SpringCloud</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>pom</packaging>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.2.RELEASE</version>
</parent>
<modules>
<module>product_service</module>
<module>cloud-api-common</module>
<module>eureka_server</module>
<module>openfeign_order_service</module>
</modules>
<!-- 统一jar包管理 -->
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
<junit.version>4.12</junit.version>
<log4j.version>1.2.17</log4j.version>
<lombok.version>1.16.18</lombok.version>
<mysql.version>5.1.47</mysql.version>
<druid.version>1.1.16</druid.version>
<mybatis.spring.boot.version>1.3.0</mybatis.spring.boot.version>
</properties>
<!-- 子模块继承之后,提供作用:锁定版本+子模块不用写groupId和version -->
<dependencyManagement>
<dependencies>
<!-- spring boot 2.2.2 -->
<!-- <dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>2.2.2.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>-->
<!-- spring cloud Hoxton.SR1 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Hoxton.SR1</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<!-- springcloud alibaba -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>2.1.0.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>${druid.version}</version>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>${mybatis.spring.boot.version}</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>${mysql.version}</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>${junit.version}</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${lombok.version}</version>
<optional>true</optional>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<!-- maven 插件 -->
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<fork>true</fork>
<addResources>true</addResources>
</configuration>
</plugin>
</plugins>
</build>
</project>
1,openfeign_order_service 子模块 pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>SpringCloud</artifactId>
<groupId>com.xiaohui.springCloud</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>openfeign_order_service</artifactId>
<dependencies>
<dependency>
<groupId>com.xiaohui.springCloud</groupId>
<artifactId>cloud-api-common</artifactId>
<version>${project.version}</version>
</dependency>
<!-- springCloud 整合的openFeign -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<!-- Eureka 客户端 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.retry</groupId>
<artifactId>spring-retry</artifactId>
</dependency>
<!-- web依赖开始 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<!-- web依赖结束 -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
</dependencies>
</project>
2,配置文件 application.yml:
server:
port: 9001
spring:
application:
name: openfeign-order-service
eureka:
client:
register-with-eureka: true
fetch-registry: true
service-url:
defaultZone: http://eureka1.com:9000/eureka/
instance:
prefer-ip-address: true #使用ip进行注册
instance-id: ${spring.cloud.client.ip-address}:${server.port} #向注册中心注册服务ID
lease-renewal-interval-in-seconds: 5 #发送心跳间隔时间 秒
lease-expiration-duration-in-seconds: 10 # 服务续约时间 10秒内没有发送心跳(宕机)
CLOUD-PAYMENT-SERVICE:
ribbon:
NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule
#Feign 日志输出配置
feign:
client:
config:
CLOUD-PAYMENT-SERVICE:
loggerLevel: FULL
connectTimeout: 5000
readTimeout: 5000
logging:
level:
com.xiaohui.springcloud.service.ProductFeignClient: debug
3,主启动类:
package com.xiaohui.springcloud;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.openfeign.EnableFeignClients;
@SpringBootApplication
@EnableFeignClients
public class OrderApplication {
public static void main(String[] args) {
SpringApplication.run(OrderApplication.class,args);
}
}
4,feign 接口类
package com.xiaohui.springcloud.service;
import com.xiaohui.springcloud.entities.CommonResult;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
@FeignClient(name="CLOUD-PAYMENT-SERVICE")
public interface ProductFeignClient {
@RequestMapping(value="/payment/get/{id}",method = RequestMethod.GET)
CommonResult getPayment(@PathVariable("id") Long id);
@GetMapping("/payment/get/timeout")
CommonResult getPaymentTimeout();
}
5,业务代码 controller
package com.xiaohui.springcloud.controller;
import com.xiaohui.springcloud.entities.CommonResult;
import com.xiaohui.springcloud.entities.Payment;
import com.xiaohui.springcloud.service.ProductFeignClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class OrderController {
@Autowired
private ProductFeignClient productFeignClient;
@GetMapping("/consumer/payment/get/{id}")
public CommonResult<Payment> getPayment(@PathVariable("id") Long id){
return productFeignClient.getPayment(id);
}
@GetMapping("/consumer/payment/get/timeout")
public CommonResult getPaymentTimeout(){
return productFeignClient.getPaymentTimeout();
}
}