1. 基本使用
1.1 安装
在调用方安装
maven方式安装
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
gradle方式安装
//OpenFeign
implementation 'org.springframework.cloud:spring-cloud-starter-openfeign'
1.2 启动类增加注解@EnableFeignClients
@SpringBootApplication
@EnableFeignClients
public class ChannelApplication {
public static void main(String[] args) {
SpringApplication.run(ChannelApplication.class, args);
}
}
1.3 编写feign客户端
package com.springcloud.user_center.feignclients;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
/**
* @Author:壹心科技BCF项目组 wangfan
* @Date:Created in 2020/10/6 01:06
* @Project:epec
* @Description:订单中心收藏模块Feign客户端
* @Modified By:wangfan
* @Version: V1.0
*/
/**
* value: 调用服务名称
* path: 统一的路径前缀
*/
@FeignClient(value = "ORDER-CENTER", path = "/order")
public interface OrderCenterCollectClientV3 {
/**
* 通过用户ID获取收藏的订单列表
* @param userId
* @return
*/
@GetMapping("/user/collect/list/{userId}")
public String getOrderList(@PathVariable Integer userId);
}
1.4 controller
import com.springcloud.user_center.feignclients.OrderCenterCollectClientV3;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
/**
* @Author:壹心科技BCF项目组 wangfan
* @Date:Created in 2020/10/5 13:17
* @Project:epec
* @Description:TODO
* @Modified By:wangfan
* @Version: V1.0
*/
@RestController
public class CollectController {
@Autowired
private OrderCenterCollectClientV3 orderCenterCollectClientV3;
/**
* 获取用户收藏的订单列表版本v3
* @param userId
* @return
*/
@GetMapping("/collect/orders/v3/{userId}")
public Object getCollectOrdersV3(@PathVariable("userId") Integer userId){
String result = orderCenterCollectClientV3.getOrderList(userId);
return result;
}
}
1.5 验证
打开浏览器. 输入URL: http://localhost:9000/collect/orders/v3/1
获取结果:
{“code”:”0000”,”msg”:”success”,”rersult”:[{“name”:”name1”,”id”:”100034”},{“name”:”name2”,”id”:”100035”},{“name”:”name3”,”id”:”100036”}]}
2. 进阶
2.1 Feign和OpenFeign的关系
Feign不在Spring生态内, 本身不支持Spring MVC的注解,它有一套自己的注解.
OpenFeign是Spring Cloud为Feign适应Spring生态开发的组件, 在Feign的基础上支持了Spring MVC的注解,如@RequesMapping
等等。OpenFeign的@FeignClient
可以解析SpringMVC的@RequestMapping注解下的接口,并通过动态代理的方式产生实现类,实现类中做负载均衡并调用其他服务。
2.2 单独使用OpenFeign
编写feign客户端
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
/**
* @Author:壹心科技BCF项目组 wangfan
* @Date:Created in 2020/10/6 01:06
* @Project:epec
* @Description:订单中心收藏模块Feign客户端,单独使用OpenFeign.不集成Eureka
* @Modified By:wangfan
* @Version: V1.0
*/
/**
* value: 调用服务名称,随便指定一个
* url: 具体地址
*/
@FeignClient(value = "MY-ORDER-CENTER", url = "http://localhost:10000/order")
public interface OrderCenterCollectClientV4 {
/**
* 通过用户ID获取收藏的订单列表
* @param userId
* @return
*/
@GetMapping("/user/collect/list/{userId}")
public String getOrderList(@PathVariable Integer userId);
}
controller中使用
import com.springcloud.user_center.feignclients.OrderCenterCollectClientV4;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
/**
* @Author:壹心科技BCF项目组 wangfan
* @Date:Created in 2020/10/5 13:17
* @Project:epec
* @Description:TODO
* @Modified By:wangfan
* @Version: V1.0
*/
@RestController
public class CollectController {
@Autowired
private OrderCenterCollectClientV4 orderCenterCollectClientV4;
/**
* 获取用户收藏的订单列表版本v4
* @param userId
* @return
*/
@GetMapping("/collect/orders/v4/{userId}")
public Object getCollectOrdersV4(@PathVariable("userId") Integer userId){
String result = orderCenterCollectClientV4.getOrderList(userId);
return result;
}
}
验证
打开浏览器. 输入URL: http://localhost:9000/collect/orders/v3/1
获取结果:
{“code”:”0000”,”msg”:”success”,”rersult”:[{“name”:”name1”,”id”:”100034”},{“name”:”name2”,”id”:”100035”},{“name”:”name3”,”id”:”100036”}]}
2.3 项目里使用Feign
现有2个项目, 用户中心 user_center 和 订单中心 order_center. 现有一个业务需要查询用户收藏的订单列表. 因此需要从用户中心发起请求调用订单中心的服务.
在项目里真正的做法如下:
- 创建
- 编写order_center接口 ```java import org.springframework.web.bind.annotation.*;
/**
- @Author:壹心科技BCF项目组 wangfan
- @Date:Created in 2020/10/5 13:17
- @Project:epec
- @Description:TODO
- @Modified By:wangfan
@Version: V1.0 */ @RestController @RequestMapping(“/order”) public class OrderController {
/**
- 通过用户ID获取收藏的订单列表
- @param userId
- @return */ @GetMapping(“/user/collect/list/{userId}”) public String getOrderList(@PathVariable Integer userId){ //写死返回数据 return “{\”code\”:\”0000\”,\”msg\”:\”success\”,\”rersult\”:[{\”name\”:\”name1\”,\”id\”:\”100034\”},{\”name\”:\”name2\”,\”id\”:\”100035\”},{\”name\”:\”name3\”,\”id\”:\”100036\”}]}”; }
}
未完待续.....
<a name="jTNSQ"></a>
## 2.4 Feign注意事项
1. 带参请求
带参请求的话如果参数是在URL上, 必须加上`@RequestParam`注解
```java
/**
* 通过用户ID获取收藏的订单列表
* @param userId
*/
@GetMapping("/user/collect/list")
public String getOrderList(@RequestParam("userId") Integer userId);
- Feign默认所有带参数的请求都是Post,想要使用指定的提交方式需引入依赖
<!-- 取消Feign默认所有带参数的请求都是Post的限制 -->
<dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-httpclient</artifactId>
</dependency>
指明提交方式://取消Feign默认所有带参数的请求都是Post的限制
implementation 'io.github.openfeign:feign-httpclient'
/**
* 通过用户ID获取收藏的订单列表
* @param userId
*/
@RequestMapping(value = "/user/collect/list", method = RequestMethod.POST)
@GetMapping("/user/collect/list")
public String getOrderList(@RequestParam("userId") Integer userId);
2.5 feign拦截器
实现RequestInterceptor
接口即可
import feign.RequestInterceptor;
import feign.RequestTemplate;
import org.springframework.stereotype.Component;
/**
* @Author:壹心科技BCF项目组 wangfan
* @Date:Created in 2020/10/6 23:36
* @Project:epec
* @Description:Feign拦截器
* @Modified By:wangfan
* @Version: V1.0
*/
@Component
public class FeignRequestInteceptor implements RequestInterceptor {
@Override
public void apply(RequestTemplate template) {
String feignTargetUrl = template.feignTarget().url();
String path = template.path();
System.out.println("feign拦截器"+FeignRequestInteceptor.class.getName()+" 打印, 请求url: " + feignTargetUrl+path);
}
}
配置文件增加配置
#指定Feign拦截器
feign.client.config.service-valuation.request-interceptors[0]=com.springcloud.user_center.config.FeignRequestInteceptor
2.6 Feign开启调用权限
feign的默认配置类是:org.springframework.cloud.openfeign.FeignClientsConfiguration
。默认定义了feign使用的编码器,解码器等。
允许使用@FeignClient的configuration
的属性自定义Feign配置。自定义的配置优先级高于上面的FeignClientsConfiguration
。
2.6.1 服务提供方(order_center)开启权限认证
加上Security即可, 参照Security配置即可, 点击跳转….
2.6.2 服务提供方增加权限配置类
继承WebSecurityConfigurerAdapter类, 并重写 configure(HttpSecurity http)方法
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
/**
* @Author:壹心科技BCF项目组 wangfan
* @Date:Created in 2020/10/7 00:03
* @Project:epec
* @Description:开始Feign权限
* @Modified By:wangfan
* @Version: V1.0
*/
@Configuration
@EnableWebSecurity
public class SecurityConfigForFeign extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
// 关闭csrf
http.csrf().disable();
// 表示所有的访问都必须认证,认证处理后才可以正常进行
http.httpBasic().and().authorizeRequests().anyRequest().fullyAuthenticated();
// 所有的rest服务一定要设置为无状态,以提升操作效率和性能
http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
}
}
再使用Feign调用order_center接口则报错, 401未认证.
user_center(服务调用方)使用Feign调用order_center(服务提供方)接口时, 增加权限认证的方式有2种
- 自定义配置
@Bean
。 - 使用Feign拦截器。
2.6.3 user_center(服务调用方)增加自定义配置@Bean
```java import feign.auth.BasicAuthRequestInterceptor; import org.springframework.context.annotation.Bean;
/**
- @Author:壹心科技BCF项目组 wangfan
- @Date:Created in 2020/10/7 01:39
- @Project:epec
- @Description:Feign权限认证
- @Modified By:wangfan
@Version: V1.0 */ public class FeignAuthConfiguration {
//Feign权限认证Bean @Bean public BasicAuthRequestInterceptor basicAuthRequestInterceptor() {
return new BasicAuthRequestInterceptor("wangfan", "123");
}
}
`@FeignClient` 加上`configuration`属性
```java
@FeignClient(value = "ORDER-CENTER", path = "/order", configuration = FeignAuthConfiguration.class)
OK,可以正常访问了。
如果在配置类上添加了@Configuration
注解,并且该类在@``ComponentScan
所扫描的包中,那么该类中的配置信息就会被所有的@FeignClient
共享。
最佳实践是:不指定@Configuration
注解(或者指定configuration,用注解忽略),而用手动指定配置类:
@FeignClient(name = "service-valuation",configuration = FeignAuthConfiguration.class)
2.6.4 user_center(服务调用方)使用Feign拦截器
import feign.RequestInterceptor;
import feign.RequestTemplate;
import org.springframework.stereotype.Component;
import java.util.Base64;
/**
* @Author:壹心科技BCF项目组 wangfan
* @Date:Created in 2020/10/6 23:36
* @Project:epec
* @Description:Feign调用其他模块接口认证拦截器
* @Modified By:wangfan
* @Version: V1.0
*/
@Component
public class FeignAuthRequestInteceptor implements RequestInterceptor {
@Override
public void apply(RequestTemplate template) {
template.header("Authorization", "Basic "+ Base64.getEncoder().encodeToString("wangfan:123".getBytes()));
}
}
配置文件指定拦截器位置
#指定Feign调用其他模块接口认证拦截器
feign.client.config.service-valuation.request-interceptors[1]=learn.wangfan.springcloud.user_center.config.FeignAuthRequestInteceptor
2.7 Feign超时和重试
Feign默认支持Ribbon, Ribbon的重试机制和Feign的重试机制有冲突,所以源码中默认关闭Feign的重试机制,使用Ribbon的重试机制
#连接超时时间(ms)
ribbon.ConnectTimeout=1000
#业务逻辑超时时间(ms)
ribbon.ReadTimeout=6000
#同一台实例最大重试次数,不包括首次调用
ribbon.MaxAutoRetries=1
#重试负载均衡其他的实例最大重试次数,不包括首次调用
ribbon.MaxAutoRetriesNextServer=1
#是否所有操作都重试
ribbon.OkToRetryOnAllOperations=false
使用ribbon重试机制,请求失败后,每个6秒会重新尝试