【Spring Cloud学习】-4.Spring Cloud Feign 服务调用


1.简介

1.1 概述

Feign is a declarative web service client. It makes writing web service clients easier. To use Feign create an interface and annotate it. It has pluggable annotation support including Feign annotations and JAX-RS annotations. Feign also supports pluggable encoders and decoders. Spring Cloud adds support for Spring MVC annotations and for using the same HttpMessageConverters used by default in Spring Web. Spring Cloud integrates Ribbon and Eureka, as well as Spring Cloud LoadBalancer to provide a load-balanced http client when using Feign.

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

1.2 特点

在一般的场景中,如果要发送 http 请求,需要根据服务提供方的 ip、端口、url 进行调用,openfeign 提供了一种基于接口的调用方式。

  • 原始的调用方法: client.request(“http://ip:port/service");
  • openfeign 调用方法:service.request(args); openfeign 根据服务名进行调用,调用方配置服务提供方的服务名 spring.application.name ;

使用 openfeign 调用远程服务就像 java 代码中接口调用方法一样,不用再编写复杂的 http 请求逻辑;如果集成了 eureka 等注册中心,甚至不用配置服务提供方的 url,只需配置 eureka 即可。

2.演示环境

  1. JDK 1.8.0_201
  2. Spring Boot 2.2.0.RELEASE、Spring Cloud Hoxton.RELEASE
  3. 构建工具(apache maven 3.6.3)
  4. 开发工具(IntelliJ IDEA )

3.演示代码

Feign+Eureka+Hystrix 的混合运用

总体结构说明:

  • ofc-feign-eureka-server: eureka 服务端,提供服务注册功能;
  • ofc-feign-user-api: 公共 api,定义模型和接口,fallback;
  • ofc-feign-user-client: 服务调用方,使用 feign 调用接口,注册到 eureka server;
  • ofc-feign-user-server: 服务提供方,实现 api 中定义的接口,注册到 eureka server。

3.1 ofc-feign-eureka-server

3.1.1 代码说明

eureka 服务端,提供服务注册功能。

3.1.2 maven 依赖

pom.xml

<dependencies>
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
    </dependency>
</dependencies>

3.1.3 配置文件

application.properties

spring.application.name=ofc-feign-eureka-server
# 应用服务web访问端口
server.port=11040

# 服务注册中心主机名
eureka.instance.hostname=localhost
# 是否注册自己
eureka.client.register-with-eureka=false
# 是否检索服务
eureka.client.fetch-registry=false
# eureka server 地址
eureka.client.service-url.defaultZone=http://${eureka.instance.hostname}:${server.port}/eureka/

3.1.4 java代码

OfcFeignEurekaApplication.java

// 开启 eureka server
@EnableEurekaServer
@SpringBootApplication
public class OfcFeignEurekaApplication {

    public static void main(String[] args) {
        SpringApplication.run(OfcFeignEurekaApplication.class, args);
    }
}

3.2 ofc-feign-user-api

3.2.1 代码说明

公共 api,定义了实体模型,公共接口以及 fallback 类。

3.2.2 maven 依赖

pom.xml

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

3.2.3 配置文件

application.properties

spring.application.name=ofc-feign-user-api
# 应用服务web访问端口
server.port=8080

3.2.4 java代码

UserModel.java

public class UserModel {

    private Long id;
    private String name;
    private Integer age;
    private String birthday;
    private String address;
    private String phone;

    public UserModel() {}

    public UserModel(Long id, String name, Integer age, String birthday, String address, String phone) {
        this.id = id;
        this.name = name;
        this.age = age;
        this.birthday = birthday;
        this.address = address;
        this.phone = phone;
    }

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    public String getBirthday() {
        return birthday;
    }

    public void setBirthday(String birthday) {
        this.birthday = birthday;
    }

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }

    public String getPhone() {
        return phone;
    }

    public void setPhone(String phone) {
        this.phone = phone;
    }

    @Override
    public String toString() {
        return "UserModel{" + "id=" + id + ", name='" + name + '\'' + ", age=" + age + ", birthday='" + birthday + '\''
                + ", address='" + address + '\'' + ", phone='" + phone + '\'' + '}';
    }
}

UserService.java

// value 中定义服务提供者名称,fallback-降级
@FeignClient(value = "ofc-feign-user-server", fallback = UserServiceFallback.class)
public interface UserService {

    @GetMapping(value = "/user/list")
    List<UserModel> list();

    @PostMapping(value = "/user/save")
    UserModel save(@RequestBody UserModel userModel);
}

UserServiceFallback.java

public class UserServiceFallback implements UserService {
    @Override
    public List<UserModel> list() {
        return Collections.emptyList();
    }

    @Override
    public UserModel save(UserModel userModel) {
        return new UserModel();
    }
}

OfcUserApiApplication.java

@SpringBootApplication
public class OfcUserApiApplication {

    public static void main(String[] args) {
        SpringApplication.run(OfcUserApiApplication.class, args);
    }
}

3.3 ofc-feign-user-server

3.3.1 代码说明

实现 api 中定义的接口,对外提供服务,注册到 eureka server。

集成 hystrix,实现服务降级。

3.3.2 maven 依赖

pom.xml

<dependencies>
    <dependency>
        <groupId>com.soulballad.usage</groupId>
        <artifactId>ofc-feign-user-api</artifactId>
        <version>${project.version}</version>
    </dependency>
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
    </dependency>
</dependencies>

3.3.3 配置文件

application.properties

spring.application.name=ofc-feign-user-server
# 应用服务web访问端口
server.port=11041

eureka.server.host=localhost
eureka.server.port=11040
eureka.client.service-url.defaultZone=http://${eureka.server.host}:${eureka.server.port}/eureka/

3.3.4 java代码

UserServerController.java

@RestController
public class UserServerController {

    private static final Map<Long, UserModel> USER_MAP = new HashMap<>();
    private static final AtomicLong ID_GENERATOR = new AtomicLong(2);
    private final Random random = new Random();
    private static final Logger LOGGER = LoggerFactory.getLogger(UserServerController.class);

    @GetMapping(value = "/user/list")
    @HystrixCommand(fallbackMethod = "fallBackList", commandProperties = {
            @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "100")
    })
    public List<UserModel> list() throws InterruptedException {
        int seconds = random.nextInt(200);
        LOGGER.info("user server controller list sleep for {} seconds!", seconds);
        Thread.sleep(seconds);
        return new ArrayList<>(USER_MAP.values());
    }

    @PostMapping(value = "/user/save")
    public UserModel save(@RequestBody UserModel userModel) {
        long id = ID_GENERATOR.incrementAndGet();
        userModel.setId(id);
        USER_MAP.put(id, userModel);
        return userModel;
    }

    public List<UserModel> fallBackList() {
        LOGGER.warn("user server controller list fallback!");
        return Collections.emptyList();
    }

    // 初始化2条数据
    @PostConstruct
    public void init() {
        UserModel user1 = new UserModel(1L, "zhangsan", 20, "2000-01-01", "shenzhen", "13888888888");
        UserModel user2 = new UserModel(2L, "lisi", 21, "1999-01-01", "shanghai", "13777777777");
        USER_MAP.put(user1.getId(), user1);
        USER_MAP.put(user2.getId(), user2);
    }
}

OfcFeignUserServerApplication.java

@EnableHystrix
@EnableEurekaClient
@SpringBootApplication
public class OfcFeignUserServerApplication {

    public static void main(String[] args) {
        SpringApplication.run(OfcFeignUserServerApplication.class, args);
    }
}

3.4 ofc-feign-user-client

3.4.1 代码说明

根据 api 中的 UserService 接口调用服务,同时也需要注册到 eureka server。

3.4.2 maven 依赖

pom.xml

<dependencies>
    <dependency>
        <groupId>com.soulballad.usage</groupId>
        <artifactId>ofc-feign-user-api</artifactId>
        <version>${project.version}</version>
    </dependency>
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
    </dependency>
</dependencies>

3.2.3 配置文件

application.properties

spring.application.name=ofc-feign-user-client
# 应用服务web访问端口
server.port=11042

eureka.server.host=localhost
eureka.server.port=11040
eureka.client.service-url.defaultZone=http://${eureka.server.host}:${eureka.server.port}/eureka/

# 设置超时时间和日志级别
feign.client.config.default.connect-timeout=5000
feign.client.config.default.read-timeout=5000
feign.client.config.default.logger-level=full

logging.level.com.soulballad.usage.springcloud=debug

3.2.4 java代码

UserClientController.java

@RestController
public class UserClientController implements UserService {

    private final UserService userService;

    @Autowired
    public UserClientController(UserService userService) {
        this.userService = userService;
    }

    @Override
    public List<UserModel> list() {
        return userService.list();
    }

    @Override
    public UserModel save(@RequestBody UserModel userModel) {
        return userService.save(userModel);
    }
}

UserService.java

// 启用feign,接口为UserService;如果不配置默认扫描所有@FeignClient注解修改的类
@EnableEurekaClient
@SpringBootApplication
@EnableFeignClients(clients = UserService.class)
public class OfcFeignUserClientApplication {

    public static void main(String[] args) {
        SpringApplication.run(OfcFeignUserClientApplication.class, args);
    }
}

3.5 git 地址

spring-cloud-ofc-04-feign: Spring Cloud 官方提供的分布式服务调用方案

4.效果展示

启动 SpringBoot03WebApplication.main 方法,在 spring-boot-03-webmvc.http 访问下列地址,观察输出信息是否符合预期。

依次启动 ofc-feign-eureka-server、ofc-feign-user-server、ofc-feign-user-client 服务;

它们分别监听在 11040、11041、11042 端口,启动完成后可在 eureka 管理台上看到:

### GET eureka
GET http://localhost:11040/

spring-cloud-ofc-feign.http 中访问如下地址,查看请求结果是否符合预期

4.1 ofc-feign-user-server

查询用户列表

### GET /user/list
GET http://localhost:11041/user/list

新增用户

### POST /user/save
POST http://localhost:11041/user/save
Accept: application/json
Content-Type: application/json

{
  "name": "wangwu",
  "age": 30,
  "birthday": "1980-03-01",
  "address": "guangzhou",
  "phone": "13666666666"
}

4.2 ofc-feign-user-client

查询用户列表

### GET /user/list
GET http://localhost:11042/user/list

新增用户

### POST /user/save
POST http://localhost:11042/user/save
Accept: application/json
Content-Type: application/json

{
  "name": "zhaoliu",
  "age": 40,
  "birthday": "1970-04-02",
  "address": "wuhan",
  "phone": "13555555555"
}

5.参考

  1. 官方文档-Spring Cloud OpenFeign

文章作者: Soulballad
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 Soulballad !
评论
 上一篇
【源码分析-Spring-Cloud】-4.Spring Cloud Feign 实现原理 【源码分析-Spring-Cloud】-4.Spring Cloud Feign 实现原理
Spring Cloud Feign:【Spring Cloud学习】-4.Spring Cloud Feign 服务调用 1.Feign 是如何调用服务的?当调用 /user/list 时,可以看到这时的 UserService 是一
2020-08-21
下一篇 
【源码分析-Spring-Cloud】-3.Spring Cloud Hystrix 实现原理 【源码分析-Spring-Cloud】-3.Spring Cloud Hystrix 实现原理
Spring Cloud Hystrix:【Spring Cloud学习】-3.Spring Cloud Hystrix 服务降级 1.Hystrix 如何触发熔断?hystrix 熔断的注解 @HystrixCommand,是通过 H
2020-08-20
  目录