【问题标题】:Can't get Feign Client to work for a basic example无法让 Feign Client 为基本示例工作
【发布时间】:2016-11-25 07:40:19
【问题描述】:

无法让 Feign Client 工作。首先尝试使用 POST。不断遇到与编码器/解码器相关的错误,说类型不正确。 然后在github上找到了一个调用简单GET API的例子,决定试一试。 还是失败了

在 Github 和网上,我看到了多个版本的 Feign Client Spring-Cloud、OpenFeign、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: 错误 创建名为“testWLCController”的bean:注入自动装配 依赖失败;嵌套异常是 org.springframework.beans.factory.BeanCreationException:不能 自动装配字段:私有 com.paa.controllers.GetFeignClient com.paa.controllers.TestWLCController.gfClient;嵌套异常是 org.springframework.beans.factory.BeanCreationException:错误 创建名为 'com.aa..controllers.GetFeignClient' 的 bean: FactoryBean 在创建对象时抛出异常;嵌套异常是 java.lang.NullPointerException 在 org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues(AutowiredAnnotationBeanPostProcessor.java:334) ~[spring-beans-4.2.6.RELEASE.jar:4.2.6.RELEASE] 在 org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1214) ~[spring-beans-4.2.6.RELEASE.jar:4.2.6.RELEASE] 在 org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:543) ~[spring-beans-4.2.6.RELEASE.jar:4.2.6.RELEASE] 在 org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:482) ~[spring-beans-4.2.6.RELEASE.jar:4.2.6.RELEASE] 在 org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:306) ~[spring-beans-4.2.6.RELEASE.jar:4.2.6.RELEASE] 在 org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:230) ~[spring-beans-4.2.6.RELEASE.jar:4.2.6.RELEASE] 在 org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:302) ~[spring-beans-4.2.6.RELEASE.jar:4.2.6.RELEASE] 在 org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:197) ~[spring-beans-4.2.6.RELEASE.jar:4.2.6.RELEASE] 在 org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:772) ~[spring-beans-4.2.6.RELEASE.jar:4.2.6.RELEASE] 在 org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:839) ~[spring-context-4.2.6.RELEASE.jar:4.2.6.RELEASE] 在 org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:538) ~[spring-context-4.2.6.RELEASE.jar:4.2.6.RELEASE] 在 org.springframework.boot.context.embedded.EmbeddedWebApplicationContext.refresh(EmbeddedWebApplicationContext.java:118) ~[spring-boot-1.3.5.RELEASE.jar:1.3.5.RELEASE] 在 org.springframework.boot.SpringApplication.refresh(SpringApplication.java:766) [spring-boot-1.3.5.RELEASE.jar:1.3.5.RELEASE] 在 org.springframework.boot.SpringApplication.createAndRefreshContext(SpringApplication.java:361) [spring-boot-1.3.5.RELEASE.jar:1.3.5.RELEASE] 在 org.springframework.boot.SpringApplication.run(SpringApplication.java:307) [spring-boot-1.3.5.RELEASE.jar:1.3.5.RELEASE] 在 org.springframework.boot.SpringApplication.run(SpringApplication.java:1191) [spring-boot-1.3.5.RELEASE.jar:1.3.5.RELEASE] 在 org.springframework.boot.SpringApplication.run(SpringApplication.java:1180) [spring-boot-1.3.5.RELEASE.jar:1.3.5.RELEASE] 在 com.paa.ServiceApplication.main(ServiceApplication.java:44) [bin/:na] 引起:org.springframework.beans.factory.BeanCreationException: 无法自动装配字段:私有 com.paa.controllers.GetFeignClient com.paa.controllers.TestWLCController.gfClient;嵌套异常是 org.springframework.beans.factory.BeanCreationException:错误 创建名为 'com.paa.controllers.GetFeignClient' 的 bean: FactoryBean 在创建对象时抛出异常;嵌套异常是 java.lang.NullPointerException 在 org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:573) ~[spring-beans-4.2.6.RELEASE.jar:4.2.6.RELEASE] 在 org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:88) ~[spring-beans-4.2.6.RELEASE.jar:4.2.6.RELEASE] 在 org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues(AutowiredAnnotationBeanPostProcessor.java:331) ~[spring-beans-4.2.6.RELEASE.jar:4.2.6.RELEASE] ... 17 com.n 帧 省略

【问题讨论】:

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


    【解决方案1】:

    我不确定您是否最终自己弄清楚了,但为了其他可能偶然发现此线程的人,下面是您尝试做的工作示例。我将首先指出一些不正确的地方,或者至少是你的代码中不需要的地方,然后展示我的有效代码。

    1. 您应该尽量不要使用url 属性。相反,在bootstrap.yml(或bootstrap.properties)中使用&lt;feign client name&gt;.ribbon.listOfServers 设置服务器列表。这允许客户端负载平衡,因为listOfServers 可以是逗号分隔的列表。
    2. 使用 Ribbon 进行 HTTPS 连接,您需要指定 HTTP 连接不需要的两件事。该端口是listOfServers&lt;feign client name&gt;.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
      

    根据 Netflix Wiki,默认读取和连接超时为 3000 毫秒,因此除非您更改这些超时,否则您将始终超时。

    1. 您可能已经注意到我在curl 请求中指定了User-Agent 标头。那是因为 Reddis 似乎对此非常挑剔,如果没有指定,大多数时候会返回 HTTP 429“请求过多”。它们不会在响应中返回 Retry-After 标头,因此不知道您需要等待多长时间才能发出另一个请求。
    2. Spring Cloud Netflix 和 Netflix Feign 是开源的,因此一些(很多)耐心和调试技巧非常方便。

    “说话很便宜。给我看代码。” (托瓦兹,莱纳斯(2000-08-25))

    我使用出色的 Spring Initializr 网站生成了一个 Gradle 应用程序。这是来自build.gradle 文件的 sn-p。

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

    【讨论】:

    • 虽然我们没有真正使用“ribbon”,但我能够成功使用 Feign。我们用作 Rest Client,因为它更干净。我们学到的另一课是 - 要让 Feign 处理标头和拦截响应,必须为 Feign 实现解码器,并且还要有一个 Feign Config。有时间我会发布代码。
    • 我试图澄清的一点是我们使用了没有 Ribbon 的 Feign。所以 Ribbon 不是强制使用 Feign ====feign: hystrix: enabled: false datasource: audit: mongodb: host: localhost port: 27019 database: audit ===
    • @Rockoder 你成功了。你有一些示例代码吗?
    • @alltej yes 能够使它工作,尽管获取代码会有点棘手。基本上,我们创建了一个父模块作为我们的框架并在那里使用了 Feign。使用 Feign 的模块不必在子类中显式声明 Feign。此外,春季版本很重要。我们在 3.5。我从一位同事那里了解到,当他们尝试使用更高版本时,他们遇到了问题,最终回到了我们正在使用的版本以使其正常工作。
    • @alltej cmets 有限制,所以我为您发布了答案。希望这会有所帮助。让它工作的所有基本组件/类骨架都在那里。让我知道这个是否奏效。另外,我上面提到的3.5版本是错误的。现在是 1.3.5。
    【解决方案2】:

    有人有兴趣了解我们是如何做到的,所以为了他们的利益发布答案。

    父模块

    package com.hitech.module.base;
    
    @EnableFeignClients
    public abstract class BaseApplication extends SpringBootServletInitializer {
    ...
    ..
    }
    
    build.gradle:
    
    buildscript {
        ext {
            springBootVersion = '1.3.5.RELEASE'
        }
        repositories {
            mavenCentral()
        }
        dependencies {
            classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}")
            classpath("io.spring.gradle:dependency-management-plugin:0.5.6.RELEASE")
        }
    }
    ...
    ...
    
    dependencies {
        compile('io.springfox:springfox-swagger-ui:2.5.0')
        compile('io.springfox:springfox-swagger2:2.5.0')
        compile('org.springframework.cloud:spring-cloud-starter-feign')
    

    子模块

    package com.hitech.module.app;
    
    import com.hitech.module.base.BaseApplication;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.boot.autoconfigure.data.mongo.MongoDataAutoConfiguration;
    import org.springframework.boot.autoconfigure.mongo.MongoAutoConfiguration;
    import org.springframework.cloud.netflix.feign.EnableFeignClients;
    
    @SpringBootApplication(exclude = {MongoDataAutoConfiguration.class, MongoAutoConfiguration.class},
            scanBasePackages = {"com.hitech.module.base", "com.hitech.module.app", })
    @EnableFeignClients("com.hitech.module.app.clients")
    public class MyServiceApplication extends BaseApplication {
    
        private static final Logger LOG = LoggerFactory.getLogger(MyServiceApplication.class);
    
        public static void main(String[] args) {
    
            String s1 = "google";
    
            LOG.info ("=== Started Orchestration Service ===");
            SpringApplication.run(MyServiceApplication.class, args);
    
    
        }
    }
    

    bootstrap.yml

    feign:
      hystrix:
        enabled: false
    datasource:
      audit:
        mongodb:
          host: localhost
          port: 27019
          database: audit
    

    【讨论】:

      【解决方案3】:

      依赖关系:

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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2023-04-11
        • 2011-05-24
        • 2013-11-26
        • 1970-01-01
        • 2014-05-13
        • 1970-01-01
        • 1970-01-01
        • 2014-06-07
        相关资源
        最近更新 更多