需求
一个应用通过接口,调用另一个应用的接口。使用OpenFeign来实现接口调用。
说明
通过OpenFeign(本文接下来简称Feign)调用远程接口,需要Eureka注册中心的支持。
OpenFeign调用接口的逻辑如下:
- 提供接口的应用(A),将自身注册到Eureka服务器(注册中心);应用A需要给自己起一个应用名称;
- 调用接口的应用(B),从Eureka读取所有已注册服务的信息;
- B应用的Feign客户端,通过服务的应用名称,从已注册服务的信息中,找到应用A(对应的IP地址和端口号),从而调用A的接口。
本文主要内容
本文主要讲述,如何配置一个注册中心(Eureka),Feign的配置,以及使用Feign来调用接口。
主要包含三个部分:
- 配置Eureka注册中心(单体,非集群);
- 配置提供接口的应用,注册到Eureka:提供被调用的接口;
- 配置调用接口的应用,从Eureka获取到被调用方地址:调用接口。
Eureka服务器
1. 依赖
<dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
2. 配置(application.properties)
此配置为单体服务器配置,非集群配置。
server.port=8761# 主机名,不配置的时候将根据操作系统的主机名获取。
eureka.instance.hostname=localhost# 不将自身注册到注册中心。是否将自己注册到注册中心,默认为true。单个Eureka服务器,不需要注册自身,配置为false;如果是Eureka集群,则需要注册自身,即配置为true。
eureka.client.registerWithEureka=false
# 是否从注册中心获取服务注册信息,默认为true。
eureka.client.fetchRegistry=false
# 注册中心对外暴露的注册地址
eureka.client.serviceUrl.defaultZone=http://${eureka.instance.hostname}:${server.port}/eureka/
3. 开启Eureka服务器
在 Application 启动类上,添加注解 @EnableEurekaServer
.
示例代码:
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;@EnableEurekaServer
@SpringBootApplication
public class EurekaServerDemoApplication {public static void main(String[] args) {SpringApplication.run(EurekaServerDemoApplication.class, args);}}
FeignServer
提供接口的应用,可以通过Feign来调用接口。
1. 依赖
- Eureka Discovery Client
<dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
2. 配置(application.properties)
server.port=8081# 应用名称
spring.application.name=feign-server
# 使用 ip地址:端口号 注册
eureka.instance.prefer-ip-address=true
eureka.instance.instance-id=${spring.cloud.client.ip-address}:${server.port}
# 注册中心地址
eureka.client.service-url.defaultZone=http://localhost:8761/eureka/
3. 提供接口
package com.example.feign.server.controller;import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;@RestController
@RequestMapping("feign_server_path")
public class FeignServerController {@GetMapping("hello")public String hello() {return "hello feign server!";}@GetMapping("data")public String getData() {return "来自FeignServer的数据!";}}
Feign客户端
通过Feign,调用FeignServer应用的接口。
1. 依赖
需要引入两个依赖:
- Eureka Discovery Client
- OpenFeign
<dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
注意:需要通过 <dependencyManagement>
和 <properties>
,管理 spring cloud 版本。如果项目中已经添加,则无需再额外修改。
<dependencyManagement><dependencies><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-dependencies</artifactId><version>${spring-cloud.version}</version><type>pom</type><scope>import</scope></dependency></dependencies>
</dependencyManagement>
<properties><spring-cloud.version>2021.0.8</spring-cloud.version>
</properties>
2. 配置(application.properties)
server.port=8082# 不将自身注册到Eureka注册中心。本配置为是否将自己注册到注册中心,默认为true。
eureka.client.registerWithEureka=false
# 注册中心地址
eureka.client.service-url.defaultZone=http://localhost:8761/eureka/
3. 开启Feign客户端
在 Application 启动类上,添加注解 @EnableFeignClients
.
示例代码:
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.openfeign.EnableFeignClients;@EnableFeignClients
@SpringBootApplication
public class FeignClientDemoApplication {public static void main(String[] args) {SpringApplication.run(FeignClientDemoApplication.class, args);}}
4. 定义接口(与FeignServer对应)
注解 @FeignClient
:表示Feign接口。
value:Feign所调用的应用的应用名称。
path:Feign所调用的应用的对应Controller的接口路径,即 Controller 上 @RequestMapping 中的接口路径。
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;@FeignClient(value = "feign-server", path = "feign_server_path")
public interface FeignInvocationService {@GetMapping("data")String getFeignServerData();}
5. 调用Feign接口
像调用本地方法一样,调用Feign接口。
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;import com.example.feign.client.feign.FeignInvocationService;@RestController
@RequestMapping("feign_client")
public class FeignClientController {@GetMapping("hello")public String hello() {return "hello feign client!";}@Autowiredprivate FeignInvocationService feignInvocationService;@GetMapping("feign_server_data")public String getFeignServerData() {return "通过FeignClient调用:" + feignInvocationService.getFeignServerData();}}
调用示例
Eureka
FeignServer的接口直接调用
FeignClient通过Feign,调用FeignServer的接口
Get方法报错
两种报错
Body parameter 0 was null
Feign客户端,调用Get方法时,接口包含一个参数,报错:
java.lang.IllegalArgumentException: Body parameter 0 was null
Method has too many Body parameters
Feign客户端,调用Get方法时,接口包含多个参数,报错:
Method has too many Body parameters
报错接口的原始代码
Body parameter 0 was null
- Feign服务器端接口
@GetMapping("result")public String getData(String account) {return "从FeignServer查询的数据!入参为:" + account;}
- Feign客户端
@GetMapping("result")String getData(String account);
Method has too many Body parameters
- Feign服务器端接口
@GetMapping("two_params")public String getDataByTwoParam(String account, String name) {return "从FeignServer查询的数据!account=" + account + ",name=" + name;}
- Feign客户端
@GetMapping("two_params")public String getDataByTwoParam(String account, String name);
解决方法:@RequestParam
Feign接口参数添加@RequestParam
注解。
Feign客户端,修改后的代码如下:
import org.springframework.web.bind.annotation.RequestParam;
@GetMapping("result")String getData(@RequestParam("account") String account);@GetMapping("two_params")public String getDataByTwoParam(@RequestParam("account") String account, @RequestParam("name") String name);
完整的Feign客户端代码示例
package com.example.feign.client.feign;import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;@FeignClient(value = "feign-server", path = "feign_server_path")
public interface FeignInvocationService {@GetMapping("data")String getFeignServerData();@GetMapping("result")String getData(@RequestParam("account") String account);@GetMapping("two_params")public String getDataByTwoParam(@RequestParam("account") String account, @RequestParam("name") String name);}