Spring Cloud OpenFeign 是声明式的服务调用工具,它整合了Ribbon和Hystrix,拥有负载均衡和服务容错功能。本文开始学习对OpenFeign的使用。
属性名 | 默认值 | 作用 | 备注 |
---|---|---|---|
value | 空字符串 | 调用服务名称,和name属性相同 | |
serviceId | 空字符串 | 服务id,作用和name属性相同 | 已过期 |
name | 空字符串 | 调用服务名称,和value属性相同 | |
url | 空字符串 | 全路径地址或hostname,http或https可选 | |
decode404 | false | 配置响应状态码为404时是否应该抛出FeignExceptions | |
configuration | {} | 自定义当前feign client的一些配置 参考FeignClientsConfiguration | |
fallback | void.class | 熔断机制,调用失败时,走的一些回退方法,可以用来抛出异常或给出默认返回数据。 | 底层依赖hystrix,启动类要加上@EnableHystrix |
path | 空字符串 | 自动给所有方法的requestMapping前加上前缀,类似与controller类上的requestMapping | |
primary | true |
feign.client.config.default.connect-timeout=5000
feign.client.config.default.read-timeout=5000
feign.client.config.default.logger-level=basic
#开启Okhttp请求
feign.okhttp.enabled=true
#在Feign中开启Hystrix
feign.hystrix.enabled=true
#开启请求数据压缩
feign.compression.request.enabled=true
#开启支持压缩的MIME TYPE
feign.compression.request.mime-types=text/xml,application/xml,application/json
#开启响应GZIP压缩
feign.compression.response.enabled=true
#配置压缩数据大小的下限
feign.compression.request.min-request-size=2048
**注:**由于开启GZIP压缩之后,Feign之间的调用数据通过二进制协议进行传输,返回值需要修改为ResponseEntity
示例:
@PostMapping("/order/{productId}")
ResponseEntity<byte[]> addCart(@PathVariable("productId") Long productId);
方式一:通过java bean 的方式指定。
@EnableFeignClients注解上有个defaultConfiguration属性,可以指定默认Feign Client的一些配置。
@EnableFeignClients(defaultConfiguration = DefaultFeignConfiguration.class)
@Configuration
public class DefaultFeignConfiguration {
@Bean
public Retryer feignRetryer() {
return new Retryer.Default(1000,3000,3);
}
}
方式二:通过配置文件方式指定。
#连接超时
feign.client.config.default.connect-timeout=5000
#读取超时
feign.client.config.default.read-timeout=5000
#日志等级
feign.client.config.default.logger-level=basic
方式一:通过java bean的方式指定
@Configuration
public class DefaultFeignConfiguration {
@Bean
public Logger.Level feignLoggerLevel(){
return Logger.Level.BASIC;
}
}
方式二:通过配置文件指定
目前,feign不支持GET请求直接传递POJO对象的,目前解决方法如下:
实战示例:
通过feign的拦截器来实现。
@Component
@Slf4j
public class FeignCustomRequestInteceptor implements RequestInterceptor {
@Autowired
private ObjectMapper objectMapper;
@Override
public void apply(RequestTemplate template) {
if (HttpMethod.GET.toString() == template.method() && template.body() != null) {
//feign 不支持GET方法传输POJO 转换成json,再换成query
try {
Map<String, Collection<String>> map = objectMapper.readValue(template.bodyTemplate(), new TypeReference<Map<String, Collection<String>>>() {
});
template.body(null);
template.queries(map);
} catch (IOException e) {
log.error("cause exception", e);
}
}
}
<?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>eureka</artifactId>
<groupId>com.hjl</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>feign-service</artifactId>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<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>
</dependencies>
</project>
package com.hjl.feign.controller;
import com.hjl.feign.pojo.Result;
import com.hjl.feign.pojo.User;
import com.hjl.feign.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.List;
/**
* TODO:
*
* @Version 1.0
* @Author HJL
* @Date 2021/12/31 9:00
*/
@RestController
@RequestMapping("/user")
public class UserFeignController {
@Autowired
private UserService userService;
@PostMapping("/insert")
public Result insert(@RequestBody User user) {
return userService.insert(user);
}
@GetMapping("/{id}")
public Result<User> getUser(@PathVariable Long id) {
return userService.getUser(id);
}
@GetMapping("/listUsersByIds")
public Result<List<User>> listUsersByIds(@RequestParam List<Long> ids) {
return userService.listUsersByIds(ids);
}
@GetMapping("/getByUsername")
public Result<User> getByUsername(@RequestParam String username) {
return userService.getByUsername(username);
}
@PostMapping("/update")
public Result update(@RequestBody User user) {
return userService.update(user);
}
@PostMapping("/delete/{id}")
public Result delete(@PathVariable Long id) {
return userService.delete(id);
}
}
package com.hjl.feign.service;
import com.hjl.feign.pojo.Result;
import com.hjl.feign.pojo.User;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.*;
import java.util.List;
/**
* TODO:
*
* @Version 1.0
* @Author DELL
* @Date 2021/12/31 8:58
*/
@FeignClient(value = "user-service", fallback = UserFallbackService.class)
public interface UserService {
@PostMapping("/user/insert")
Result insert(@RequestBody User user);
@GetMapping("/user/{id}")
Result<User> getUser(@PathVariable Long id);
@GetMapping("/user/listUsersByIds")
Result<List<User>> listUsersByIds(@RequestParam List<Long> ids);
@GetMapping("/user/getByUsername")
Result<User> getByUsername(@RequestParam String username);
@PostMapping("/user/update")
Result update(@RequestBody User user);
@PostMapping("/user/delete/{id}")
Result delete(@PathVariable Long id);
}
通过@FeignClient(value = “user-service”, fallback = UserFallbackService.class)注解完成feign的服务配置。
value属性执行远程调用的服务;
fallback 属性指定服务降级的具体实现类。
package com.hjl.feign.service;
import com.hjl.feign.pojo.Result;
import com.hjl.feign.pojo.User;
import org.springframework.stereotype.Component;
import java.util.List;
/**
* TODO:
*
* @Version 1.0
* @Author HJL
* @Date 2021/12/31 9:01
*/
@Component
public class UserFallbackService implements UserService {
@Override
public Result insert(User user) {
return new Result("调用失败,服务被降级",500);
}
@Override
public Result<User> getUser(Long id) {
return new Result("调用失败,服务被降级",500);
}
@Override
public Result<List<User>> listUsersByIds(List<Long> ids) {
return new Result("调用失败,服务被降级",500);
}
@Override
public Result<User> getByUsername(String username) {
return new Result("调用失败,服务被降级",500);
}
@Override
public Result update(User user) {
return new Result("调用失败,服务被降级",500);
}
@Override
public Result delete(Long id) {
return new Result("调用失败,服务被降级",500);
}
}
feign组件的底层通过ribbon实现了负载均衡功能,需要依赖eureka功能完成服务地址的查找。
package com.hjl.feign;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.cloud.openfeign.EnableFeignClients;
/**
* TODO:
*
* @Version 1.0
* @Author HJL
* @Date 2021/12/30 22:02
*/
@EnableEurekaClient
@EnableFeignClients
@SpringBootApplication
public class FeignServiceApplication {
public static void main(String[] args) {
SpringApplication.run(FeignServiceApplication.class,args);
}
}
#服务端口
server.port=1009
#服务名称
spring.application.name=feign-service
#############################eureka相关配置###################################
#开启服务注册
eureka.client.register-with-eureka=true
#开启从服务注册中心获取服务地址的功能
eureka.client.fetch-registry=true
#指定服务注册中心地址
eureka.client.service-url.defaultZone=http://root:root@localhost:1001/eureka,http://root:root@localhost:1010/eureka
##############################feign相关配置##################################
#连接超时
feign.client.config.default.connect-timeout=5000
#读取超时
feign.client.config.default.read-timeout=5000
#日志等级
feign.client.config.default.logger-level=basic
#开启Okhttp请求
feign.okhttp.enabled=true
#在Feign中开启Hystrix
feign.hystrix.enabled=true
#开启请求数据压缩
feign.compression.request.enabled=true
#开启支持压缩的MIME TYPE
feign.compression.request.mime-types=text/xml,application/xml,application/json
#开启响应GZIP压缩
feign.compression.response.enabled=true
#配置压缩数据大小的下限
feign.compression.request.min-request-size=2048
#配置远程调用服务-用户服务
service-url.user-service=http://user-service
关于Hystrix启动配置说明:
1、Spring Cloud 2020之前的版本,只需在配置文件中设置feign.hystrix.enabled=true;
2、Spring Cloud 2020之后的版本,feign.hystrix.enabled=true无法解析,需要配置:feign.circuitbreaker.enabled=true。
在注册中心查看服务
表明服务已经成功注册。
访问feign-service服务端口多次调用服务:http://127.0.0.1:1009/user/1,
对feign中的服务降级功能进行测试,关闭user-service服务后,再次请求接口,服务请求已经完成服务降级处理;
Feign是声明式的服务调用工具,我们只需创建一个接口并用注解的方式来配置它,就可以实现对某个服务接口的调用,简化了直接使用RestTemplate来调用服务接口的开发量。Feign具备可插拔的注解支持,同时支持Feign注解、JAX-RS注解及SpringMvc注解。当使用Feign时,Spring Cloud集成了Ribbon和Eureka以提供负载均衡的服务调用及基于Hystrix的服务容错保护功能。