SpringCloud-Feign:Web客户端

摘要

  微服务架构中,若各个微服务通过RestTemplate相互调用,较为不便,SpringCloud整合了Feign,服务相互调用的Web客户端。本文介绍如何使用Feign。

参考文档:http://cloud.spring.io/spring-cloud-static/Finchley.RELEASE/single/spring-cloud.html#_spring_cloud_openfeign
略:22.4、22.7-

正文

1.1 如何加入Feign

  • 加入Feign依赖
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
  • 为被调用服务创建FeignClient

      此处以user-service服务的findById接口为例
      

@FeignClient(name="user-service")
public interface UserFeignClient{
    @RequestMapping(value="/{id}", method = RequestMethod.GET)
    public User finaById(@PathVariable("id") Long id);
}
  • 启动加入@EnableFeignClients注解声明开启Feign
@SpringBootApplication
@EnableFeignClients
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

  可以通过url属性指定请求,如:@FeignClient(name="xxx",url="http://ip:port/")

1.2 自定义Feign配置

1.2.1 配置类实现自定义配置

  SpringCloud默认为每个FeignClient创建一个FeignClientsConfiguration,可以通过@FeignClient(name="user-service",configuration = UserServiceFeignClientConfig.class)的方式自定义FeignClient配置,这种情况下配置由FeignClientsConfigurationUserServiceFeignClientConfig共同组成(后者优先级较高)。
  注:UserServiceFeignClientConfig可以不标注@Configuration,若标注该注解,应该避免被Spring容器扫描到,否则会成为全局Feign配置类。

1.2.2 配置文件实现自定义配置

feign:
    client:
        config:
            # 指定服务的Feign配置
            user-service:
                connectTimeout: 5000
                readTimeout: 5000
                loggerLevel: full
                errorDecoder: com.example.SimpleErrorDecoder
                retryer: com.example.SimpleRetryer
                requestInterceptors:
                  - com.example.FooRequestInterceptor
                  - com.example.BarRequestInterceptor
                decode404: false
                encoder: com.example.SimpleEncoder
                decoder: com.example.SimpleDecoder
                contract: com.example.SimpleContract
            # 全局Feign配置
            default:
                connectTimeout:5000 
                readTimeout:5000 
                loggerLevel:basic

  默认使用ribbon客户端,若配置feign.okhttp.enabledfeign.httpclient.enabled为true

1.2.3 配置类和配置文件的优先级

  当配置类和配置文件同时存在,配置文件将优先生效,若希望配置类优先生效,在配置类中加入feign.client.default-to-properties=false即可。

1.2.4 使用Hystrix需要注意

  若需要在RequestInterceptor使用ThreadLocal绑定变量,需要设置Hystrix隔离级别为SEMAPHORE或者关闭Hystrix。

# 关闭
feign:
  hystrix:
    enabled: false

# 设置隔离级别
hystrix:
  command:
    default:
      execution:
        isolation:
          strategy: SEMAPHORE

1.3 手动创建FeignClient

  通过下面的例子讲解如何手动创建FeignClient,首先要去掉之前项目中的@FeignClient和@EnableFeignClients。

@Import(FeignClientsConfiguration.class)
class UserController {
    private UserFeignClient userFeignClient;

    @Autowired
    public UserController(Decoder decoder, Encoder encoder, Client client, Contract contract) {
        this.userFeignClient = Feign.builder().client(client)
                .encoder(encoder)
                .decoder(decoder)
                .contract(contract)
                .requestInterceptor(new BasicAuthRequestInterceptor("user", "user"))
                .target(FooClient.class, "http://USER-SERVICE");
    }
}

1.4 加入Hystrix支持

  通过下面的例子介绍如何为Feign加入Hystrix

@FeignClient(name = "user-service", fallback = UserServiceClientFallback.class)
public interface UserServiceClient {
    @RequestMapping(method = RequestMethod.GET, value = "/{id}")
    User findById(@PathVariable("id") Long id);
}

public class UserServiceClientFallback implements UserServiceClient {
    @Override
    public User findById(Long id) {
        return new User();
    }
}

  若希望得到异常信息,通过下面的例子介绍Hystrix如何获取Feign调用异常

@FeignClient(name = "user-service", fallbackFactory = UserServiceClientFallback.class)
public interface UserServiceClient {
    @RequestMapping(method = RequestMethod.GET, value = "/{id}")
    User findById(@PathVariable("id") Long id);
}
@Component
public class UserServiceClientFallback implements FallbackFactory<UserServiceClient> {
    @Override
    public HystrixClient create(Throwable cause) {
        return new HystrixClient() {
            @Override
            public User findById(Long id) {
                return new User();
            }
        };
    }
}

1.5 @FeignClient属性详解

public @interface FeignClient {

    /**
     * The name of the service with optional protocol prefix. Synonym for {@link #name()
     * name}. A name must be specified for all clients, whether or not a url is provided.
     * Can be specified as property key, eg: ${propertyKey}.
     */
    @AliasFor("name")
    String value() default "";

    /**
     * The service id with optional protocol prefix. Synonym for {@link #value() value}.
     *
     * @deprecated use {@link #name() name} instead
     */
    @Deprecated
    String serviceId() default "";

    /**
     * The service id with optional protocol prefix. Synonym for {@link #value() value}.
     */
    @AliasFor("value")
    String name() default "";

    /**
     * Sets the <code>@Qualifier</code> value for the feign client.
     */
    String qualifier() default "";

    /**
     * An absolute URL or resolvable hostname (the protocol is optional).
     */
    String url() default "";

    /**
     * Whether 404s should be decoded instead of throwing FeignExceptions
     */
    boolean decode404() default false;

    /**
     * A custom <code>@Configuration</code> for the feign client. Can contain override
     * <code>@Bean</code> definition for the pieces that make up the client, for instance
     * {@link feign.codec.Decoder}, {@link feign.codec.Encoder}, {@link feign.Contract}.
     *
     * 指定该FeignClient的配置
     */
    Class<?>[] configuration() default {};

    /**
     * 指定回调函数类
     */
    Class<?> fallback() default void.class;

    /**
     * 指定回调函数工厂类
     */
    Class<?> fallbackFactory() default void.class;

    /**
     * 为所有当请求地址加入统一前缀,若目标微服务设置了context-path属性,配置该属性可以简化代码。
     */
    String path() default "";

    /**
     * 是否设置FeignClient为primary bean,效果类似@Primary注解。
     * 整合Hystrix时,容器中可能会有多个该类型bean,该属性主要用于这种情况。
     */
    boolean primary() default true;
}

常见问题

1 使用FeignClient出现404问题

  • 检查目标服务是否配置context-path属性,若配置过,则设置@FeignClient的path属性与目标服务context-path一致。

2 RequestParam.value() was empty on parameter 0

  • 检查异常的FeignClient类中,所有的@RequestParam是否指定了value属性,如:@RequestParam(value = “param”)

3 Required String parameter ‘xxx’ is not present

  • 同问题2

4 not annotated with HTTP method type (ex. GET, POST)

  • FeignClient不识别@GetMapping、@PostMapping等注解,应该通过这种方式指定请求类型:@RequestMapping(method = RequestMethod.GET)
  • 使用FeignClient继承方式时,有时即使接口方法使用@RequestMapping(method = RequestMethod.GET)方式声明请求类型,也会出现该异常,可以在重写的方法上再标记一次@RequestMapping(method = RequestMethod.GET,value=”xxx”)

未完待续

猜你喜欢

转载自blog.csdn.net/weixin_38229356/article/details/80883045