5、Spring-Cloud-声明式调用 Feign(上)

 5.1、写一个 Feign 害户端

新建项目:

 

 依赖:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-test</artifactId>
    <scope>test</scope>
</dependency>

 

关于服务中心使用的是8762(之前的案列写过)

地址:https://www.cnblogs.com/Mrchengs/p/10645911.html

 
配置文件:
spring.application.name=feign
server.port=8088
eureka.client.service-url.defaultZone=http://localhost:8762/eureka/
 
主配置类:

@EnableFeignClients:开启Feign Client功能

@EnableFeignClients(basePackages = "com.cr.eurekafeignclient.feign")
@EnableDiscoveryClient
@SpringBootApplication
public class EurekaFeignClientApplication {

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

 

上述的三个步骤已经开启了Feign的功能
 
实现之前eureka client的/port服务的调用

 

EurekaClientFeign.java

@FeignClient(value = "CLINET",configuration = feignconfig.class)
public interface EurekaClientFeign {
    @GetMapping("/port")
    String port();

}
@FeignClient:
value:远程调用其他服务的服务名
feignconfig.class为Feign Client的配置类
port():通过Feign来调用CLIENT服务的“/port”的API接口

 

feignconfig.java

@Configuration
public class feignconfig {

    @Bean
    public Retryer feignRetryer(){
        return  new Retryer.Default(100, TimeUnit.SECONDS.toMillis(1L),5);
    }
}

 

配置类:注入一个feignRetryer的Retryer的Bean
注入Bean之后,Feign在远程调用失败之后会进行重试
 

FeignService.java

@Service
public class FeignService {

    @Autowired
    EurekaClientFeign eurekaClientFeign;

    public String  port(){
        return eurekaClientFeign.port();
    }
}
在Service层FeignService注入EurekaClientFeign去调用port()方法

 

FeignController.java

@RestController
public class FeignController {
    @Autowired
    FeignService feignService;

    @GetMapping("/feign")
    public String sayPort(){
        return  feignService.port();
    }
}
自动注入FeignService 去调用其port()方法
FeignService 通过 EurekaClientFeign远程调用CLIENT服务的API接口“/port”

 

此时的两个提供者:

 

分别是8089和8090端口
 
启动服务:

 

 

 

Feign Client 程调用了 eureka-client 务(8089、8090)两个端口的实例“/port”API接口
Feign Client 有负载均衡的能力。

 

spring-cloud-starter-openfeign 的porn 文件

<dependency>
   <groupId>org.springframework.cloud</groupId>
   <artifactId>spring-cloud-starter</artifactId>
</dependency>
<dependency>
   <groupId>org.springframework.cloud</groupId>
   <artifactId>spring-cloud-openfeign-core</artifactId>
</dependency>
<dependency>
   <groupId>org.springframework</groupId>
   <artifactId>spring-web</artifactId>
</dependency>
<dependency>
   <groupId>org.springframework.cloud</groupId>
   <artifactId>spring-cloud-commons</artifactId>
</dependency>
<dependency>
   <groupId>io.github.openfeign</groupId>
   <artifactId>feign-core</artifactId>
</dependency>
<dependency>
   <groupId>io.github.openfeign</groupId>
   <artifactId>feign-slf4j</artifactId>
</dependency>
<dependency>
   <groupId>io.github.openfeign</groupId>
   <artifactId>feign-hystrix</artifactId>
</dependency>
<dependency>
   <groupId>io.github.openfeign</groupId>
   <artifactId>feign-java8</artifactId>
</dependency>
<dependency>
   <groupId>org.springframework.cloud</groupId>
   <artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
   <optional>true</optional>
</dependency>
<dependency>
   <groupId>org.springframework.cloud</groupId>
   <artifactId>spring-cloud-starter-netflix-archaius</artifactId>
   <optional>true</optional>
</dependency>

 

 

 5.2、FeignClient详解

 @FeignClient注解源码

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

package org.springframework.cloud.openfeign;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.core.annotation.AliasFor;

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface FeignClient {
    @AliasFor("name")
    String value() default "";

    /** @deprecated */
    @Deprecated
    String serviceId() default "";

    String contextId() default "";

    @AliasFor("value")
    String name() default "";

    String qualifier() default "";

    String url() default "";

    boolean decode404() default false;

    Class<?>[] configuration() default {};

    Class<?> fallback() default void.class;

    Class<?> fallbackFactory() default void.class;

    String path() default "";

    boolean primary() default true;
}
FeignClient 注解被@Target(ElementType TYPE)修饰:
表示 FeignClient 注解的作用目标在接口上
 
@Retention(RetentionPolicy.RUNTll\伍)注解表明该注解会在 lass 字节码文件中存在,
在运行时可以通过反射获取到
 
@Documented 表示该注解将被包含在 Javadoc 中。
 
@Feign Client 注解用于创建声明式 API 接口,该接口是RESTful 风格的。
Feign 被设计成插拔式的,可以注入其他组件和 Feign一起使用
最典型的是如果 Ribbon 可用, Feign 会和Ribbon 结合进行负载均衡。
 
注解中的属性:
---value()和 name() 样,是被调用的服务的 Serviceld
---url ()直接填写硬编码的Uri 地址
---decode404()即 404 是被解码,还是抛异常。
---configuration ()指明 FeignClient 配置类,
    默认的配置类为 FeignClientsConfiguration类,在缺省的情况下 这个类注入
    了默认的 DecoderEncoder、 Contract 等配置的 Bean
---fall back()为配置熔断器的处理类。

 

 5.3、FeignClient的配置

Feign Client 默认的配置类为 FeignClientsConfiguration,这个类在在 pring-cloud-netflix-core的jar下
现这个类注入了很多 Feign 相关的配置 Bean(默认不配置的时候使用默认的)
Decoder、Encoder 、Contract个类在没有Bean被注入的情况下,会自动注入默认配置的 Bean

 

@Configuration
public class FeignClientsConfiguration {
    @Autowired
    private ObjectFactory<HttpMessageConverters> messageConverters;
    @Autowired(
        required = false
    )
    private List<AnnotatedParameterProcessor> parameterProcessors = new ArrayList();
    @Autowired(
        required = false
    )
    private List<FeignFormatterRegistrar> feignFormatterRegistrars = new ArrayList();
    @Autowired(
        required = false
    )
    private Logger logger;

    public FeignClientsConfiguration() {
    }

    @Bean
    @ConditionalOnMissingBean
    public Decoder feignDecoder() {
        return new OptionalDecoder(new ResponseEntityDecoder(new SpringDecoder(this.messageConverters)));
    }

    @Bean
    @ConditionalOnMissingBean
    @ConditionalOnMissingClass({"org.springframework.data.domain.Pageable"})
    public Encoder feignEncoder() {
        return new SpringEncoder(this.messageConverters);
    }

    @Bean
    @ConditionalOnClass(
        name = {"org.springframework.data.domain.Pageable"}
    )
    @ConditionalOnMissingBean
    public Encoder feignEncoderPageable() {
        return new PageableSpringEncoder(new SpringEncoder(this.messageConverters));
    }

    @Bean
    @ConditionalOnMissingBean
    public Contract feignContract(ConversionService feignConversionService) {
        return new SpringMvcContract(this.parameterProcessors, feignConversionService);
    }

    @Bean
    public FormattingConversionService feignConversionService() {
        FormattingConversionService conversionService = new DefaultFormattingConversionService();
        Iterator var2 = this.feignFormatterRegistrars.iterator();

        while(var2.hasNext()) {
            FeignFormatterRegistrar feignFormatterRegistrar = (FeignFormatterRegistrar)var2.next();
            feignFormatterRegistrar.registerFormatters(conversionService);
        }

        return conversionService;
    }

    @Bean
    @ConditionalOnMissingBean
    public Retryer feignRetryer() {
        return Retryer.NEVER_RETRY;
    }

    @Bean
    @Scope("prototype")
    @ConditionalOnMissingBean
    public Builder feignBuilder(Retryer retryer) {
        return Feign.builder().retryer(retryer);
    }

    @Bean
    @ConditionalOnMissingBean({FeignLoggerFactory.class})
    public FeignLoggerFactory feignLoggerFactory() {
        return new DefaultFeignLoggerFactory(this.logger);
    }

    @Bean
    @ConditionalOnClass(
        name = {"org.springframework.data.domain.Page"}
    )
    public Module pageJacksonModule() {
        return new PageJacksonModule();
    }

    @Configuration
    @ConditionalOnClass({HystrixCommand.class, HystrixFeign.class})
    protected static class HystrixFeignConfiguration {
        protected HystrixFeignConfiguration() {
        }

        @Bean
        @Scope("prototype")
        @ConditionalOnMissingBean
        @ConditionalOnProperty(
            name = {"feign.hystrix.enabled"}
        )
        public Builder feignHystrixBuilder() {
            return HystrixFeign.builder();
        }
    }
}

 

 

重写 FeignClientsConfiguration 类中的 Bean
覆盖掉默认的配置 Bean 
从而达到自定义配置的目的
 
Feign 默认的配置在请求失败后 重试次数为 ,即不重试Retryer.NEVER_RETRY
现在希望在请求失败后能够重试
这时需要写 个配置 FeignConfig 类
在该类中注入Retryer的Bean会覆盖掉默认的 Retryer的Bean
@Configuration
public class feignconfig {
    @Bean
    public Retryer feignRetryer(){
        return  new Retryer.Default(100, TimeUnit.SECONDS.toMillis(1L),5);
    }
}

 

posted @ 2019-04-02 23:12  MrChengs  阅读(895)  评论(0编辑  收藏  举报