spring boot:Feign调用远程HTTP服务
2019-11-03 本文已影响0人
远方的橄榄树

1. 简介
- 调用远程服务器的HTTP接口时,通常有以下几种方式:
- JDK原生的URLConnection
- Apache http client
- Spring RestRemplate
- spring cloud feign
- spring cloud feign是声明性Web服务客户端。Feign客户端使用@FeignClient注册组合成组件,可以实现调用远程服务器。Feign通过接口的方式发起HTTP请求,这种方法相对简便。
2. 基本使用
1.创建maven项目
<?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
https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.9.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.samllbear</groupId>
<artifactId>feign-demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>feign-demo</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
<spring-cloud.version>Greenwich.SR3</spring-cloud.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--添加Feign依赖-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<!--集成org.apache.http-components, 使用httpclient作为client。
默认情况下,feign通过jdk中的HttpURLConnection向下游服务发起http请求,
这种方式缺乏连接池的支持,所以我们选用http-client-->
<dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-httpclient</artifactId>
<version>10.1.0</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<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>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
- 修改配置文件
feign:
httpclient:
enabled: true #开启httpClient
hystrix:
enabled: false #没有用到
server:
port: 8081
servlet:
context-path: /feign
logging:
level:
com:
samllbear:
feigindemo:
debug #开启DEBUG日志
user-url: http://localhost:${server.port}${server.servlet.context-path}
3、在启动类上中添加注解@EnableFeignClients
- 举例应用,我们通过
TestController
来实现对UserController
接口的调用。
/**
*实体类
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
@Accessors(chain = true)
public class User {
private Long id;
private String name;
private String nickname;
private Integer age;
}
/**
*DTO模板类
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
@Accessors(chain = true)
public class BaseDto<T> {
private Integer code;
private String msg;
private T data;
}
import feign.codec.Encoder;
import feign.form.FormEncoder;
import org.springframework.beans.factory.ObjectFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.boot.autoconfigure.http.HttpMessageConverters;
import org.springframework.cloud.openfeign.support.SpringEncoder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.context.annotation.Scope;
@Configuration
public class FeignConfig {
@Autowired
private ObjectFactory<HttpMessageConverters> messageConverters;
//Feign表单提交配置
@Bean
@Primary
@Scope(BeanDefinition.SCOPE_PROTOTYPE)
Encoder formEncoder() {
return new FormEncoder(new SpringEncoder(this.messageConverters));
}
}
/**
* 通过Feign调用UserController的api
*/
@FeignClient(value = "userClient", url = "${user-url}/user")
public interface UserClient {
@GetMapping("get/{id}")
BaseDto<User> get(@PathVariable("id") Long id);
@GetMapping("list")
BaseDto<List<User>> list();
//提交表单
//头部(header)默认是Content-Type: application/json
//我们将其设置成application/x-www-form-urlencoded便可提交表单对象(存放在body中)
@PostMapping(value = "add", consumes =
MediaType.APPLICATION_FORM_URLENCODED_VALUE)
BaseDto<User> add(@RequestBody User user);
}
/**
*创建一个service, 模拟数据库操作
*/
@Service
public class UserService {
private static List<User> users = new ArrayList<>();
static {
users.add(new User(1L, "亚索", "托儿所", 21));
users.add(new User(2L, "盲僧", "小学僧", 23));
users.add(new User(3L, "阿狸", null, 26));
users.add(new User(4L, "阿卡丽", null, 21));
}
public User get(Long id) {
Optional<User> userOpt = users.stream()
.filter(user -> user.getId().equals(id))
.findFirst();
return userOpt.isPresent() ? userOpt.get() : null;
}
public List<User> list() {
return users;
}
public User insert(User user) {
user.setId(users.size() + 1L);
users.add(user);
return user;
}
}
@RestController
@RequestMapping("user")
public class UserController {
@Autowired
private UserService userService;
@GetMapping("get/{id}")
public BaseDto<User> get(@PathVariable("id") Long id) {
User user = userService.get(id);
return new BaseDto<>(200, "访问成功", user);
}
@GetMapping("list")
public BaseDto<List<User>> list() {
List<User> users = userService.list();
return new BaseDto<>(200, "访问成功", users);
}
@PostMapping("add")
public BaseDto<User> add(User user) {
User newUser = userService.insert(user);
return new BaseDto<>(200, "访问成功", newUser);
}
}
/**
*测试类
*/
@RestController
@RequestMapping("/test")
public class TestController {
@Autowired
private UserClient userClient;
@GetMapping("testGet/{id}")
public BaseDto<User> testGet(@PathVariable("id") Long id) {
return userClient.get(id);
}
@GetMapping("/testList")
public BaseDto<List<User>> testList() {
return userClient.list();
}
@PostMapping("/testAdd")
public BaseDto<User> testAdd(User user) {
return userClient.add(user);
}
}
- 测试结果
a. 启动工程,访问http://127.0.0.1:8081/feign/test/testGet/1,浏览器显示
{
"code": 200,
"msg": "访问成功",
"data": {
"id": 1,
"name": "亚索",
"nickname": "托儿所",
"age": 21
}
}
b. 启动工程,访问http://127.0.0.1:8081/feign/test/testList,浏览器显示
{
"code": 200,
"msg": "访问成功",
"data": [
{
"id": 1,
"name": "亚索",
"nickname": "托儿所",
"age": 21
},
{
"id": 2,
"name": "盲僧",
"nickname": "小学僧",
"age": 23
},
{
"id": 3,
"name": "阿狸",
"nickname": null,
"age": 26
},
{
"id": 4,
"name": "阿卡丽",
"nickname": null,
"age": 21
}
]
}
c. 启动工程,访问http://127.0.0.1:8081/feign/test/testAdd,结果如下图所示:
3. 设置请求头部信息
- 方法一:利用
@RequestMapping
设置header
在UserClient.java
中添加
// @Headers({"Content-Type=application/json", "greeting=hello, world"})
// Feign提供的注解@Header不起作用,不知道为啥
@GetMapping(value = "header",
headers = {"Content-Type=application/json", "greeting=hello, world"})
User header();
在UserController.java
添加
@GetMapping("header")
public User header(HttpServletRequest request) {
System.out.println(request.getHeader("greeting")); // hello, World
System.out.println(request.getHeader("Content-Type")); // application/json
return new User(6L, "劫", "儿童劫", 43);
}
在 TestController.java
中添加
@GetMapping("testHeader")
public User testHeader() {
return userClient.header();
}
启动工程,访问http://127.0.0.1:8081/feign/test/testHeader,结果如下:{ "id": 6, "name": "劫", "nickname": "儿童劫", "age": 43 }
,并且控制台打印hello, World
和application/json
。
- 方法二:实现
RequestInterceptor
接口设置header信息
FeignConfig.java
修改如下
/**
*Feign发起请求的拦截器
*/
public class FeignInterceptor implements RequestInterceptor {
@Override
public void apply(RequestTemplate requestTemplate) {
requestTemplate.header("Greeting", "hello, World")
.header("TimeStamp", Long.toString(System.currentTimeMillis()));
}
}
import feign.Logger;
import feign.codec.Encoder;
import feign.form.spring.SpringFormEncoder;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.context.annotation.Scope;
@Configuration
public class FeignConfig {
@Autowired
private ObjectFactory<HttpMessageConverters> messageConverters;
//Feign表单提交配置
@Bean
@Primary
@Scope(BeanDefinition.SCOPE_PROTOTYPE)
public Encoder feignFormEncoder() {
return new SpringFormEncoder(new SpringEncoder(messageConverters));
//feign拦截器,设置header信息
@Bean
FeignInterceptor feignInterceptor() {
return new FeignInterceptor();
}
//开启feign日志
@Bean
Logger.Level feignLevel() {
return Logger.Level.FULL;
}
}
修改UserClient.java
的@FeignClient
注解
//设置配置文件
@FeignClient(value = "userClient", url = "${user-url}/user",
configuration = FeignConfig.class)