Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Configuring a fallback in feign clients #298

Closed
codependent opened this issue Dec 1, 2015 · 13 comments
Closed

Configuring a fallback in feign clients #298

codependent opened this issue Dec 1, 2015 · 13 comments

Comments

@codependent
Copy link

It would be great if we could configure a Hystrix fallback in Feign clients. Any plans to implement this funcionality?

@codefromthecrypt
Copy link
Contributor

codefromthecrypt commented Dec 3, 2015 via email

@codependent
Copy link
Author

Hi! I searched the repository for something related and didn't find anything.

This is related to an issue I opened in Spring Cloud Netflix repo: I am using their Feign integration through the use of @FeingClient annotation:

@FeignClient("stories-microservice")
public interface StoryClient {

    @RequestMapping(method = RequestMethod.GET, value = "/stories", params="random")
    String getStory(@RequestParam("random") boolean random);

I have a rest controller that calls this client:

@RestController
@RequestMapping("/api")
@EnableEurekaClient
@EnableFeignClients
@EnableCircuitBreaker
@RefreshScope
@SpringBootApplication
public class StoryTellerApiApplication {

    @Autowired
    private StoryClient sgc;

    @RequestMapping(value="/stories", params="random=true")
    public String getRandomStory(HttpServletResponse response) {
        String randomStory = sgc.getStory(true);

The point is that there is no way of configuring a Hystrix callback for Feign clients. Under the hood feign-hystrix is used:

org.springframework.cloud.netflix.feign.FeignClientFactoryBean;

    Feign.Builder builder = HystrixFeign.builder()
    // required values
    .logger(logger)
    .encoder(get(factory, Encoder.class))
    .decoder(get(factory, Decoder.class))
    .contract(get(factory, Contract.class));

I have been looking for a way of defining hystrix callback methods in the native HystrixFeignBuilder but it seems there isn't any way to do it.

@spencergibb
Copy link
Contributor

@adriancole this if for defining a Hystrix fallback when there are errors. It was intentionally left out because I didn't know how to define a fallback and it wasn't needed to get simple hystrix support working.

@dnathanson
Copy link

I was wondering about this, too. Maybe something with an @fallback annotation that can be added to methods that points to static methods on an extension of the client interface.

public interface FooClient {
      void doSomething(String withArg);
}

@FeignClient("foo-client")       
public interface FeignFooClient extends FooClient {

    @Override
    @RequestMapping(method = RequestMethod.GET, value = "/something/{withArg}")
    @Fallback("trySomethingElse")
    SomethingResponse doSomething(String withArg);

    static SomethingResponse trySomethingElse(String withArg) {
        ...
    }
}       

This assumes you don't own the FooClient interface. If you do own that code, you could put the annotations directly on that interface.

@spencergibb
Copy link
Contributor

@dnathanson a static fallback won't be enough for me. I need it to be an instance that is looked up by some mechanism, so it can be properly configured.

@stuarthendren
Copy link

How about a fallback implementation of the interface?

public interface FooClient {
      void doSomething(String withArg);
}

public class FooFallback implements FooClient {
      public void doSomething(String withArg){
            System.out.println(withArg);
       }
}

@FeignClient(name="foo-client", fallback=FooFallback.class)       
public interface FeignFooClient extends FooClient {

    @Override
    @RequestMapping(method = RequestMethod.GET, value = "/something/{withArg}")
    void doSomething(String withArg);

}  

@spencergibb
Copy link
Contributor

I like that. There would need to be some kind of factory interface to create the impl. Default to new it using reflection, but allow spring or guice to look it up.

@spencergibb
Copy link
Contributor

FeignClient is specific to spring Cloud Netflix BTW.

@codefromthecrypt
Copy link
Contributor

How about a fallback implementation of the interface?

public interface FooClient {
void doSomething(String withArg);
}
public class FooFallback implements FooClient {
public void doSomething(String withArg){
System.out.println(withArg);
}
}

I like this.. I think that we could address this in the
HystrixFeignBuilder, ex add an arg for instance of T which is the fallback.
This makes the feature wiring agnostic.

codefromthecrypt pushed a commit that referenced this issue Jan 6, 2016
Fallbacks are known values, which you return when there's an error invoking an http method.
For example, you can return a cached result as opposed to raising an error to the caller. To use
this feature, pass a safe implementation of your target interface as the last parameter to `HystrixFeign.Builder.target`.

Here's an example:

```java
// When dealing with fallbacks, it is less tedious to keep interfaces small.
interface GitHub {
  @RequestLine("GET /repos/{owner}/{repo}/contributors")
  List<String> contributors(@param("owner") String owner, @param("repo") String repo);
}

// This instance will be invoked if there are errors of any kind.
GitHub fallback = (owner, repo) -> {
  if (owner.equals("Netflix") && repo.equals("feign")) {
    return Arrays.asList("stuarthendren"); // inspired this approach!
  } else {
    return Collections.emptyList();
  }
};

GitHub github = HystrixFeign.builder()
                            ...
                            .target(GitHub.class, "https://api.github.com", fallback);
```

Credit to the idea goes to @stuarthendren!

Fixes #298
@codefromthecrypt
Copy link
Contributor

here t'is #308 cc @jdamick who's been quiet lately :P

@Dreampie
Copy link

I like this.when this implementation can we use?

public interface FooClient {
      void doSomething(String withArg);
}

public class FooFallback implements FooClient {
      public void doSomething(String withArg){
            System.out.println(withArg);
       }
}

@FeignClient(name="foo-client", fallback=FooFallback.class)       
public interface FeignFooClient extends FooClient {

    @Override
    @RequestMapping(method = RequestMethod.GET, value = "/something/{withArg}")
    void doSomething(String withArg);

}  

@codefromthecrypt
Copy link
Contributor

codefromthecrypt commented Jan 12, 2016 via email

@herrvlk
Copy link

herrvlk commented Aug 22, 2016

In fact the FooFallback must implement FeignFooClient interface to make it work. I'm leaving this comment here in case someone follows this as a guide :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

7 participants