Feign 是 spring Cloud Netflix 组件中的一量级 Restful的HTTP 服务客户端,实现了负载均衡和 Rest 调用的开源框架,封装了Ribbon和RestTemplate,实现WebService的面向接口编程。
Feign 简化了RestTemplate代码,是声明式服务调用组件:核心就是像调用本地方法一样调用远程方法。让开发者无需关注,远程调用过程,和交互细节。
Feign 本身并不支持spring MVC注解,它有一套自己的注解,为了更方便使用Spring Cloud孵化OpenFeign。并且支持spring mvc的注解,例如:@RequestMapping、@PathVariable。
openFeign 的@FeignClient可以解析Spring MVC的@RequestMapping注解下的接口,并通过动态代理方式产生实现类,实现类中做负载均衡调用服务。
soringboot 2.0 以后基本使用 OpenFeign
官网
特性
- Hystrix 和它的 Fallback
- HTTP 请求响应的压缩
- Ribbon 负载均衡客户端
- 可拔插的HTTP编码器和解码器
- 拔插的注解支持,包括Feign 注解 和JAX-RS 注解
- Feign 微服务调用 (nacos 作为注册中心)
- 快速开始
- 项目
- pom 文件
- goods
- order
- 测试
- @FeignClient ‼️
- Feign 日志开启
- HTTP Client 替换
- 修改
- 如果使用 okhttp 的可以自定义配置
- 参数传递
- 特殊需求,Get方法传递了多参数
- 实现Token 传递
- 底部
项目预览
项目 pom 文件父项目
org.springframework.boot spring-boot-starter-parent 2.1.3.RELEASE org.springframework.boot spring-boot-starter-web com.alibaba.cloud spring-cloud-alibaba-nacos-discovery 2.0.1.RELEASE org.springframework.cloud spring-cloud-dependencies Finchley.RELEASE pom import spring-milestones Spring Milestones https://repo.spring.io/libs-milestone false
goods 消费者
springcloud-openfeig org.example 1.0-SNAPSHOT 4.0.0 openfeig-goods 商品服务 8 8
order 生产者
goodsspringcloud-openfeig org.example 1.0-SNAPSHOT 4.0.0 openfeig-order 8 8 org.springframework.cloud spring-cloud-starter-openfeign
server:
port: 8081
spring:
application:
name: openfeig-goods
cloud:
nacos:
server-addr: ...:8848
@RestController
public class GoodsController {
@GetMapping("hello/{message}")
public String hello(@PathVariable String message){
return "来自goods的消息 =="+ message;
}
}
order
server:
port: 8080
spring:
application:
name: openfeig-order
cloud:
nacos:
server-addr: ....:8848
@SpringBootApplication
//开启远程调用的注解 扫描,可以扫描 @FeignClient
@EnableFeignClients
public class OpenFeigOrderApplication {
public static void main(String[] args) {
SpringApplication.run(OpenFeigOrderApplication.class,args);
}
}
// 准备远程调用的接口
@FeignClient(name = "openfeig-goods")
public interface GoodsClienService {
@GetMapping("hello/{message}")
String hello(@PathVariable String message);
}
//提供对外暴露的 api
@RestController
public class OrderController {
//nacos 客户端信息
@Autowired
private LoadBalancerClient loadBalancerClient;
//调用的 service
@Autowired
private GoodsClienService cartService;
@GetMapping("add/{message}")
public String add(@PathVariable String message){
ServiceInstance choose = loadBalancerClient.choose("openfeig-goods");
return cartService.hello(message) +" "+ choose.getHost() + "--" + choose.getPort();
}
}
测试
@FeignClient ‼️
需要 @EnableFeignClients来开启扫描
当定义的Feign接口中的方法被调用时,通过JDK的代理方式,生成具体的 RequestTemplate。这个对象中,封装了http需要的全部信息。参数、方法名等等
| 属性 | 说明 |
|---|---|
| name | 指定FeignClient等名称,如果项目使用了Ribbon,那么name属性会作为微服务等名称,用于服务发现 |
| url | 一般用于调试,可以手动指定@FeignClient调用的服务地址 |
| decode404 | 当发生404错误时,如果该字段值为true,会调用decoder进行编码,否则抛出FeignException |
| configuration | Feign 配置列,可以自定义Feign 的 Encoder、Decoder、LogLevel、Contract 也可以在配置文件中配置 |
| fallback | 定义容错的处理类,当调用远程接口失败或超时时,会调用对应接口的容错逻辑,fallback指定的类必须实现 @FeignClient标记的接口 |
| fallbackFactory | 工厂类,用于生成fallback类实例,通过这个属性可以实现每个接口通用的容错逻辑,减少重复的代码 |
| path | 定义当前FeignClient 的统一前缀 |
需要在配置文件中 logback.xml 把日志级别改为debug
# open feign 日志设置
logging:
level:
com.springcloud.study.service.ProviderClientService: debug
//全局的配置 日志配置类
@Configuration
public class FeignServiceConfig {
@Bean
Logger.Level feignLogger(){
return Logger.Level.FULL;
}
}
//指定单独的配置 MyFeignConfig 是一个 日志的配置类 @FeignClient(configurtion = MyFeignConfig.class)HTTP Client 替换
修改Feign 默认使用的是JDK原生的 URLConnection发送HTTP请求,没有用链接池。对每个地址都会建立一个长链接。
feign 的HTTP客户端支持3中框架HttpURLConnection、HttpClient、OkHttp
默认是 HttpURLConnection
io.github.openfeign feign-httpclient io.github.openfeign feign-okhttp
feign:
httpclient:
# 开启httpclient
enabled: true
如果使用 okhttp 的可以自定义配置
okHttp 优势
- 支持SPDY,合并多个请求到同一个主机
- 使用链接池
- 使用GZIP压缩减少传输数据体积
- 缓存响应结果,减少重复请求
@Configuration
@ConditionalOnClass(Feign.class)
@AutoConfigureBefore(FeignAutoConfiguration.class)
public class FeignOkHttpConfig {
@Bean
public okhttp3.OkHttpClient okHttpClient() {
return new OkHttpClient.Builder()
.connectTimeout(60, TimeUnit.SECONDS) //链接超时时间
.readTimeout(60,TimeUnit.SECONDS) //读超时
.writeTimeout(60,TimeUnit.SECONDS) //写超时
.retryOnConnectionFailure(true) //自动重试
.connectionPool(new ConnectionPool()) //创建链接池 okhttp包的
.build();
}
}
参数传递
特殊需求,Get方法传递了多参数get 方式,@PathVariable、@RequestParm注解来接收
post 方式 ,@RequestBody接收请求参数
需要时实现Feign 的RequestInterceptor中的pally进行统一处理
TODO 还是用 post 方法发送省事
实现Token 传递认证鉴权的时候,使用JWT,或spring security 都需要拿到token
RequestInterceptor 拦截器,在feign 调用的时候,向请求头里添加需要传递的token
基于上面的代码改动 注意token 大小写的问题
//生产端
@Component
public class FeignTokenAddInterceptor implements RequestInterceptor {
@Override
public void apply(RequestTemplate requestTemplate) {
if(null==getHttpServletRequest()){
//此处省略日志记录
return;
}
//将获取Token对应的值往下面传
requestTemplate.header("oauthToken", getHeaders(getHttpServletRequest()).get("oauthtoken"));
}
private HttpServletRequest getHttpServletRequest() {
try {
return ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
} catch (Exception e) {
return null;
}
}
private Map getHeaders(HttpServletRequest request) {
Map map = new LinkedHashMap<>();
Enumeration enumeration = request.getHeaderNames();
while (enumeration.hasMoreElements()) {
String key = enumeration.nextElement();
String value = request.getHeader(key);
map.put(key, value);
}
System.out.println("模拟token " + map.get("oauthtoken"));
return map;
}
}
@GetMapping("hello/{message}")
public String hello(@PathVariable("message") String message, HttpServletRequest req){
String oauthToken = req.getHeader("oauthToken");
System.out.println( "token =" + oauthToken);
return "来自goods的消息 =="+ message + oauthToken;
}
底部


