SpringCloud入门笔记04:Feign-声明式Rest客户端

889 阅读3分钟

本文所构建的代码已上传至Github(注意切换分支) ,所有代码均亲测有效,祝食用愉快。

一、Why Feign

1.1 直接使用RestTemplate的缺点

  1. 手动定义接口的调用路径
  2. 执行结果未知

二、What is Feign

openFeign是针对消费端的 官网对于Spring Cloud OpenFeign的说明:

Declarative REST Client: Feign creates a dynamic implementation of an interface decorated with JAX-RS or Spring MVC annotations.
翻译:作为声明式的REST客户端,针对被JAX-RS和Spring MVC注解修饰的接口,Feign提供了一种动态的实现。

Feign译为:假装、伪装。就是将服务名伪装为接口名对外提供服务。
此外,OpenFeign 中使用 Ribbon 进行负载均衡,所以 OpenFeign 直接内置了 Ribbon。即在导入 OpenFeign 依赖后,无需再专门导入 Ribbon 依赖了。

三、How to use Feign

2.1 新建项目(consumer-feign-demo-9020)

2.2 添加依赖

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>

    <!-- Eureka客户端 -->
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
    </dependency>
    <!--  OpenFeign  -->
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-openfeign</artifactId>
    </dependency>
</dependencies>

2.3 添加配置

spring.application.name=consumer-feign-demo
server.port=9020
# EurekaServer地址
eureka.client.service-url.defaultZone=http://127.0.0.1:7001/eureka
# 指定自己的ip信息,不指定的话会自己寻找
eureka.instance.prefer-ip-address=true
eureka.instance.ip-address=127.0.0.1

2.4 编写调用服务端接口的声明式接口

@FeignClient("user-service")
@RequestMapping("/sysUser")
public interface FeignSysUserService {
    @RequestMapping("/selectById/{id}")
    SysUser selectById(@PathVariable Integer id);
}

2.5 Controller层调用FeignService

@RestController
@RequestMapping("consumer/sysUser")
public class SysUserController {

    @Resource
    private FeignSysUserService feignSysUserService;

    @RequestMapping("selectById/{id}")
    public SysUser selectById(@PathVariable Integer id){
        return feignSysUserService.selectById(id);
    }
}

2.6 启动类上开启Feign

@SpringBootApplication
@EnableFeignClients
public class OpenFeignApplication {
    public static void main(String[] args) {
        SpringApplication.run(OpenFeignApplication.class,args);
    }
}

2.7 效果

启动eureka-serveruser-serviceconsumer-feign-demo-9020后,请求接口地址http://localhost:9020/consumer/sysUser/selectById/1可以看到,与使用RestTemplate直接调用无异。

// 20191231213541
// http://localhost:9020/consumer/sysUser/selectById/1

{
  "id": 1,
  "username": "haha",
  "password": "111111",
  "name": "zhangsan",
  "age": 21,
  "gender": 1,
  "remarks": "11223344"
}

四、关于Feign接口调用时的传参问题

个人感觉Spring Cloud Feign在传参的时候严格遵守Restful风格,在执行get请求时,仅从url路径中提取参数,如果我们平时开发时,未遵守Restful风格,在提交get请求时,其携带的参数将会被丢失掉:

4.1 使用Get提交多个条件的查询(使用/xx/xx?x1=xx&...&...)

4.1.1 consumer-feign-demo-9020

// SysUserController
@RequestMapping("selectById4Get")
public SysUser selectById4Get(Integer id){
    return feignSysUserService.selectById4Get(id);
}
// FeignSysUserService
@RequestMapping("/selectById4Get")
SysUser selectById4Get(Integer id);

4.1.2 user-service

// SysUserController
@RequestMapping("selectById4Get")
public SysUser selectById4Get(Integer id){
    System.err.println(request.getRequestURL());
    return sysUserService.selectById(id);
}

4.1.3 执行请求,将会看到user-service接收不到参数

请求地址:http://localhost:9020/consumer/sysUser/save?id=1

4.2 解决方案:使用Apache HttpClient替换Feign原生httpclient

4.2.1 新增配置

feign.httpclient.enabled=true

4.2.2 新增依赖

<!--使用Apache HttpClient替换Feign原生httpclient-->
<dependency>
    <groupId>org.apache.httpcomponents</groupId>
    <artifactId>httpclient</artifactId>
</dependency>
<dependency>
    <groupId>io.github.openfeign</groupId>
    <artifactId>feign-httpclient</artifactId>
</dependency>

4.2.3 FeignService与服务提供方(user-service)均采用json方式通信

  • consumer-feign-demo-9020
    // SysUserController
    // 无需json方式
    @RequestMapping("query4Get")
    public SysUser query4Get(SysUser sysUser){
        return feignSysUserService.query4Get(sysUser);
    }
    
    //FeignSysUserService
    @RequestMapping("/query4Get")
    SysUser query4Get(@RequestBody SysUser sysUser);
    
  • user-service
    // SysUserController
    @RequestMapping("query4Get")
    public SysUser query4Get(@RequestBody SysUser sysUser){
        return sysUserService.query4Get(sysUser);
    }
    
    // SysUserService
    public SysUser query4Get(SysUser sysUser){
        return this.sysUserMapper.select(sysUser).get(0);
    }
    
  • 效果:
    // http://localhost:9020/consumer/sysUser/query4Get?id=2
    {
    "id": 2,
    "username": "heihei",
    "password": "222222",
    "name": "lisi20",
    "age": 20,
    "gender": 2,
    "remarks": null
    }
    

4.3 其他解决方案:

  • GET方式改POST方式提交(手动滑稽)
  • 开发时遵守Restful风格规范