Spring Cloud 参考文档(声明式REST客户端:Feign)

栏目: 编程工具 · 发布时间: 6年前

内容简介:要在项目中包含Feign,请使用包含组名为spring boot应用示例

声明式REST客户端:Feign

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

如何包含Feign

要在项目中包含Feign,请使用包含组名为 org.springframework.cloud 和工件名为 spring-cloud-starter-openfeign 的启动器。

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();

    @RequestMapping(method = RequestMethod.POST, value = "/stores/{storeId}", consumes = "application/json")
    Store update(@PathVariable("storeId") Long storeId, Store store);
}

@FeignClient 注解中,String值(上面的“stores”)是一个任意客户端名称,用于创建Ribbon负载均衡器,你还可以使用 url 属性指定URL(绝对值或仅指定主机名),应用程序上下文中bean的名称是接口的完全限定名称,要指定自己的别名值,可以使用 @FeignClient 注解的 qualifier 值。

上面的Ribbon客户端将想要发现“stores”服务的物理地址,如果你的应用程序是Eureka客户端,那么它将解析Eureka服务注册表中的服务,如果你不想使用Eureka,只需在外部配置中配置服务器列表。

覆盖Feign默认值

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

Spring Cloud允许你通过使用 @FeignClient 声明其他配置(在 FeignClientsConfiguration 之上)来完全控制feign客户端,例如:

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

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

FooConfiguration 不需要使用 @Configuration 注解,但是,如果是,则注意将其从任何包含此配置的 @ComponentScan 中排除,因为它将成为 feign.Decoderfeign.Encoderfeign.Contract 等的默认源。这可以通过将其放在任何 @ComponentScan@SpringBootApplication 的单独的非重叠包中来避免,也可以在 @ComponentScan 中明确排除。

现在不推荐使用 serviceId 属性,而是使用 name 属性。

使用 @FeignClient 注解的 contextId 属性除了更改 ApplicationContext 集成的名称,它将覆盖客户端名称的别名,它将用作为该客户端创建的配置bean名称的一部分。

以前,使用 url 属性不需要 name 属性,现在需要使用 name

nameurl 属性支持占位符。

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

Spring Cloud Netflix默认为feign( BeanType beanName: ClassName )提供以下bean:

  • Decoder feignDecoder: ResponseEntityDecoder (包装 SpringDecoder
  • Encoder feignEncoder: SpringEncoder
  • Logger feignLogger: Slf4jLogger
  • Contract feignContract: SpringMvcContract
  • Feign.Builder feignBuilder: HystrixFeign.Builder
  • Client feignClient:如果启用了Ribbon,则它是 LoadBalancerFeignClient ,否则使用默认的feign客户端。

可以通过将 feign.okhttp.enabledfeign.httpclient.enabled 分别设置为 true ,并将它们放在类路径上来使用 OkHttpClientApacheHttpClient feign客户端,你可以通过在使用Apache时提供 ClosableHttpClient 或在使用OK HTTP时提供 OkHttpClient 的bean来定制使用的HTTP客户端。

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

Logger.Level
Retryer
ErrorDecoder
Request.Options
Collection<RequestInterceptor>
SetterFactory

创建其中一种类型的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");
    }
}

这将使用 feign.Contract.Default 替换 SpringMvcContract ,并将 RequestInterceptor 添加到 RequestInterceptor 的集合中。

@FeignClient 也可以使用配置属性进行配置。

application.yml

feign:
  client:
    config:
      feignName:
        connectTimeout: 5000
        readTimeout: 5000
        loggerLevel: full
        errorDecoder: com.example.SimpleErrorDecoder
        retryer: com.example.SimpleRetryer
        requestInterceptors:
          - com.example.FooRequestInterceptor
          - com.example.BarRequestInterceptor
        decode404: false
        encoder: com.example.SimpleEncoder
        decoder: com.example.SimpleDecoder
        contract: com.example.SimpleContract

可以以与上述类似的方式在 @EnableFeignClients 属性 defaultConfiguration 中指定默认配置,不同之处在于此配置将适用于所有feign客户端。

如果你更喜欢使用配置属性来配置所有 @FeignClient ,则可以使用 default feign名称创建配置属性。

application.yml

feign:
  client:
    config:
      default:
        connectTimeout: 5000
        readTimeout: 5000
        loggerLevel: basic

如果我们同时创建 @Configuration bean和配置属性,配置属性将获胜,它将覆盖 @Configuration 值,但是,如果要将优先级更改为 @Configuration ,则可以将 feign.client.default-to-properties 更改为 false

如果需要在 RequestInterceptor 中使用 ThreadLocal 绑定变量,则需要将Hystrix的线程隔离策略设置为“SEMAPHORE”或在Feign中禁用Hystrix。

application.yml

# To disable Hystrix in Feign
feign:
  hystrix:
    enabled: false

# To set thread isolation to SEMAPHORE
hystrix:
  command:
    default:
      execution:
        isolation:
          strategy: SEMAPHORE

如果我们想要创建具有相同名称或URL的多个feign客户端,以便它们指向同一服务器但每个都具有不同的自定义配置,那么我们必须使用 @FeignClientcontextId 属性,以避免这些配置bean的名称冲突。

@FeignClient(contextId = "fooClient", name = "stores", configuration = FooConfiguration.class)
public interface FooClient {
    //..
}
@FeignClient(contextId = "barClient", name = "stores", configuration = BarConfiguration.class)
public interface BarClient {
    //..
}

手动创建Feign客户端

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

@Import(FeignClientsConfiguration.class)
class FooController {

    private FooClient fooClient;

    private FooClient adminClient;

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

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

在上面的示例中, FeignClientsConfiguration.class 是Spring Cloud Netflix提供的默认配置。

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

Feign Contract 对象定义了哪些注解和值在接口上是有效的,自动装配的 Contract bean提供对SpringMVC注解的支持,而不是默认的Feign原生注解。

Feign Hystrix支持

如果Hystrix位于类路径并且 feign.hystrix.enabled=true ,则Feign将使用断路器包装所有方法,返回 com.netflix.hystrix.HystrixCommand 也可用,这允许你使用反应模式(通过调用 .toObservable().observe() 或异步使用(通过调用 .queue() )。

要在每个客户端的基础上禁用Hystrix支持,请创建一个带有“prototype”范围的vanilla F​​eign.Builder ,例如:

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

在Spring Cloud Dalston发布之前,如果Hystrix在类路径上,Feign会默认将所有方法包装在断路器中,Spring Cloud Dalston中更改了此默认行为,转而采用了选择加入方法。

Feign Hystrix Fallback

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

@FeignClient(name = "hello", fallback = HystrixClientFallback.class)
protected interface HystrixClient {
    @RequestMapping(method = RequestMethod.GET, value = "/hello")
    Hello iFailSometimes();
}

static class HystrixClientFallback implements HystrixClient {
    @Override
    public Hello iFailSometimes() {
        return new Hello("fallback");
    }
}

如果需要访问产生回退触发器的原因,可以使用 @FeignClient 中的 fallbackFactory 属性。

@FeignClient(name = "hello", fallbackFactory = HystrixClientFallbackFactory.class)
protected interface HystrixClient {
    @RequestMapping(method = RequestMethod.GET, value = "/hello")
    Hello iFailSometimes();
}

@Component
static class HystrixClientFallbackFactory implements FallbackFactory<HystrixClient> {
    @Override
    public HystrixClient create(Throwable cause) {
        return new HystrixClient() {
            @Override
            public Hello iFailSometimes() {
                return new Hello("fallback; reason was: " + cause.getMessage());
            }
        };
    }
}

在Feign中实现回退以及Hystrix回退如何工作都有一定的限制,返回 com.netflix.hystrix.HystrixCommandrx.Observable 的方法目前不支持回退。

Feign和 @Primary

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

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

Feign继承支持

Feign通过单继承接口支持样板api,这允许将通用操作分组为方便的基本接口。

UserService.java

public interface UserService {

    @RequestMapping(method = RequestMethod.GET, value ="/users/{id}")
    User getUser(@PathVariable("id") long id);
}

UserResource.java

@RestController
public class UserResource implements UserService {

}

UserClient.java

package project.user;

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

}

通常不建议在服务器和客户端之间共享接口,它引入了紧耦合,并且实际上也不能以其当前形式使用Spring MVC(方法参数映射不会被继承)。

Feign请求/响应压缩

你可以考虑为你的Feign请求启用请求或响应GZIP压缩,你可以通过启用以下属性之一来执行此操作:

feign.compression.request.enabled=true
feign.compression.response.enabled=true

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

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

通过这些属性,你可以选择压缩介质类型和最小请求阈值长度。

Feign记录日志

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

application.yml

logging.level.project.user.UserClient: DEBUG

你可以为每个客户端配置 Logger.Level 对象,告诉Feign要记录多少,选择是:

  • NONE ,没有记录( DEFAULT )。
  • BASIC ,仅记录请求方法和URL以及响应状态代码和执行时间。
  • HEADERS ,记录基本信息以及请求和响应headers。
  • FULL ,记录请求和响应的headers、body和元数据。

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

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

Feign @QueryMap支持

OpenFeign @QueryMap 注解为POJO提供了支持,可用作GET参数映射,不幸的是,默认的OpenFeign QueryMap注解与Spring不兼容,因为它缺少 value 属性。

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

例如, Params 类定义参数 param1param2

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

    // [Getters and setters omitted for brevity]
}

以下feign客户端使用 @SpringQueryMap 注解使用 Params 类:

@FeignClient("demo")
public class DemoTemplate {

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

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Impractical Python Projects

Impractical Python Projects

Lee Vaughan / No Starch Press / 2018-11 / USD 29.95

Impractical Python Projects picks up where the complete beginner books leave off, expanding on existing concepts and introducing new tools that you’ll use every day. And to keep things interesting, ea......一起来看看 《Impractical Python Projects》 这本书的介绍吧!

html转js在线工具
html转js在线工具

html转js在线工具

RGB HSV 转换
RGB HSV 转换

RGB HSV 互转工具

RGB CMYK 转换工具
RGB CMYK 转换工具

RGB CMYK 互转工具