无法让Feign客户端为基本示例

时间:2016-07-21 15:53:29

标签: rest spring-boot netflix-feign spring-cloud-feign feign

无法让Feign客户端工作。首先尝试使用POST。保持运行与编码器/解码器相关的错误说类型不正确。 然后在github上找到一个例子,最后调用简单的GET API并决定试一试。 仍然失败

在Github和网上,我看到了Feign Client的多个版本 Spring-Cloud,OpenFign,Netflix.feign有不同的版本。 任何人都可以描述一下应该用于生产的最好和最稳定的Feign客户端吗?

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();
        }

    }
}

应用:

尝试了所有可能的注释排列,认为它会解决AutoWire的问题,但仍然失败

@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 [主] [0; 39m   [36mo.s.boot.SpringApplication [0; 39m [2m:[0; 39m   应用程序启动失败

     

org.springframework.beans.factory.BeanCreationException:错误   创建名为&#39; testWLCController&#39;:注入自动装配的bean   依赖失败;嵌套异常是   org.springframework.beans.factory.BeanCreationException:不能   autowire字段:private com.paa.controllers.GetFeignClient   com.paa.controllers.TestWLCController.gfClient;嵌套异常是   org.springframework.beans.factory.BeanCreationException:错误   创建名为&#39; com.aa..controllers.GetFeignClient&#39;的bean   FactoryBean在对象创建时抛出异常;嵌套异常是   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]   引起:org.springframework.beans.factory.BeanCreationException:   无法自动装配字段:private com.paa.controllers.GetFeignClient   com.paa.controllers.TestWLCController.gfClient;嵌套异常是   org.springframework.beans.factory.BeanCreationException:错误   创建名为&#39; com.paa.controllers.GetFeignClient&#39;的bean   FactoryBean在对象创建时抛出异常;嵌套异常是   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   省略

2 个答案:

答案 0 :(得分:3)

我不确定你是否最终自己弄清楚了,但是为了其他可能偶然发现这个问题的人,下面是你要做的事情的一个有效例子。我首先要指出一些不正确的,或者至少在代码中不需要的东西,然后展示我的代码。

  1. 您应该尽量不使用url属性。而是使用<feign client name>.ribbon.listOfServers(或bootstrap.yml)中的bootstrap.properties设置服务器列表。这允许客户端负载平衡,因为listOfServers可以是以逗号分隔的列表。
  2. 使用功能区进行HTTPS连接,您需要指定HTTP连接不需要的两件事。该端口是listOfServers<feign client name>.ribbon.IsSecure: true的一部分。如果没有端口,则连接到端口80,如果没有IsSecure,则使用HTTP。
  3. 使用curl进行测试,我发现Reddit需要非常很长时间才能做出响应。有关如何细分请求 - 响应周期所花费的总时间的详细信息,请参阅此SO post

    $ 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
    
  4. 根据Netflix Wiki,默认的读取和连接超时为3000毫秒,因此除非您更改,否则您将始终超时。

    1. 您可能已经注意到我在User-Agent请求中指定了curl标头。这是因为Reddis似乎对此非常挑剔,如果没有指定,则返回HTTP 429&#34;太多的请求&#34;大部分的时间。他们不会在回复中返回Retry-After标题,因此在发出其他请求之前,您无需等待多长时间。
    2. Spring Cloud Netflix以及Netflix Feign是开源的,因此一些(很多)耐心和调试技巧非常方便。
    3. &#34;谈话很便宜。告诉我代码。&#34; (Torvalds,Linus(2000-08-25))

      我使用优秀的Spring Initializr网站制作了Gradle应用。这是build.gradle文件的摘录。

      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"
          }
      }
      

      怀抱客户

      @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();
      }
      

      启动应用

      @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
      

      整合测试

      @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));
          }
      }
      

答案 1 :(得分:0)

依赖项:

<dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
        </dependency>

         <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-starter-parent</artifactId>
                <version>Greenwich.RELEASE</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

应用程序:

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.openfeign.EnableFeignClients;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

import com.test.cloud.bookservice.models.Book;

@SpringBootApplication
@RestController
@RequestMapping("/books")
@EnableFeignClients
public class BookServiceApplication {

    Logger logger = LogManager.getLogger(BookServiceApplication.class);

    @Autowired
    private StoreClient storeClient;


    public static void main(String[] args) {
        SpringApplication.run(BookServiceApplication.class, args);
    }


    @GetMapping("/book")
    public Book findBook() {
            return this.restTemplate.getForObject("http://stores/book", Book.class);
    }

    @FeignClient(name = "StoreClient", url = "127.0.0.1:8089")
    interface StoreClient {
        @RequestMapping(method = RequestMethod.GET, value = "/stores/book", consumes = "application/json")
        Book getBook();
    }

}