一、Feign简介
Feign是Netflix开发的声明式、模板化的HTTP客户端,可以更加快捷、优雅地调用HTTP API。SpringCloud对Feign进行了增强,使Feign支持了SpringMVC的注解,并整合了Ribbon和Eureka,在使用Feign时提供了HTTP客户端负载平衡,从而让Feign的使用更加便捷。
GitHub地址:https://github.com/OpenFeign/feign
二、Feign使用
1、pom.xml引入maven依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
2、客户端启动类添加注解@EnableFeignClients
@SpringCloudApplication
@EnableFeignClients(basePackages = {"com.ultrapower.uws"})
public class UwsDataApplication {
public static void main(String[] args) {
SpringApplication.run(UwsDataApplication.class);
}
}
3、Feign客户端
import com.ultrapower.uws.data.api.feign.hystrix.CollectDeviceFeignClientHystrix;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import java.util.List;
import java.util.Map;
@FeignClient(name ="uws-data-service",fallback=CollectDeviceHystrixClient.class)
public interface CollectDeviceFeignClient {
@PostMapping(value="/getDeviceLastestPhone")
String getDeviceLasestPhone(@RequestBody Map<String, Object> map);
}
@FeignClient中的"uws-data-service"是客户端名称,即提供方的服务名,用于创建Ribbon负载均衡器。在上边的例子中,由于使用了Eureka,所以Ribbon负载时候会把uws-data-service解析成Eureka Server服务注册表中的服务。可以用url属性指定请求的URL(可以是完整的URL或主机名),如http://localhost:8788。同时,SpringCloud中Feign整合了Hystrix,@FeignClient中的fallback属性指定的类,用于服务降级,当请求失败时候交由CollectDeviceHystrixClient处理。
三、自定义Feign配置
在SpringCloud中,Feign默认配置类是FeignClientsConfiguration,该类定义了Feign默认使用的编码器、解码器、契约等。SpringCloud允许通过注解@FeignClient的configuration属性自定义Feign的配置,自定义的配置优先级比FeignClientsConfiguration更高,就是会覆盖默认的配置。
@FeignClient(name = "uws-data-service", configuration = CollectDeviceHystrixClient.class)
public interface CollectDeviceFeignClient {
//..
}
@FeignClient的name和url属性中支持从配置中引用
@FeignClient(name = "${feign.name}", url = "${feign.url}")
public interface CollectDeviceFeignClient {
//..
}
Spring Cloud Netflix 为Feign提供了以下默认的beans (BeanType
beanName: ClassName
):
Decoder
feignDecoder:ResponseEntityDecoder
(which wraps aSpringDecoder
)Encoder
feignEncoder:SpringEncoder
Logger
feignLogger:Slf4jLogger
Contract
feignContract:SpringMvcContract
Feign.Builder
feignBuilder:HystrixFeign.Builder
Client
feignClient: 若启用了Ribbon客户端,就是LoadBalancerFeignClient
, 否则默认使用 feign客户端
SpringCloud中,使用OkHttpClient和ApacheHttpClient Feign客户端,在类路径上设置feign.okhttp.enabled或feign.httpclient.enabled为true。你也可以提供一个Apache的ClosableHttpClient或者HTTP的OkHttpClient来定制自己的HTTP客户端。默认情况下,SpringCloudNetflix不为feign提供以下bean,但仍然从应用程序上下文中查找这些类型的bean以创建feign客户端:
Logger.Level
Retryer
ErrorDecoder
Request.Options
Collection<RequestInterceptor>
SetterFactory
创建一个此类bean并将其放置在@FeignClient配置中(如上面的CollectDeviceFeignClient ),可以覆盖所描述的每个bean。配置类不应该被放在主应用上下文的@ComponentScan中,即不能被注解扫描到。
@Configuration
public class CollectDeviceFeignClient {
@Bean
public Contract feignContract() {
return new feign.Contract.Default();
}
@Bean
public BasicAuthRequestInterceptor basicAuthRequestInterceptor() {
return new BasicAuthRequestInterceptor("user", "password");
}
}
SpringCloud中,Feign默认使用的是SpringMvcContract
,因此它可以使用SpringMVC的注解。但这里使用了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,可以使用默认的Feign名称创建配置属性。如下application.yml
feign:
client:
config:
default:
connectTimeout: 5000
readTimeout: 5000
loggerLevel: basic
如果我们同时使用@Configuration配置和在配置文件中,那么配置文件中的属性优先级更高。但是若你想使的优先级更高,你可设置feign.client.default-to-properties=
false,关闭默认优先级。
如果您需要在RequestInterceptor中使用ThreadLocal 绑定变量,您需要将Hystrix的线程隔离策略设置为“SEMAPHORE ”,或者在Feign中禁用Hystrix。
# To disable Hystrix in Feign
feign:
hystrix:
enabled: false
#or
# To set thread isolation to SEMAPHORE
hystrix:
command:
default:
execution:
isolation:
strategy: SEMAPHORE
四、Feign and @Primary
当Feign使用Hystrix进行服务降级的时候,同一类型的bean上下文中会有多个,这将导致添加@Autowired注解的类无法注入,因为没有一个bean是最原始的,多个bean Spring不在知道找哪一个。为了解决此问题,Spring Cloud Netflix将所有Feign实例标记为@Primary,从而Spring Framework知道将要注入那个bean。但是在某些情况下这是不可取的,若要关闭这种行为,可将@feignclient的primary
属性设置为false。如下
@FeignClient(name = "hello", primary = false)
public interface HelloClient {
// methods here
}
五、Feign对继承的支持
Feign支持继承,使用继承可以将一些操作公共部分接口分组到一些父接口中,简化开发。
公共部分接口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
@FeignClient("users")
public interface UserClient extends UserService {
}
不过这种方式在SpringCloud官方文档中比不太十分推荐,因为此种方式是服务端与客户端共享接口,造成了服务端与客户端代码的紧耦合。并且Feign目前并没有使用SpringMVC的工作机制(方法参数映射不被继承)
六、Feign对Hystrix支持
Feign提供了对Hystrix的支持,用于断路或者出错时候服务降级处理。下边是摘自官方文档中的示例,HystrixClientFallback就是降级处理的类,实现了HystrixClient,重写其原方法即可。
@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定义降级处理的类,并继成FallbackFactory<T>,重写create(Throwable cause)方法,返回失败信息
@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对压缩的支持
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.compression.request.mime-types设置压缩类型,默认类型为text/xml,application/xml,application/json。feign.compression.request.min-request-size设置请求最小阀值长度,默认长度2048。
八、Feign对日志的处理
很多场景下,需要知道Feign处理请求的具体细节。Feign也提供了灵活的日志处理,可以为每个Feign客户端指定日志记录策略,每个Feign客户端都会创建一个logger。默认情况下,logger的名称Feign接口的完整类名,Feign的日志只会对DEBUG级别的日志做出响应。
Logger.Level可以为每个Feign客户端配置各自的日志的策略,告诉Feign记录哪些日志。Logger.Level有以下可选值:
NONE
, 不记录任何值 (默认值).BASIC
, 仅记录请求方法,URL、响应状态代码及执行时间HEADERS
, 记录Basic级别的基础上,记录请求和响应头FULL
, 记录请求和响应头,body和元数据
如下,设置Logger.Level的策略为
FULL
@Configuration
public class FooConfiguration {
@Bean
Logger.Level feignLoggerLevel() {
return Logger.Level.FULL;
}
}
在Feign的接口上指定配置配置类
@FeignClient(name = "hello", @Configuration=FooConfiguration.class)
protected interface HystrixClient {
@RequestMapping(method = RequestMethod.GET, value = "/hello")
Hello iFailSometimes();
}
在application.yml中指定Feign接口的日志级别为DEBUG
logging:
level:
com.ultrapower.uws.base.utils.SnowFlakeUtil: DEBUG
参考:《Spring Cloud与Docker微服务架构实战》周立