5-Spring cloud之Feign的使用——服务器上实操
- 1. 前言
- 2. 搭建Feign
- 2.1 添加子模块——`dog-api`
- 2.1.1 子模块结构
- 2.1.2 pom文件
- 2.1.3 核心接口DogClientApi
- 2.2 添加子模块——`dog-consumer-feign-80`
- 2.2.1 子模块结构
- 2.2.2 pom文件
- 2.2.3 yml文件
- 2.2.4 主启动类
- 2.2.5 controller
- 2.3 测试看效果(本地单机)
- 2.4 测试看效果(服务器看负载均衡)
- 2.5 关于Feign自定义负载均衡
- 3. 注意问题以及常见问题
- 3.1 注意问题
- 3.2 遇到的问题
- 3.2.1 feign.FeignException: status 404 reading DogClientApi#
- 4. 总结Feign
- 4.1 什么是Feign?为什么要使用Feign?
- 4.1.1 什么是Feign?
- 4.1.2 为什么要使用Feign?
- 4.1.3 Feign 和 Ribbon
- 4.2 Feign依赖注入原理
- 4.3 自动装载和动态代理机制源码剖析
1. 前言
- 前几篇文章,有需要的自行查看
- 1-Eureka服务注册与发现以及Eureka集群搭建(实操型).
- 2-Spring cloud之Eureka快速剔除失效服务
- 3-Spring cloud之搭建Ribbon负载均衡——服务器上实操(上)
- 4-Spring cloud之搭建Ribbon负载均衡——服务器上实操(下)
- 本篇文章是在续前缘,……
2. 搭建Feign
2.1 添加子模块——dog-api
2.1.1 子模块结构
- 如下:
2.1.2 pom文件
-
核心配置
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-feign</artifactId> </dependency>
-
完整文件
<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> <parent> <groupId>com.liu.susu</groupId> <artifactId>dog-cloud-parent</artifactId> <version>1.0-SNAPSHOT</version> </parent> <artifactId>dog-api</artifactId> <packaging>jar</packaging> <name>dog-api</name> <url>http://maven.apache.org</url> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> </properties> <dependencies> <dependency> <groupId>com.liu.susu</groupId> <artifactId>dog-po</artifactId> <version>${project.version}</version> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-feign</artifactId> </dependency> </dependencies> </project>
2.1.3 核心接口DogClientApi
-
如下:
package com.liu.susu.api; import com.liu.susu.pojo.Dog; import org.springframework.cloud.netflix.feign.FeignClient; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import java.util.List; /** * @Description * @Author susu */ @FeignClient(value = "DOG-PROVIDER") public interface DogClientApi { @RequestMapping(value = "/dog/hello",method = RequestMethod.GET) String hello(); @RequestMapping(value = "/dog/getDogByNum/{dogNum}",method = RequestMethod.GET) Object getDogByNum(@PathVariable("dogNum") Long dogNum); @RequestMapping("/dog/getAllDog") List<Dog> getAllDog(); }
2.2 添加子模块——dog-consumer-feign-80
2.2.1 子模块结构
- 如下:
2.2.2 pom文件
-
如下:
<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> <parent> <groupId>com.liu.susu</groupId> <artifactId>dog-cloud-parent</artifactId> <version>1.0-SNAPSHOT</version> </parent> <artifactId>dog-consumer-feign-80</artifactId> <packaging>jar</packaging> <name>dog-consumer-feign-80</name> <url>http://maven.apache.org</url> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> </properties> <dependencies> <dependency> <groupId>com.liu.susu</groupId> <artifactId>dog-api</artifactId> <version>${project.version}</version> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <scope>test</scope> </dependency> <!--下面这几个,版本同${spring-boot.version}--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!--热部署 版本同${spring-boot.version}--> <dependency> <groupId>org.springframework</groupId> <artifactId>springloaded</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> </dependency> <!--引入Feign相关依赖--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-feign</artifactId> </dependency> <!-- 因为feign内集成了ribbon,所以spring-cloud-starter-ribbon依赖可以不加--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-eureka</artifactId> </dependency> <!-- <dependency>--> <!-- <groupId>org.springframework.cloud</groupId>--> <!-- <artifactId>spring-cloud-starter-ribbon</artifactId>--> <!-- </dependency>--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-config</artifactId> </dependency> </dependencies> </project>
2.2.3 yml文件
-
和单独使用ribbon时一样,如下:
server: port: 80 spring: application: name: dog-consumer-feign eureka: client: # 客户端注册进eureka服务列表内 register-with-eureka: false # false表示不向注册中心注册自己 service-url: defaultZone: http://IP1:2886/eureka/,http://IP2:2886/eureka,http://IP3.175:2886/eureka/
2.2.4 主启动类
- 如下:
package com.liu.susu; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.netflix.eureka.EnableEurekaClient; import org.springframework.cloud.netflix.feign.EnableFeignClients; @SpringBootApplication @EnableEurekaClient /** * 在该注解中添加 FeignClient 的所在包,以便Spring容器能够扫描到所有的FeignClient,并进行托管。 * 后面我们便可以使用 @Autowired 注解自动导入了 */ @EnableFeignClients(basePackages = {"com.liu.susu.api"}) //开启Fiegn功能 public class DogConsumerFeignApp { public static void main( String[] args ) { SpringApplication.run(DogConsumerFeignApp.class, args); System.out.println( "Hello feign!" ); } }
2.2.5 controller
-
这里的路径可以随便定义,但是API下的必须与服务提供者的路径一致
package com.liu.susu.controller; import com.liu.susu.api.DogClientApi; import com.liu.susu.pojo.Dog; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RestController; import java.util.List; /** * @Description * @Author susu */ @RestController public class DogConsumerFeignController { @Autowired private DogClientApi dogClientApi; //http://localhost:80/consumer/dog/hello @RequestMapping(value = "/consumer/dog/hello") public String hello(){ System.out.println("hello========"); return dogClientApi.hello(); } @RequestMapping(value = "/consumer/dog/getDogByNum/{dogNum}",method = RequestMethod.GET) public Object getDogByNum(@PathVariable("dogNum") Long dogNum){ return dogClientApi.getDogByNum(dogNum); } @RequestMapping("/consumer/dog/getAllDog") List<Dog> getAllDog(){ return dogClientApi.getAllDog(); } }
2.3 测试看效果(本地单机)
-
启动,服务提供者8001 和 feign80,如下
2.4 测试看效果(服务器看负载均衡)
- 首先,将本地的8001停掉,启动服务器上的其中3台8001,如下:
- 然后重新访问,看效果(可见,可访问,并且自带负载均衡,默认是轮询)
http://localhost/consumer/dog/getDogByNum/1
2.5 关于Feign自定义负载均衡
- 用法同只用ribbon时,一样的用法,如下:
- 更多负载的详细配置,请看上篇文章吧:
4-Spring cloud之搭建Ribbon负载均衡——服务器上实操(下).
3. 注意问题以及常见问题
3.1 注意问题
- 注意 DogClientApi 的注入问题,启动类上下面注解包路径要对,否则注入不上,如下:
@EnableFeignClients(basePackages = {"com.liu.susu.api"}) //开启Fiegn功能 //@EnableFeignClients(basePackages = {"com.liu.susu"}) 可以往上,被包括就行
3.2 遇到的问题
3.2.1 feign.FeignException: status 404 reading DogClientApi#
- 问题描述:
feign.FeignException: status 404 reading DogClientApi#getDogByNum(Long); content: {"timestamp":1688683822011,"status":404,"error":"Not Found","message":"Not Found","path":"/consumer/dog/getDogByNum/1"}
- 问题原因
DogClientApi 里的映射路径和 服务提供者的controller里的映射路径不一致。- 看一下服务提供者的controller里的路径
- 再看一下
DogClientApi
里的路径
- 看一下服务提供者的controller里的路径
- 解决问题:
去掉DogClientApi
里的路径 /consumer,如下:
- 然后重新启动,问题已解决
4. 总结Feign
4.1 什么是Feign?为什么要使用Feign?
4.1.1 什么是Feign?
- Feign是一个声明式WebService客户端,使用Feign能让编写WebService客户端更加简单(只需要创建一个接口,然后上面加上注解
@FeignClient(value = "DOG-PROVIDER")
即可),即:Feign的主要目标是降低大家使用Http API的复杂性。 - 其实,Feign底层依赖于Java的动态代理机制,对原生Java Socket或者Apache HttpClient进行封装,实现了基于Http协议的远程过程调用。当然,Feign还在此基础上实现了负载均衡、熔断等机制。
- Feign自带负载均衡配置项,因为它集成了Ribbon
4.1.2 为什么要使用Feign?
- 使用Feign可以使得编写Web Service客户端变得更加简单、方便、快捷,提高代码的可读性、可维护性、可靠性和可用性。比如:
- 声明式的API:通过定义接口并注解它,Feign可以自动构造出符合该接口定义的Web Service客户端,使得客户端代码更加简洁、易读、易维护。像调用本地方法即可,从而提高了编码效率。
- 集中管理 Http 请求方法,代码边界更加清晰。
- 支持负载均衡:Feign内置了负载均衡功能( Ribbon),可以根据配置的负载均衡策略,自动选择可用的服务实例进行请求,从而提高系统的可用性和可靠性。
- 支持熔断器:Feign还支持熔断器功能,可以在服务不可用或超时的情况下,自动降级或熔断服务,从而保证系统的稳定性和可用性。
4.1.3 Feign 和 Ribbon
- Feign 是一个声明式的 Web Service客户端,它将服务调用抽象为接口定义,可以使得客户端代码更加简洁、易读、易维护;
Feign还内置了负载均衡和熔断器等功能。 - 而 Ribbon 是一个客户端负载均衡器,它通过配置负载均衡策略来选择可用的服务实例进行请求。
4.2 Feign依赖注入原理
@EnableFeignClients
注解是开启 Fiegn 功能的关键,通常会在该注解中添加FeignClient
的所在包,以便 Spring 容器能够扫描到所有的FeignClient
,并进行托管。后面我们便可以使用@Autowired
注解自动导入了。
4.3 自动装载和动态代理机制源码剖析
- 更多源码以及原理分析,可以参考下面的文章
Feign底层原理分析-自动装载&动态代理.