1. 基本使用

1.1 安装

在调用方安装
maven方式安装

  1. <dependency>
  2. <groupId>org.springframework.cloud</groupId>
  3. <artifactId>spring-cloud-starter-openfeign</artifactId>
  4. </dependency>

gradle方式安装

  1. //OpenFeign
  2. implementation 'org.springframework.cloud:spring-cloud-starter-openfeign'

1.2 启动类增加注解@EnableFeignClients

  1. @SpringBootApplication
  2. @EnableFeignClients
  3. public class ChannelApplication {
  4. public static void main(String[] args) {
  5. SpringApplication.run(ChannelApplication.class, args);
  6. }
  7. }

1.3 编写feign客户端

  1. package com.springcloud.user_center.feignclients;
  2. import org.springframework.cloud.openfeign.FeignClient;
  3. import org.springframework.web.bind.annotation.GetMapping;
  4. import org.springframework.web.bind.annotation.PathVariable;
  5. /**
  6. * @Author:壹心科技BCF项目组 wangfan
  7. * @Date:Created in 2020/10/6 01:06
  8. * @Project:epec
  9. * @Description:订单中心收藏模块Feign客户端
  10. * @Modified By:wangfan
  11. * @Version: V1.0
  12. */
  13. /**
  14. * value: 调用服务名称
  15. * path: 统一的路径前缀
  16. */
  17. @FeignClient(value = "ORDER-CENTER", path = "/order")
  18. public interface OrderCenterCollectClientV3 {
  19. /**
  20. * 通过用户ID获取收藏的订单列表
  21. * @param userId
  22. * @return
  23. */
  24. @GetMapping("/user/collect/list/{userId}")
  25. public String getOrderList(@PathVariable Integer userId);
  26. }

1.4 controller

  1. import com.springcloud.user_center.feignclients.OrderCenterCollectClientV3;
  2. import org.springframework.beans.factory.annotation.Autowired;
  3. import org.springframework.web.bind.annotation.GetMapping;
  4. import org.springframework.web.bind.annotation.PathVariable;
  5. import org.springframework.web.bind.annotation.RestController;
  6. /**
  7. * @Author:壹心科技BCF项目组 wangfan
  8. * @Date:Created in 2020/10/5 13:17
  9. * @Project:epec
  10. * @Description:TODO
  11. * @Modified By:wangfan
  12. * @Version: V1.0
  13. */
  14. @RestController
  15. public class CollectController {
  16. @Autowired
  17. private OrderCenterCollectClientV3 orderCenterCollectClientV3;
  18. /**
  19. * 获取用户收藏的订单列表版本v3
  20. * @param userId
  21. * @return
  22. */
  23. @GetMapping("/collect/orders/v3/{userId}")
  24. public Object getCollectOrdersV3(@PathVariable("userId") Integer userId){
  25. String result = orderCenterCollectClientV3.getOrderList(userId);
  26. return result;
  27. }
  28. }

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客户端

  1. import org.springframework.cloud.openfeign.FeignClient;
  2. import org.springframework.web.bind.annotation.GetMapping;
  3. import org.springframework.web.bind.annotation.PathVariable;
  4. /**
  5. * @Author:壹心科技BCF项目组 wangfan
  6. * @Date:Created in 2020/10/6 01:06
  7. * @Project:epec
  8. * @Description:订单中心收藏模块Feign客户端,单独使用OpenFeign.不集成Eureka
  9. * @Modified By:wangfan
  10. * @Version: V1.0
  11. */
  12. /**
  13. * value: 调用服务名称,随便指定一个
  14. * url: 具体地址
  15. */
  16. @FeignClient(value = "MY-ORDER-CENTER", url = "http://localhost:10000/order")
  17. public interface OrderCenterCollectClientV4 {
  18. /**
  19. * 通过用户ID获取收藏的订单列表
  20. * @param userId
  21. * @return
  22. */
  23. @GetMapping("/user/collect/list/{userId}")
  24. public String getOrderList(@PathVariable Integer userId);
  25. }

controller中使用

  1. import com.springcloud.user_center.feignclients.OrderCenterCollectClientV4;
  2. import org.springframework.beans.factory.annotation.Autowired;
  3. import org.springframework.web.bind.annotation.GetMapping;
  4. import org.springframework.web.bind.annotation.PathVariable;
  5. import org.springframework.web.bind.annotation.RestController;
  6. /**
  7. * @Author:壹心科技BCF项目组 wangfan
  8. * @Date:Created in 2020/10/5 13:17
  9. * @Project:epec
  10. * @Description:TODO
  11. * @Modified By:wangfan
  12. * @Version: V1.0
  13. */
  14. @RestController
  15. public class CollectController {
  16. @Autowired
  17. private OrderCenterCollectClientV4 orderCenterCollectClientV4;
  18. /**
  19. * 获取用户收藏的订单列表版本v4
  20. * @param userId
  21. * @return
  22. */
  23. @GetMapping("/collect/orders/v4/{userId}")
  24. public Object getCollectOrdersV4(@PathVariable("userId") Integer userId){
  25. String result = orderCenterCollectClientV4.getOrderList(userId);
  26. return result;
  27. }
  28. }

验证
打开浏览器. 输入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. 现有一个业务需要查询用户收藏的订单列表. 因此需要从用户中心发起请求调用订单中心的服务.
在项目里真正的做法如下:

  1. 创建
  1. 编写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\”}]}”; }

}

  1. 未完待续.....
  2. <a name="jTNSQ"></a>
  3. ## 2.4 Feign注意事项
  4. 1. 带参请求
  5. 带参请求的话如果参数是在URL上, 必须加上`@RequestParam`注解
  6. ```java
  7. /**
  8. * 通过用户ID获取收藏的订单列表
  9. * @param userId
  10. */
  11. @GetMapping("/user/collect/list")
  12. public String getOrderList(@RequestParam("userId") Integer userId);
  1. Feign默认所有带参数的请求都是Post,想要使用指定的提交方式需引入依赖
    1. <!-- 取消Feign默认所有带参数的请求都是Post的限制 -->
    2. <dependency>
    3. <groupId>io.github.openfeign</groupId>
    4. <artifactId>feign-httpclient</artifactId>
    5. </dependency>
    1. //取消Feign默认所有带参数的请求都是Post的限制
    2. implementation 'io.github.openfeign:feign-httpclient'
    指明提交方式:
    1. /**
    2. * 通过用户ID获取收藏的订单列表
    3. * @param userId
    4. */
    5. @RequestMapping(value = "/user/collect/list", method = RequestMethod.POST)
    6. @GetMapping("/user/collect/list")
    7. public String getOrderList(@RequestParam("userId") Integer userId);

2.5 feign拦截器

实现RequestInterceptor接口即可

  1. import feign.RequestInterceptor;
  2. import feign.RequestTemplate;
  3. import org.springframework.stereotype.Component;
  4. /**
  5. * @Author:壹心科技BCF项目组 wangfan
  6. * @Date:Created in 2020/10/6 23:36
  7. * @Project:epec
  8. * @Description:Feign拦截器
  9. * @Modified By:wangfan
  10. * @Version: V1.0
  11. */
  12. @Component
  13. public class FeignRequestInteceptor implements RequestInterceptor {
  14. @Override
  15. public void apply(RequestTemplate template) {
  16. String feignTargetUrl = template.feignTarget().url();
  17. String path = template.path();
  18. System.out.println("feign拦截器"+FeignRequestInteceptor.class.getName()+" 打印, 请求url: " + feignTargetUrl+path);
  19. }
  20. }

配置文件增加配置

  1. #指定Feign拦截器
  2. 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)方法

  1. import org.springframework.context.annotation.Configuration;
  2. import org.springframework.security.config.annotation.web.builders.HttpSecurity;
  3. import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
  4. import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
  5. import org.springframework.security.config.http.SessionCreationPolicy;
  6. /**
  7. * @Author:壹心科技BCF项目组 wangfan
  8. * @Date:Created in 2020/10/7 00:03
  9. * @Project:epec
  10. * @Description:开始Feign权限
  11. * @Modified By:wangfan
  12. * @Version: V1.0
  13. */
  14. @Configuration
  15. @EnableWebSecurity
  16. public class SecurityConfigForFeign extends WebSecurityConfigurerAdapter {
  17. @Override
  18. protected void configure(HttpSecurity http) throws Exception {
  19. // 关闭csrf
  20. http.csrf().disable();
  21. // 表示所有的访问都必须认证,认证处理后才可以正常进行
  22. http.httpBasic().and().authorizeRequests().anyRequest().fullyAuthenticated();
  23. // 所有的rest服务一定要设置为无状态,以提升操作效率和性能
  24. http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
  25. }
  26. }

再使用Feign调用order_center接口则报错, 401未认证.

user_center(服务调用方)使用Feign调用order_center(服务提供方)接口时, 增加权限认证的方式有2种

  1. 自定义配置@Bean
  2. 使用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() {

    1. return new BasicAuthRequestInterceptor("wangfan", "123");

    }

}

  1. `@FeignClient` 加上`configuration`属性
  2. ```java
  3. @FeignClient(value = "ORDER-CENTER", path = "/order", configuration = FeignAuthConfiguration.class)

OK,可以正常访问了。

如果在配置类上添加了@Configuration注解,并且该类在@``ComponentScan所扫描的包中,那么该类中的配置信息就会被所有的@FeignClient共享。
最佳实践是:不指定@Configuration注解(或者指定configuration,用注解忽略),而用手动指定配置类:

  1. @FeignClient(name = "service-valuation",configuration = FeignAuthConfiguration.class)

2.6.4 user_center(服务调用方)使用Feign拦截器

  1. import feign.RequestInterceptor;
  2. import feign.RequestTemplate;
  3. import org.springframework.stereotype.Component;
  4. import java.util.Base64;
  5. /**
  6. * @Author:壹心科技BCF项目组 wangfan
  7. * @Date:Created in 2020/10/6 23:36
  8. * @Project:epec
  9. * @Description:Feign调用其他模块接口认证拦截器
  10. * @Modified By:wangfan
  11. * @Version: V1.0
  12. */
  13. @Component
  14. public class FeignAuthRequestInteceptor implements RequestInterceptor {
  15. @Override
  16. public void apply(RequestTemplate template) {
  17. template.header("Authorization", "Basic "+ Base64.getEncoder().encodeToString("wangfan:123".getBytes()));
  18. }
  19. }

配置文件指定拦截器位置

  1. #指定Feign调用其他模块接口认证拦截器
  2. feign.client.config.service-valuation.request-interceptors[1]=learn.wangfan.springcloud.user_center.config.FeignAuthRequestInteceptor

2.7 Feign超时和重试

Feign默认支持Ribbon, Ribbon的重试机制和Feign的重试机制有冲突,所以源码中默认关闭Feign的重试机制,使用Ribbon的重试机制

  1. #连接超时时间(ms)
  2. ribbon.ConnectTimeout=1000
  3. #业务逻辑超时时间(ms)
  4. ribbon.ReadTimeout=6000
  5. #同一台实例最大重试次数,不包括首次调用
  6. ribbon.MaxAutoRetries=1
  7. #重试负载均衡其他的实例最大重试次数,不包括首次调用
  8. ribbon.MaxAutoRetriesNextServer=1
  9. #是否所有操作都重试
  10. ribbon.OkToRetryOnAllOperations=false

使用ribbon重试机制,请求失败后,每个6秒会重新尝试