Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can't get Feign Client to work for a basic example

Can't get Feign Client to work. First tried with POST. Kept running into errors related to Encoder/Decoder saying type is not right. Then found an example on github to call simple GET API finally and decided to give it a shot. Still fails

On Github and online, I am seeing multiple versions of Feign Client Spring-Cloud, OpenFeign, Netflix.feign having different versions. Could anyone describe what's the best and stable Feign client one should use for production?

package com.paa.controllers;

import org.springframework.cloud.netflix.feign.FeignClient;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

@FeignClient (name="test-service",url="https://www.reddit.com/r")
public interface GetFeignClient {

     @RequestMapping(method = RequestMethod.GET, value = "/java.json")
     public String posts();
}

Controller:

@RestController
@RequestMapping("/some/api")
public class TestWLCController {

  @Autowired
  private GetFeignClient getFeignClient;

  .. some stuff


    @RequestMapping(value="/postSomething",method = RequestMethod.POST)
    @ApiOperation(value = "Configures something",
            notes = "basic rest controller for testing feign")

    public ResponseEntity<SomeResponse> feignPost(
            UriComponentsBuilder builder,
            @ApiParam(name = "myRequest", 
            value = "request for configuring something", 
            required = true)
            @Valid @RequestBody SomeRequest someRequest) {

        String resp = null;
        try {
            resp = getFeignClient.posts();
        } catch (Exception er) {
            er.printStackTrace();
        }

    }
}

Application:

Tried all possible permutations of annotations thinking it would resolve AutoWire stuff but still fails

@Configuration
@ComponentScan
@EnableAutoConfiguration
//@EnableEurekaClient
@EnableFeignClients

//@SpringBootApplication
//@EnableFeignClients
//@EnableFeignClients(basePackages = {"com.paa.xenia.controllers", "com.paa.xenia.services"})
public class ServiceApplication extends SpringBootServletInitializer {

    @Override
    protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {

        return application.sources(XeniaServiceApplication.class);
    }

    public static void main(String[] args) {

        SpringApplication.run(ServiceApplication.class, args);
    }
}

2016-07-20 18:15:42.406[0;39m [31mERROR[0;39m [35m32749[0;39m [2m---[0;39m [2m[ main][0;39m [36mo.s.boot.SpringApplication [0;39m [2m:[0;39m Application startup failed

org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'testWLCController': Injection of autowired dependencies failed; nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire field: private com.paa.controllers.GetFeignClient com.paa.controllers.TestWLCController.gfClient; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'com.aa..controllers.GetFeignClient': FactoryBean threw exception on object creation; nested exception is java.lang.NullPointerException at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues(AutowiredAnnotationBeanPostProcessor.java:334) ~[spring-beans-4.2.6.RELEASE.jar:4.2.6.RELEASE] at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1214) ~[spring-beans-4.2.6.RELEASE.jar:4.2.6.RELEASE] at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:543) ~[spring-beans-4.2.6.RELEASE.jar:4.2.6.RELEASE] at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:482) ~[spring-beans-4.2.6.RELEASE.jar:4.2.6.RELEASE] at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:306) ~[spring-beans-4.2.6.RELEASE.jar:4.2.6.RELEASE] at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:230) ~[spring-beans-4.2.6.RELEASE.jar:4.2.6.RELEASE] at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:302) ~[spring-beans-4.2.6.RELEASE.jar:4.2.6.RELEASE] at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:197) ~[spring-beans-4.2.6.RELEASE.jar:4.2.6.RELEASE] at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:772) ~[spring-beans-4.2.6.RELEASE.jar:4.2.6.RELEASE] at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:839) ~[spring-context-4.2.6.RELEASE.jar:4.2.6.RELEASE] at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:538) ~[spring-context-4.2.6.RELEASE.jar:4.2.6.RELEASE] at org.springframework.boot.context.embedded.EmbeddedWebApplicationContext.refresh(EmbeddedWebApplicationContext.java:118) ~[spring-boot-1.3.5.RELEASE.jar:1.3.5.RELEASE] at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:766) [spring-boot-1.3.5.RELEASE.jar:1.3.5.RELEASE] at org.springframework.boot.SpringApplication.createAndRefreshContext(SpringApplication.java:361) [spring-boot-1.3.5.RELEASE.jar:1.3.5.RELEASE] at org.springframework.boot.SpringApplication.run(SpringApplication.java:307) [spring-boot-1.3.5.RELEASE.jar:1.3.5.RELEASE] at org.springframework.boot.SpringApplication.run(SpringApplication.java:1191) [spring-boot-1.3.5.RELEASE.jar:1.3.5.RELEASE] at org.springframework.boot.SpringApplication.run(SpringApplication.java:1180) [spring-boot-1.3.5.RELEASE.jar:1.3.5.RELEASE] at com.paa.ServiceApplication.main(ServiceApplication.java:44) [bin/:na] Caused by: org.springframework.beans.factory.BeanCreationException: Could not autowire field: private com.paa.controllers.GetFeignClient com.paa.controllers.TestWLCController.gfClient; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'com.paa.controllers.GetFeignClient': FactoryBean threw exception on object creation; nested exception is java.lang.NullPointerException at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:573) ~[spring-beans-4.2.6.RELEASE.jar:4.2.6.RELEASE] at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:88) ~[spring-beans-4.2.6.RELEASE.jar:4.2.6.RELEASE] at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues(AutowiredAnnotationBeanPostProcessor.java:331) ~[spring-beans-4.2.6.RELEASE.jar:4.2.6.RELEASE] ... 17 com.n frames omitted

like image 550
Rockoder Avatar asked Jul 21 '16 15:07

Rockoder


People also ask

Can we use feign client without Eureka?

Yes you can use Feign without Ribbon, All you need to do is specify the base url in your Feign Java interface class.

Which annotation is used to enable feign client?

Each feign client is part of an ensemble of components that work together to contact a remote server on demand, and the ensemble has a name that you give it as an application developer using the @FeignClient annotation.

How do I call a feign client API?

Let's implement the Feign in our project and invoke other microservices using Feign. Step 1: Select currency-conversion-service project. Step 2: Open the pom. xml and add the Feign dependency.


1 Answers

I'm not sure if you eventually figured it out yourself, but for the sake of others who might stumble across this thread, below is a working example of what you were trying to do. I'll first point out a few things that are incorrect, or at least, not desired in your code, then show my code that works.

  1. You should try not to use the url attribute. Instead, set up a list of servers using <feign client name>.ribbon.listOfServers in bootstrap.yml (or bootstrap.properties). This allows for client side load balancing because listOfServers can be a comma-separated list.
  2. Using Ribbon for HTTPS connections, you need to specify two things that you don't need for HTTP connections. The port, which is part of the listOfServers and <feign client name>.ribbon.IsSecure: true. Without the port, the connection is made to port 80 and without the IsSecure, HTTP is used.
  3. Testing using curl, I found that Reddit takes a very long time to respond. See this SO post for details of how to break down the total time taken by the request-response cycle.

    $ curl -v -H "User-Agent: Mozilla/5.0" -w "@curl-format.txt" -o /dev/null -s "https://www.reddit.com/r/java/top.json?count=1"
    { [2759 bytes data]
    * Connection #0 to host www.reddit.com left intact
    time_namelookup:     0.527
    time_connect:        0.577
    time_appconnect:     0.758
    time_pretransfer:    0.758
    time_redirect:       0.000
    time_starttransfer: 11.189
                      ----------
    time_total:         11.218
    

According the Netflix Wiki, the default read and connect timeouts are 3000 milliseconds, so you will always timeout unless you change those.

  1. You may have noticed that I specified a User-Agent header in the curl request. That's because Reddis seems to be very picky about that and if not specified, returns a HTTP 429 "Too many requests" most of the times. They don't return a Retry-After header in the response so there's no telling how long you need to wait before making another request.
  2. Spring Cloud Netflix as well as Netflix Feign is open source, so some (a lot of) patience and debugging skills come really handy.

"Talk is cheap. Show me the code." (Torvalds, Linus (2000-08-25)).

I generated a Gradle app using the excellent Spring Initializr site. Here's a snippet from the build.gradle file.

dependencies {
    compile('org.springframework.cloud:spring-cloud-starter-feign')
    compile('org.springframework.boot:spring-boot-starter-web')
    testCompile('org.springframework.boot:spring-boot-starter-test')
}

dependencyManagement {
    imports {
        mavenBom "org.springframework.cloud:spring-cloud-dependencies:Camden.SR3"
    }
}

Feign client:

@FeignClient(name = "reddit")
public interface RedditClient {
    @RequestMapping(method = GET, value = "/r/java/top.json?count=1",
            headers = {USER_AGENT + "=Mozilla/5.0", ACCEPT + "=" + APPLICATION_JSON_VALUE})
    public String posts();
}

Boot Application:

@SpringBootApplication
@EnableFeignClients
public class DemoApplication {
    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }

    @RestController
    static class DemoController {
        @Autowired
        private RedditClient redditClient;

        @GetMapping("/posts")
        public String posts() {
            return redditClient.posts();
        }
    }
}

bootstrap.yml:

reddit:
  ribbon:
    listOfServers: www.reddit.com:443
    ConnectTimeout: 20000
    ReadTimeout: 20000
    IsSecure: true
hystrix.command.default.execution:
  timeout.enabled: true
  isolation.thread.timeoutInMilliseconds: 50000

Integration test:

@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class DemoApplicationTest {
    @Autowired
    private TestRestTemplate restTemplate;

    @Test
    public void testGetPosts() {
        ResponseEntity<String> responseEntity = restTemplate.getForEntity("/posts", String.class);

        HttpStatus statusCode = responseEntity.getStatusCode();
        assertThat(String.format("Actual status code: %d, reason: %s.",
                statusCode.value(), statusCode.getReasonPhrase()),
                statusCode.is2xxSuccessful(), equalTo(true));
    }
}
like image 143
Abhijit Sarkar Avatar answered Oct 01 '22 01:10

Abhijit Sarkar