Spring Cloud OpenFeign 4.1.1

声明式 REST 客户端: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

要将 Feign 包含在您的项目中,请使用带有组 org.springframework.cloud 和工件 ID spring-cloud-starter-openfeign 的启动器。有关使用当前 Spring Cloud 发布系列设置构建系统的详细信息,请参阅 Spring Cloud 项目页面。

示例 Spring Boot 应用程序

@SpringBootApplication
@EnableFeignClients
public class Application {

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

}

StoreClient.java

@FeignClient("stores")
public interface StoreClient {
	@RequestMapping(method = RequestMethod.GET, value = "/stores")
	List<Store> getStores();

	@GetMapping("/stores")
	Page<Store> getStores(Pageable pageable);

	@PostMapping(value = "/stores/{storeId}", consumes = "application/json")
	Store update(@PathVariable("storeId") Long storeId, Store store);

	@DeleteMapping("/stores/{storeId:\\d+}")
	void delete(@PathVariable Long storeId);
}

在 @FeignClient 注释中,字符串值(上面的“stores”)是任意客户端名称,用于创建 Spring Cloud LoadBalancer 客户端。您还可以使用 url 属性(绝对值或只是主机名)指定 URL。应用程序上下文中 bean 的名称是接口的完全限定名称。要指定您自己的别名值,您可以使用 @FeignClient 注释的 qualifiers 值。

上面的负载平衡器客户端将希望发现“stores”服务的物理地址。如果您的应用程序是 Eureka 客户端,那么它将解析 Eureka 服务注册表中的服务。如果您不想使用 Eureka,您可以使用 SimpleDiscoveryClient 在外部配置中配置服务器列表。

Spring Cloud OpenFeign 支持 Spring Cloud LoadBalancer 阻塞模式的所有可用功能。

属性解析方式

在创建 Feign 客户端 bean 时,我们解析通过 @FeignClient 注释传递的值。从 4.x 开始,这些值正在被急切地解析。对于大多数用例来说,这是一个很好的解决方案,并且它还允许 AOT 支持。

如果您需要延迟解析属性,请将 spring.cloud.openfeign.lazy-attributes-resolution 属性值设置为 true 。

覆盖 Feign 默认值

Spring Cloud Feign 支持的一个核心概念是指定客户端。每个假客户端都是组件集合的一部分,这些组件协同工作以按需联系远程服务器,并且该集合有一个名称,您作为应用程序开发人员使用 @FeignClient 注释为其指定名称。 Spring Cloud 使用 FeignClientsConfiguration 按需为每个指定客户端创建一个新的集合作为 ApplicationContext 。其中包含(除其他外)一个 feign.Decoder 、一个 feign.Encoder 和一个 feign.Contract 。可以使用 @FeignClient 注释的 contextId 属性覆盖该集合的名称。

Spring Cloud 允许您通过使用 @FeignClient 声明附加配置(在 FeignClientsConfiguration 之上)来完全控制 feign 客户端。例子:

@FeignClient(name = "stores", configuration = FooConfiguration.class)
public interface StoreClient {
	//..
}

在这种情况下,客户端由 FeignClientsConfiguration 中已有的组件以及 FooConfiguration 中的任何组件组成(后者将覆盖前者)。

@FeignClient(value = "cloud-payment-service")
public interface PayFeignApi
{
    /**
     * 新增一条支付相关流水记录
     * @param payDTO
     * @return
     */
    @PostMapping("/pay/add")
    public ResultData addPay(@RequestBody PayDTO payDTO);

    /**
     * 按照主键记录查询支付流水信息
     * @param id
     * @return
     */
    @GetMapping("/pay/get/{id}")
    public ResultData getPayInfo(@PathVariable("id") Integer id);

    /**
     * openfeign天然支持负载均衡演示
     * @return
     */
    @GetMapping(value = "/pay/get/info")
    public String mylb();
}

name 和 url 属性支持占位符。以前,使用 url 属性不需要 name 属性。现在需要使用 name 。

@FeignClient(name = "${feign.name}", url = "${feign.url}")
public interface StoreClient {
	//..
}

Spring Cloud OpenFeign默认不为feign提供以下bean,但仍然从应用程序上下文中查找这些类型的bean来创建feign客户端:

  • Logger.Level

  • Retryer

  • ErrorDecoder

  • Request.Options

  • Collection<RequestInterceptor>

  • SetterFactory

  • QueryMapEncoder

  • Capability (MicrometerObservationCapability and CachingCapability are provided by default)
    Capability (默认提供 MicrometerObservationCapability 和 CachingCapability )

默认情况下会创建类型为 Retryer 的 Retryer.NEVER_RETRY bean,这将禁用重试。请注意,此重试行为与 Feign 默认行为不同,Feign 会自动重试 IOException,将它们视为与网络相关的瞬态异常,以及从 ErrorDecoder 抛出的任何 RetryableException。

开启

package com.atguigu.cloud.config;

import feign.Retryer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * @auther zzyy
 * @create 2023-11-10 11:09
 */
@Configuration
public class FeignConfig
{
    @Bean
    public Retryer myRetryer()
    {
        //return Retryer.NEVER_RETRY; //Feign默认配置是不走重试策略的

        //最大请求次数为3(1+2),初始间隔时间为100ms,重试间最大间隔时间为1s
        return new Retryer.Default(100,1,3);
    }
}

创建这些类型之一的 bean 并将其放置在 @FeignClient 配置中(例如上面的 FooConfiguration )允许您覆盖所描述的每一个 bean。例子:

@Configuration
public class FooConfiguration {
	@Bean
	public Contract feignContract() {
		return new feign.Contract.Default();
	}

	@Bean
	public BasicAuthRequestInterceptor basicAuthRequestInterceptor() {
		return new BasicAuthRequestInterceptor("user", "password");
	}
}

超时处理

我们可以在默认客户端和指定客户端上配置超时。 OpenFeign 使用两个超时参数:

connectTimeout 防止由于服务器处理时间过长而阻塞调用者。

readTimeout 从连接建立时开始应用,当返回响应时间过长时触发。

单独某个服务

全局配置

手动创建客户端

在某些情况下,可能需要以使用上述方法无法实现的方式自定义您的 Feign 客户端。在这种情况下,您可以使用 Feign Builder API 创建客户端。下面是一个示例,它创建两个具有相同接口的 Feign 客户端,但为每个客户端配置一个单独的请求拦截器。

@Import(FeignClientsConfiguration.class)
class FooController {

	private FooClient fooClient;

	private FooClient adminClient;

	@Autowired
	public FooController(Client client, Encoder encoder, Decoder decoder, Contract contract, MicrometerObservationCapability micrometerObservationCapability) {
		this.fooClient = Feign.builder().client(client)
				.encoder(encoder)
				.decoder(decoder)
				.contract(contract)
				.addCapability(micrometerObservationCapability)
				.requestInterceptor(new BasicAuthRequestInterceptor("user", "user"))
				.target(FooClient.class, "https://PROD-SVC");

		this.adminClient = Feign.builder().client(client)
				.encoder(encoder)
				.decoder(decoder)
				.contract(contract)
				.addCapability(micrometerObservationCapability)
				.requestInterceptor(new BasicAuthRequestInterceptor("admin", "admin"))
				.target(FooClient.class, "https://PROD-SVC");
	}
}

PROD-SVC 是客户端将向其发出请求的服务的名称。

Feign Spring Cloud 断路器支持

如果 Spring Cloud CircuitBreaker 位于类路径中并且 spring.cloud.openfeign.circuitbreaker.enabled=true ,Feign 会用断路器包装所有方法。

要在每个客户端上禁用 Spring Cloud CircuitBreaker 支持,请创建一个具有“原型”范围的普通 Feign.Builder ,例如:

@Configuration
public class FooConfiguration {
	@Bean
	@Scope("prototype")
	public Feign.Builder feignBuilder() {
		return Feign.builder();
	}
}

断路器名称遵循此模式 <feignClientClassName>#<calledMethod>(<parameterTypes>) 。当使用 FooClient 接口调用 @FeignClient 且被调用的无参数接口方法为 bar 时,断路器名称将为 FooClient#bar() 。

提供 CircuitBreakerNameResolver 的 bean,您可以更改断路器名称模式。

@Configuration
public class FooConfiguration {
	@Bean
	public CircuitBreakerNameResolver circuitBreakerNameResolver() {
		return (String feignClientName, Target<?> target, Method method) -> feignClientName + "_" + method.getName();
	}
}

使用配置属性配置断路器

您可以通过配置属性配置断路器。


例如,如果您有这个 Feign 客户端

@FeignClient(url = "http://localhost:8080")
public interface DemoClient {

    @GetMapping("demo")
    String getDemo();
}

您可以通过执行以下操作使用配置属性来配置它

spring:
  cloud:
    openfeign:
      circuitbreaker:
        enabled: true
        alphanumeric-ids:
          enabled: true
resilience4j:
  circuitbreaker:
    instances:
      DemoClientgetDemo:
        minimumNumberOfCalls: 69
  timelimiter:
    instances:
      DemoClientgetDemo:
        timeoutDuration: 10s

Feign Spring Cloud 断路器回退

Spring Cloud CircuitBreaker 支持回退的概念:当回路打开或出现错误时执行的默认代码路径。要为给定的 @FeignClient 启用回退,请将 fallback 属性设置为实现回退的类名称。您还需要将您的实现声明为 Spring bean。

@FeignClient(name = "test", url = "http://localhost:${server.port}/", fallback = Fallback.class)
protected interface TestClient {

	@GetMapping("/hello")
	Hello getHello();

	@GetMapping("/hellonotfound")
	String getException();

}

@Component
static class Fallback implements TestClient {

	@Override
	public Hello getHello() {
		throw new NoFallbackAvailableException("Boom!", new RuntimeException());
	}

	@Override
	public String getException() {
		return "Fixed response";
	}

}

Feign and @Primary 

当使用 Feign 和 Spring Cloud CircuitBreaker 回退时, ApplicationContext 中有多个相同类型的 bean。这将导致 @Autowired 无法工作,因为没有一个 Bean 或标记为主要的 Bean。为了解决这个问题,Spring Cloud OpenFeign 将所有 Feign 实例标记为 @Primary ,因此 Spring Framework 将知道要注入哪个 bean。在某些情况下,这可能是不可取的。要关闭此行为,请将 @FeignClient 的 primary 属性设置为 false。

@FeignClient(name = "hello", primary = false)
public interface HelloClient {
	// methods here
}

Feign 继承支持

Feign 通过单继承接口支持样板 API。这允许将常见操作分组到方便的基本接口中。

UserService.java

public interface UserService {

	@GetMapping("/users/{id}")
	User getUser(@PathVariable("id") long id);
}

UserResource.java 

@RestController
public class UserResource implements UserService {

}

UserClient.java

@FeignClient("users")
public interface UserClient extends UserService {

}

Feign 请求/响应压缩

@FeignClient 接口不应在服务器和客户端之间共享,并且不再支持在类级别使用 @RequestMapping 注释 @FeignClient 接口。

可以考虑为您的 Feign 请求启用请求或响应 GZIP 压缩。您可以通过启用以下属性之一来做到这一点:

spring.cloud.openfeign.compression.request.enabled=true
spring.cloud.openfeign.compression.response.enabled=true

Feign 请求压缩为您提供的设置类似于您为 Web 服务器设置的设置:

spring.cloud.openfeign.compression.request.enabled=true
spring.cloud.openfeign.compression.request.mime-types=text/xml,application/xml,application/json
spring.cloud.openfeign.compression.request.min-request-size=2048

这些属性允许您选择压缩媒体类型和最小请求阈值长度。

Feign logging 日志记录

为每个创建的 Feign 客户端创建一个记录器。默认情况下,记录器的名称是用于创建 Feign 客户端的接口的完整类名。 Feign 日志记录仅响应 DEBUG 级别。

logging.level.project.user.UserClient: DEBUG

您可以为每个客户端配置的 Logger.Level 对象告诉 Feign 要记录多少内容。选项有:

  • NONE ,不记录(默认)。

  • BASIC ,仅记录请求方法和URL以及响应状态码和执行时间。

  • HEADERS ,记录基本信息以及请求和响应标头。

  • FULL ,记录请求和响应的标头、正文和元数据。

例如,以下将 Logger.Level 设置为 FULL :

@Configuration
public class FooConfiguration {
	@Bean
	Logger.Level feignLoggerLevel() {
		return Logger.Level.FULL;
	}
}

Feign 能力支持

Feign 能力暴露核心 Feign 组件,以便可以修改这些组件。例如,这些功能可以获取 Client ,对其进行修饰,然后将修饰后的实例返回给 Feign。对 Micrometer 的支持就是一个很好的现实例子。

创建一个或多个 Capability bean 并将它们放入 @FeignClient 配置中可以让您注册它们并修改相关客户端的行为。

@Configuration
public class FooConfiguration {
	@Bean
	Capability customCapability() {
		return new CustomCapability();
	}
}

Micrometer Support

如果满足以下所有条件,则会创建并注册一个 MicrometerObservationCapability bean,以便 Micrometer 可以观察您的 Feign 客户端:

feign-micrometer 位于类路径上

ObservationRegistry bean 可用

feign 千分尺属性设置为 true (默认情况下)

spring.cloud.openfeign.micrometer.enabled=true (适用于所有客户端)

spring.cloud.openfeign.client.config.feignName.micrometer.enabled=true (对于单个客户端)

Feign 缓存

如果使用 @EnableCaching 注释,则会创建并注册一个 CachingCapability bean,以便您的 Feign 客户端识别其接口上的 @Cache* 注释:

public interface DemoClient {

	@GetMapping("/demo/{filterParam}")
    @Cacheable(cacheNames = "demo-cache", key = "#keyParam")
	String demoEndpoint(String keyParam, @PathVariable String filterParam);
}

您还可以通过属性 spring.cloud.openfeign.cache.enabled=false 禁用该功能。

Feign @QueryMap 支持

Spring Cloud OpenFeign 提供了等效的 @SpringQueryMap 注解,用于将 POJO 或 Map 参数注解为查询参数映射。

例如, Params 类定义参数 param1 和 param2 :

// Params.java
public class Params {
	private String param1;
	private String param2;

	// [Getters and setters omitted for brevity]
}

以下 feign 客户端通过使用 @SpringQueryMap 注释来使用 Params 类:

@FeignClient("demo")
public interface DemoTemplate {

	@GetMapping(path = "/demo")
	String demoEndpoint(@SpringQueryMap Params params);
}

OpenFeign默认HttpClient修改

使用Apache HttpClient5替换,

引入依赖

<!-- feign-hc5-->
<dependency>
    <groupId>io.github.openfeign</groupId>
    <artifactId>feign-hc5</artifactId>
    <version>13.1</version>
</dependency>

修改yml配置

#  Apache HttpClient5 配置开启
spring:
  cloud:
    openfeign:
      httpclient:
        hc5:
          enabled: true
  • 28
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值