内容简介:要在项目中包含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.Decoder
、 feign.Encoder
和 feign.Contract
,可以使用 @FeignClient
注解的 contextId
属性覆盖该集成的名称。
Spring Cloud允许你通过使用 @FeignClient
声明其他配置(在 FeignClientsConfiguration
之上)来完全控制feign客户端,例如:
@FeignClient(name = "stores", configuration = FooConfiguration.class)
public interface StoreClient {
//..
}
在这种情况下,客户端由 FeignClientsConfiguration
中已有的组件以及 FooConfiguration
中的任何组件组成(后者将覆盖前者)。
FooConfiguration
不需要使用 @Configuration
注解,但是,如果是,则注意将其从任何包含此配置的 @ComponentScan
中排除,因为它将成为 feign.Decoder
、 feign.Encoder
、 feign.Contract
等的默认源。这可以通过将其放在任何 @ComponentScan
或 @SpringBootApplication
的单独的非重叠包中来避免,也可以在 @ComponentScan
中明确排除。
现在不推荐使用 serviceId
属性,而是使用 name
属性。
使用 @FeignClient
注解的 contextId
属性除了更改 ApplicationContext
集成的名称,它将覆盖客户端名称的别名,它将用作为该客户端创建的配置bean名称的一部分。
以前,使用 url
属性不需要 name
属性,现在需要使用 name
。
name
和 url
属性支持占位符。
@FeignClient(name = "${feign.name}", url = "${feign.url}")
public interface StoreClient {
//..
}
Spring Cloud Netflix默认为feign( BeanType
beanName: ClassName
)提供以下bean:
-
DecoderfeignDecoder:ResponseEntityDecoder(包装SpringDecoder) -
EncoderfeignEncoder:SpringEncoder -
LoggerfeignLogger:Slf4jLogger -
ContractfeignContract:SpringMvcContract -
Feign.BuilderfeignBuilder:HystrixFeign.Builder -
ClientfeignClient:如果启用了Ribbon,则它是LoadBalancerFeignClient,否则使用默认的feign客户端。
可以通过将 feign.okhttp.enabled
或 feign.httpclient.enabled
分别设置为 true
,并将它们放在类路径上来使用 OkHttpClient
和 ApacheHttpClient
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客户端,以便它们指向同一服务器但每个都具有不同的自定义配置,那么我们必须使用 @FeignClient
的 contextId
属性,以避免这些配置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 Feign.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.HystrixCommand
和 rx.Observable
的方法目前不支持回退。
Feign和 @Primary
当与Hystrix回退一起使用Feign时, ApplicationContext
中有相同类型的多个bean,这将导致 @Autowired
无法工作,因为没有一个明确的bean或一个标记为 primary
的bean。为了解决这个问题,Spring Cloud Netflix将所有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 {
@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
类定义参数 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 class DemoTemplate {
@GetMapping(path = "/demo")
String demoEndpoint(@SpringQueryMap Params params);
}
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:- Spring Cloud实战系列(三) - 声明式客户端调用Feign
- JS变量声明和函数声明提升
- JavaScript声明变量详解
- javascript的变量声明
- Go - 变量声明
- PHP函数的类型声明
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
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》 这本书的介绍吧!