简介

之前学习了Ribbon,Ribbon结合RestTemplate可以实现服务远端调用和负载均衡的效果。而Feign也可以用来做远端调用以及负载均衡,那么一山不容二虎,为什么有了Ribbon又要出来Feign呢?

官网的解释如下:
Feign 是一个声明式 Web 服务客户端。它使编写 Web 服务客户端变得更容易。要使用 Feign 创建一个接口并对其进行注释。它具有可插入的注释支持,包括 Feign 注释和 JAX-RS 注释。 Feign 还支持可插拔的编码器和解码器。 Spring Cloud 添加了对 Spring MVC 注释的支持,并支持使用 Spring Web 中默认使用的相同 HttpMessageConverters。 Spring Cloud 集成了 Eureka、Spring Cloud CircuitBreaker 和 Spring Cloud LoadBalancer,在使用 Feign 时提供负载均衡的 http 客户端。

说人话就是:Feign是一个声明式的Web服务(Service层)客户端,使得编写Web服务客户端变得非常容易,只需要创建一个接口,然后在上面添加注解即可。

之前使用 Ribbon,使得我们可以面向微服务编程,在restTemplate的路径上,可以通过微服务名进行访问。但是现在我们更习惯于面向接口编程,接口+注解的方式,即可更加优雅的调用我们的微服务,而Feign可以做到。

使用

Feign的搭建

新建一个模块,专门用来做 Feign 的实现。

pom.xml 如下

<?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>springcloudservice</artifactId>
        <groupId>org.example</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>springcloudservice-feign</artifactId>

    <dependencies>
        <!-- 引入自定义的 api -->
        <dependency>
            <groupId>org.example</groupId>
            <artifactId>springcloudservice-api</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>

        <!-- 引入 feign 依赖,feign 集成了 ribbon -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-feign</artifactId>
        </dependency>
    </dependencies>


</project>

接下来创建一个接口。这里要注意:

  • 用Feign来实现微服务之间接口的调用,就是直接将被调用的微服务中的方法和参数直接复制过来。
  • 方法上的mapping映射路径要写完整,比如controller上也加了RequestMapping,则复制过来的路径要在方法的路径上加上controller上的路径。
  • 如果方法上的mapping映射路径中有pathVariable参数,则必须用@PathVariable指明,不可省略value属性,不然可能会有问题。

现在我要实现,feign去调用服务提供者。那么我就将服务提供者的方法全部复制到接口中。并且我们要在接口上标注 @FeignClient 注解,并指定value属性,也即微服务名,大写小写都可以,找到对应的微服务。

package com.zxb.springcloud.service;


import com.zxb.springcloud.bean.User;
import org.springframework.cloud.netflix.feign.FeignClient;
import org.springframework.web.bind.annotation.*;

import java.util.List;

// 微服务名字
@FeignClient(value = "SPRINGCLOUDSERVICE-USER")
public interface UserClientService {

    @PostMapping(path = "/user/add")
    public Boolean addUser(@RequestBody User user);

    @GetMapping(path = "/user/del/{id}")
    public Integer deleteUser(@PathVariable(value = "id") Integer id);

    @PutMapping(path = "/user/update")
    public void updateUser(@RequestBody User user);

    @GetMapping(path = "/user/get/{id}")
    public User getUserById(@PathVariable(value = "id") Integer id);

    @GetMapping(path = "/user/get")
    public List<User> getUsers();
}

feign就配置好了。随后将此模块,maven clean,maven install ,供其它模块使用。如果出现install失败,则先install父工程,再去install此模块即可。

消费者的搭建

之前使用Ribbon+RestTemplate搭建消费者。而现在,使用声明式的 Feign 来进行搭建。

创建一个新的模块。
pom.xml 如下:

<?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>springcloudservice</artifactId>
        <groupId>org.example</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>springcloudservice-consumer-user-feign-80</artifactId>
    <dependencies>
        <dependency>
            <groupId>org.example</groupId>
            <artifactId>springcloudservice-api</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
        <dependency>
            <groupId>org.example</groupId>
            <artifactId>springcloudservice-feign</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-eureka</artifactId>
        </dependency>
    </dependencies>

</project>

application.properties 配置文件如下:

server.port=80

eureka.client.register-with-eureka=false
eureka.client.service-url.defaultZone=http://eureka7001.com:7001/eureka,http://eureka7002.com:7002/eureka,http://eureka7003.com:7003/eureka

此时我们再去写 controller

package com.zxb.springcloud.controller;


import com.zxb.springcloud.bean.User;
import com.zxb.springcloud.service.UserClientService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.client.RestTemplate;

import java.util.List;

@RestController
public class UserController_Consumer {
//    Ribbon 的方式
//    private static final String REST_URL = "http://SPRINGCLOUDSERVICE-USER";
//
//    @Autowired
//    private RestTemplate restTemplate;

//  Feign 的方式
    @Autowired
    private UserClientService userClientService;


    @GetMapping(path = "/feign/user/get/{id}")
    public User getById(@PathVariable(value = "id") Integer id) {
        return userClientService.getUserById(id);
    }

    @PostMapping(path = "/feign/user/add")
    public boolean addUser(User user) {
        return userClientService.addUser(user);
    }

    @GetMapping(path = "/feign/user/get")
    public List<User> getAll() {
        return userClientService.getUsers();
    }

    @DeleteMapping(path = "/feign/user/del/{id}")
    public Integer deleteUserById(@PathVariable(value = "id") Integer id) {
        return userClientService.deleteUser(id);
    }
}

可以看到,Feign的方式更加符合我们现在的编程方式,controller去调用service。在消费者的controller中,其实就是去调用了feign中的方法,由feign根据注解上的微服务名称去实现远端调用对应的provider的方法,然后找到对应的映射地址,进行调用。

测试

调用消费者的接口,http://localhost/feign/user/get/ ,进行测试

feign 负载均衡策略 feign负载机制_feign 负载均衡策略


通过 Feign,也是成功的实现了微服务直接接口的调用,并且 Feign 集成了 Ribbon,也自带负载均衡功能,多次访问默认采用轮询算法。