转载请注明出处 http://www.paraller.com
原文排版地址 点击跳转html
Feign是从Netflix中分离出来的轻量级项目,可以在类接口上添加注释,成为一个REST API 客户端。java
Feign中对 Hystrix 有依赖关系。Feign只是一个便利的rest框架,简化调用,最后仍是经过ribbon在注册服务器中找到服务实例,而后对请求进行分配。react
在入口程序中添加注释git
@EnableFeignClients
REST API 客户端github
@FeignClient(value = "ticket-service", configuration = YeaFeignConfiguration.class,fallback = TicketClientHystrix.class) interface TicketClient { @RequestMapping(method = RequestMethod.POST, value = "/create") Message<String> create( @RequestParam(value = "Type") Integer Type, @RequestParam(value = "amount") String amount, @RequestParam(value = "userId") String userId, @RequestParam(value = "mobile") String mobile, @RequestParam(value = "status") Integer status, @RequestParam(value = "belong") Integer belong, @RequestParam(value = "useProfit")String useProfit, @RequestParam(value = "useCounter")String useCounter); }
自定义FeignConfiguration属性web
@Configuration public class YeaFeignConfiguration { public static final int CONNECT_TIMEOUT_MILLIS = 5000; public static final int READ_TIMEOUT_MILLIS = 5000; @Bean public Logger.Level feignLogger() { return Logger.Level.FULL; } @Bean public Request.Options options() { return new Request.Options(CONNECT_TIMEOUT_MILLIS, READ_TIMEOUT_MILLIS); } }
pom.xmlspring
<dependency> <groupId>io.github.openfeign</groupId> <artifactId>feign-core</artifactId> <version>${project.version}</version> </dependency> <dependency> <groupId>io.github.openfeign</groupId> <artifactId>feign-gson</artifactId> <version>${project.version}</version> </dependency>
获取URL的代码,而后封装成对象返回json
public class GitHubExample { interface GitHub { class Repository { String name; } class Contributor { String login; } @RequestLine("GET /users/{username}/repos?sort=full_name") List<Repository> repos(@Param("username") String owner); @RequestLine("GET /repos/{owner}/{repo}/contributors") List<Contributor> contributors(@Param("owner") String owner, @Param("repo") String repo); /** Lists all contributors for all repos owned by a user. */ default List<String> contributors(String owner) { return repos(owner).stream().flatMap(repo -> contributors(owner, repo.name).stream()).map(c -> c.login) .distinct().collect(Collectors.toList()); } static GitHub connect() { Decoder decoder = new GsonDecoder(); return Feign.builder().decoder(decoder).errorDecoder(new GitHubErrorDecoder(decoder)) .logger(new Logger.ErrorLogger()).logLevel(Logger.Level.BASIC) .target(GitHub.class, "https://api.github.com"); } } static class GitHubClientError extends RuntimeException { private String message; // parsed from json @Override public String getMessage() { return message; } } static class GitHubErrorDecoder implements ErrorDecoder { final Decoder decoder; final ErrorDecoder defaultDecoder = new ErrorDecoder.Default(); GitHubErrorDecoder(Decoder decoder) { this.decoder = decoder; } @Override public Exception decode(String methodKey, Response response) { try { return (Exception) decoder.decode(response, GitHubClientError.class); } catch (IOException fallbackToDefault) { return defaultDecoder.decode(methodKey, response); } } } public static void main(String... args) { GitHub github = GitHub.connect(); System.out.println("Let's fetch and print a list of the contributors to this org."); List<String> contributors = github.contributors("netflix"); for (String contributor : contributors) { System.out.println(contributor); } System.out.println("Now, let's cause an error."); try { github.contributors("netflix", "some-unknown-project"); } catch (GitHubClientError e) { System.out.println(e.getMessage()); } } }
With Hystrix on the classpath, you can also return a HystrixCommanapi
基础用法服务器
@FeignClient("http://notification-service") public interface NotificationVersionResource { @RequestMapping(value = "/version", method = GET) String version(); }
细粒度操做
@FeignClient("http://notification-service") public interface NotificationVersionResource { @RequestMapping(value = "/version", method = GET) HystrixObservable<String> version(); }
Feign Clients能直接使用降级功能,最简单的方式就是使用接口,在接口中实现你的降级代码,在服务端发生错误的时候将会被调用。
@FeignClient("http://notification-service") public interface NotificationResource { @RequestMapping(value = "/notifications", method = GET) List<Notification> findAll(); } public class NotificationResourceImpl implements NotificationResource { @Override public List<Notification> findAll() { return new ArrayList<>(); } }
以前的示例都是在服务发现中,使用service的Name 去访问,可是一样的也支持使用外部连接去访问。
@FeignClient(name = "reddit-service", url = "${com.deswaef.reddit.url}") public interface RedditResource { @RequestMapping(method = RequestMethod.GET, value = "/java.json") RedditResponse posts(); }
Spring Cloud Netflix 为 Feign提供了下面默认的配置Bean
Decoder
feignDecoder: ResponseEntityDecoderEncoder
feignEncoder: SpringEncoderLogger
feignLogger: Slf4jLoggerContract
feignContract: SpringMvcContractFeign.Builder
feignBuilder: HystrixFeign.Builder能够经过设置 feign.okhttp.enabled:true
属性来使用OkHttpClient和ApacheHttpClient,别忘了添加到类路径
Spring Cloud Netflix 不为Feign提供下面的默认属性,可是同样会在应用的上下文中去搜索这些Bean而后建立feign client
Logger.Level
Retryer
ErrorDecoder
Request.Options
Collection
若是你须要一个额外的属性,或者想覆盖一个属性,你能够为每个FeignClient建立自定义的属性Bean:
@FeignClient( name = "reddit-service", url = "${com.deswaef.reddit.url}", configuration = RedditFeignConfiguration.class )
@Configuration public class RedditFeignConfiguration { public static final int FIVE_SECONDS = 5000; @Bean public Logger.Level feignLogger() { return Logger.Level.FULL; } @Bean public Request.Options options() { return new Request.Options(FIVE_SECONDS, FIVE_SECONDS); } @Bean public Contract feignContract() { return new feign.Contract.Default(); } @Bean public BasicAuthRequestInterceptor basicAuthRequestInterceptor() { return new BasicAuthRequestInterceptor("user", "password"); } }
若是你想在你的RequestInterceptor中使用ThreadLocal绑定变量,你须要在Hystrix中设置thread isolation策略或者disable Hystrix in Feign
# To disable Hystrix in Feign feign: hystrix: enabled: false # To set thread isolation to SEMAPHORE hystrix: command: default: execution: isolation: strategy: SEMAPHORE
if this configuration class is on the component scan path, it'll be also picked up as general configuration. This means that a configuration class like this, when also scanned by our automatic component scan, will override all of the beans for each and every FeignClient, not just the one which defined it as configuration.
也就是说,要是被 automatic component扫描到了,全部的FeignClient都会按照这个class的配置项去生效,而不是仅仅configuration = RedditFeignConfiguration.class 这个显示声明的接口,
As a result, you should place it inside a package that isn't a candidate for a component scan
不要放在能被扫描到的包中。
最简单的作法,就是不要标记 @Configuration 注释。
两个示例:
@Import(FeignClientsConfiguration.class) class FooController { private FooClient fooClient; private FooClient adminClient; @Autowired public FooController( Decoder decoder, Encoder encoder, Client client) { this.fooClient = Feign.builder().client(client) .encoder(encoder) .decoder(decoder) .requestInterceptor(new BasicAuthRequestInterceptor("user", "user")) .target(FooClient.class, "http://PROD-SVC"); this.adminClient = Feign.builder().client(client) .encoder(encoder) .decoder(decoder) .requestInterceptor(new BasicAuthRequestInterceptor("admin", "admin")) .target(FooClient.class, "http://PROD-SVC"); } } ## In the above example FeignClientsConfiguration.class is the default configuration provided by Spring Cloud Netflix.
interface GitHub { class Repository { String name; } class Contributor { String login; } @RequestLine("GET /users/{username}/repos?sort=full_name") List<Repository> repos(@Param("username") String owner); @RequestLine("GET /repos/{owner}/{repo}/contributors") List<Contributor> contributors(@Param("owner") String owner, @Param("repo") String repo); /** Lists all contributors for all repos owned by a user. */ default List<String> contributors(String owner) { return repos(owner).stream().flatMap(repo -> contributors(owner, repo.name).stream()).map(c -> c.login) .distinct().collect(Collectors.toList()); } static GitHub connect() { Decoder decoder = new GsonDecoder(); return Feign.builder().decoder(decoder).errorDecoder(new GitHubErrorDecoder(decoder)) .logger(new Logger.ErrorLogger()).logLevel(Logger.Level.BASIC) .target(GitHub.class, "https://api.github.com"); } }
一、Hystrix 在 classpath中且feign.hystrix.enabled=true
, Feign 包裹的的全部方法都会自带断路器(circuit breaker)
二、在方法中返回com.netflix.hystrix.HystrixCommand也是能够支持Hystrix特性 : This lets you use reactive patterns (with a call to .toObservable() or .observe() or asynchronous use (with a call to .queue()).
Note:
Prior to the Spring Cloud Dalston release, if Hystrix was on the classpath Feign would have wrapped all methods in a circuit breaker by default. This default behavior was changed in Spring Cloud Dalston in favor for an opt-in approach.
在单独的 Feign client中禁止Hystrix特性,能够建立一个Feign.Builder with the "prototype" scope
@Configuration public class FooConfiguration { @Bean @Scope("prototype") public Feign.Builder feignBuilder() { return Feign.builder(); } }
常规的降级方式:
@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"); } }
若是想要在测试的时候触发降级操做,可使用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 HystrixClientWithFallBackFactory() { @Override public Hello iFailSometimes() { return new Hello("fallback; reason was: " + cause.getMessage()); } }; } }
Note:
There is a limitation with the implementation of fallbacks in Feign and how Hystrix fallbacks work.
Fallbacks are currently not supported for methods that return com.netflix.hystrix.HystrixCommand and rx.Observable.
经过接口实现的方式去实现降级存在一些局限性。
Fallbacks 当前不支持 在方法中返回com.netflix.hystrix.HystrixCommand and rx.Observable.的状况。
When using Feign with Hystrix fallbacks, there are multiple beans in the ApplicationContext of the same type. This will cause @Autowired to not work because there isn’t exactly one bean, or one marked as primary. To work around this, Spring Cloud Netflix marks all Feign instances as @Primary, so Spring Framework will know which bean to inject. In some cases, this may not be desirable. To turn off this behavior set the primary attribute of @FeignClient to false.
@FeignClient(name = "hello", primary = false) public interface HelloClient { // methods here }
You may consider enabling the request or response GZIP compression for your Feign requests. You can do this by enabling one of the properties:
feign.compression.request.enabled=true feign.compression.response.enabled=true Feign request compression gives you settings similar to what you may set for your web server:
feign.compression.request.enabled=true feign.compression.request.mime-types=text/xml,application/xml,application/json feign.compression.request.min-request-size=2048
These properties allow you to be selective about the compressed media types and minimum request threshold length.
A logger is created for each Feign client created. By default the name of the logger is the full class name of the interface used to create the Feign client. Feign logging only responds to the DEBUG level.
application.yml
logging.level.project.user.UserClient: DEBUG
The Logger.Level object that you may configure per client, tells Feign how much to log. Choices are:
For example, the following would set the Logger.Level to FULL:
@Configuration public class FooConfiguration { @Bean Logger.Level feignLoggerLevel() { return Logger.Level.FULL; } }
The Netflix stack, using Spring Boot - Part 3: Feign
http://cloud.spring.io/spring-cloud-netflix/spring-cloud-netflix.html#spring-cloud-feign