当前位置: 代码迷 >> 综合 >> Spring Cloud OpenFeign(一)
  详细解决方案

Spring Cloud OpenFeign(一)

热度:83   发布时间:2023-12-25 08:09:46.0

介绍

Spring Cloud OpenFeign 通过自动配置来绑定到 Spring 环境中,以此实现将 OpenFeign 集成到 SpringBoot 应用中。

Feign 本身是一个声明式的 webservice 客户端。它的目的是为了简化 webservice 的编写。通过创建一个接口,并且在接口上使用相应的注解,便可以直接使用 Feign

Feign 提供了可插拔的注解支持,包含了JAX-RS注解。

Feign 提供了可插拔的编码和解码功能。

Spring Cloud 在此基础上了,构建了 OpenFeign,增加了对 Spring MVC 注解的支持和在Spring Web中默认使用的相同HttpMessageConverters 的支持。使得 OpenFeign 可以支持 REST 风格的客户端。

当项目中使用 Spring Cloud OpenFeign时, Spring Cloud 会自动地集成 RibbonEureka ,来提供一个支持服务发现和具备负载均衡的 HTTP 客户端。

  • Eureka:当应用使用了 Eureka 作为服务发现组件时,OpenFeign 会首先前往 Eureka 的注册中心中查找对应的服务。
  • Ribbon:这是Spring Cloud Netflix 下的客户端的负载均衡

Spring Cloud OpenFeign 中,核心就是 FeignClientFeignClient 可以一个视为向远程服务的发起调用的 HTTP 客户端。


集成 OpenFeign

1)依赖

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

2)主类

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

3)FeignClient

@FeignClient("stores")
public interface StoreClient {
    @RequestMapping(method = RequestMethod.GET, value = "/stores")List<Store> getStores();@RequestMapping(method = RequestMethod.POST, value = "/stores/{storeId}",consumes = "application/json")Store update(@PathVariable("storeId") Long storeId, Store store);
}

示例中用在主类上的注解 @EnableFeignClients,表示开启 OpenFeign

示例中的 stores 就是当前 FeignClient 的名称,这是由 name 属性管理的。

OpenFeign 会创建基于 Ribbon 的具备负载均衡能力的 HTTP 客户端。该客户端会通过之前指定的名称,去查找服务对应的物理地址。如果应用中使用了 Eureka 作为服务发现组件,那么将会去 Eureka 的注册中心找到相匹配的服务。如果应用中未使用 Eureka,也可以通过单独配置服务路径信息,来实现服务的查找。

在示例中 StoreClient 客户端只是一个接口,不会有实现类。Spring Cloud 会针对使用了 @FeignClient 注解的接口,创建以该接口的全限定名称作为对应Bean 的名称。 这样就可以直接通过 @Autowired 来将 StoreClient 接口注入到其他类中。

Feign默认配置

feign:client:config:default:connectTimeout: 5000readTimeout: 5000loggerLevel: basichello-service:connectTimeout: 3000readTimeout: 3000loggerLevel: basic
  • default:作用域为所有的 @FeignClient
  • hello-service:指定的 FeignClient
  • connectTimeout:连接超时时间(毫秒)
  • readTimeout:业务执行超时时间(毫秒)
  • loggerLevel: 日志级别

Demo

1. Eureka-Server

1.1. 依赖

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

1.2. 主类

package com.mawen;import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;/*** @author mw118* @version 1.0* @date 2021/1/14 22:08*/
@SpringBootApplication
@EnableEurekaServer
public class EurekaServerApplication {
    public static void main(String[] args) {
    SpringApplication.run(EurekaServerApplication.class, args);}}

1.3. 配置

spring:application:name: eureka-serverserver:port: 8761eureka:client:fetch-registry: falseregister-with-eureka: false

2. Hello-Service

2.1. 依赖

<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId>
</dependency><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>

2.2. 主类

package com.mawen;import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;/*** @author mw118* @version 1.0* @date 2021/1/14 22:29*/@SpringBootApplication
@EnableDiscoveryClient
public class HelloServiceApplication {
    public static void main(String[] args) {
    SpringApplication.run(HelloServiceApplication.class, args);}
}

2.3. 配置

spring:application:name: hello-serviceserver:port: 21000eureka:client:serviceUrl:defaultZone: http://localhost:8761/eureka/

2.4. 业务

HelloService.java

package com.mawen.service;/*** @author mw118* @version 1.0* @date 2021/1/14 22:42*/public interface HelloService {
    String getHello();String timeout(Long timout);}

HelloServiceImpl.java

package com.mawen.service.impl;import com.mawen.service.HelloService;
import org.springframework.stereotype.Service;/*** @author mw118* @version 1.0* @date 2021/1/14 22:44*/
@Service
public class HelloServiceImpl implements HelloService {
    @Overridepublic String getHello() {
    return "Hello World!";}@Overridepublic String timeout(Long timout) {
    try {
    Thread.sleep(timout * 1000);} catch (InterruptedException e) {
    e.printStackTrace();}return "Hello World!";}}

HelloController.java

package com.mawen.controller;import com.mawen.service.HelloService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;/*** @author mw118* @version 1.0* @date 2021/1/14 22:30*/
@RestController
public class HelloController {
    @Autowiredprivate HelloService helloService;@GetMapping("/say/hello")public String sayHello() {
    return helloService.getHello();}@PostMapping("/timeout")public String timeout(@RequestParam("timeout") Long timout) {
    return helloService.timeout(timout);}
}

3. OpenFeign-Client

3.1. 依赖

<!-- openfeign 依赖 -->
<dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency><!-- eureka-client 依赖 -->
<dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId>
</dependency>

3.2. 主类

package com.mawen;import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.openfeign.EnableFeignClients;/*** @author mw118* @version 1.0* @date 2021/1/14 21:59*/@SpringBootApplication 
@EnableDiscoveryClient // 开启服务发现
@EnableFeignClients // 开启 openfeign
public class OpenFeignClientApplication {
    public static void main(String[] args) {
    SpringApplication.run(OpenFeignClientApplication.class, args);}}

3.3. 配置

spring:application:name: openfeign-clientserver:port: 20000# 配置 Eureka 注册中心
eureka:client:serviceUrl:defaultZone: http://localhost:8761/eureka/# 配置 openfeign
feign:client:config:default:  # 全局配置connectTimeout: 5000 # 连接超时时间5sreadTimeout: 5000 # 业务处理超时时间5sloggerLevel: basic # 日志级别hello-service:  # 指定 feign client名为hello-service 的配置connectTimeout: 3000 # 连接超时时间3sreadTimeout: 3000 # 业务处理超时时间3sloggerLevel: basic # 日志级别

3.4. 业务

HelloService.java

package com.mawen.service;import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;/*** 标识接口为FeignClient及对应的服务名称* @FeignClient: 表明该接口会被 SpringCloud 以当前类的全限定类名(com.mawen.service.HelloService)定义Bean,而放到 Spring 容器中* hello-service: 表示会去 Eureka 的注册中心,查找名为 hello-service 的服务而找到具体的服务信息,用于在后续请求中发起调用*/
@FeignClient("hello-service")
public interface HelloService {
    /*** 对 hello-service/say/hello 发起调用* @return*/@GetMapping("/say/hello")String sayHello();/*** 对 hello-service/timeout 发起调用* @param timeout* @return*/@PostMapping("/timeout")String timeout(@RequestParam("timeout") Long timeout);
}

OpenFeignController.java

package com.mawen.controller;import com.mawen.service.HelloService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;/*** @author mw118* @version 1.0* @date 2021/1/14 22:13*/
@RestController
public class OpenFeignController {
    /*** 注入 HelloService*/@Autowiredprivate HelloService helloService;@GetMapping("/hello")public String getHello() {
    return helloService.sayHello();}@GetMapping("/timeout")public String timeout(@RequestParam("timeout") Long timeout) {
    return helloService.timeout(timeout);}
}

4. 测试访问

依次启动 Eureka-ServerHello-ServiceOpenFeign-Client

然后访问 http://localhost:20000/hello,此时会返回 Hello World!。这就表明 OpenFeign 已经起效了。

接下来可以测试访问 http://localhost:20000/timeout?timeout=1http://localhost:20000/timeout?timeout=3。会发现第一次访问可以成功返回 Hello World!,而第二次接口访问返回 read time out 的错误。这是因为在配置文件中设置了 readTimeout 为3s。超过这个时候,就会报错,丢弃请求结果。

资源

代码

  • Github spring-cloud-openfeign
  • Github 官方示例 feign-eureka

参考

  • Spring Cloud OpenFeign 官方文档
  相关解决方案