美文网首页嘟嘟程序猿
SpringCloud--Feign RPC调用(五)

SpringCloud--Feign RPC调用(五)

作者: 无剑_君 | 来源:发表于2019-07-14 14:10 被阅读95次

    一、Feign简介

    Feign是Netflix开发的声明式、模板化的HTTP客户端, Feign可以帮助我们更快捷、优雅地调用HTTP API。
    在Spring Cloud中,使用Feign非常简单——创建一个接口,并在接口上添加一些注解,代码就完成了。Feign支持多种注解,例如Feign自带的注解或者JAX-RS注解等。
    Spring Cloud对Feign进行了增强,使Feign支持了Spring MVC注解,并整合了Ribbon和Eureka,从而让Feign的使用更加方便。
    Spring Cloud Feign是基于Netflix feign实现,整合了Spring Cloud Ribbon和Spring Cloud Hystrix,除了提供这两者的强大功能外,还提供了一种声明式的Web服务客户端定义的方式。
    Spring Cloud Feign帮助我们定义和实现依赖服务接口的定义。在Spring Cloud feign的实现下,只需要创建一个接口并用注解方式配置它,即可完成服务提供方的接口绑定,简化了在使用Spring Cloud Ribbon时自行封装服务调用客户端的开发量。
    Spring Cloud Feign具备可插拔的注解支持,支持Feign注解、JAX-RS注解和Spring MVC的注解。

    文档地址:
    https://cloud.spring.io/spring-cloud-openfeign/spring-cloud-openfeign.html

    二、Maven依赖

    <!--web依赖-->
    <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>
    <!--openfeign依赖-->
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-openfeign</artifactId>
    </dependency>
    

    三、配置文件

    server.port=8008
    eureka.register.port=8761
    #服务实例名
    eureka.instance.hostname=order-service
    #服务名,如果没有则为 unknown
    spring.application.name=order-service
    
    eureka.register.host=localhost
    eureka.client.serviceUrl.defaultZone=http\://${eureka.register.host}\:${eureka.register.port}/eureka/
    #显示IP
    eureka.instance.prefer-ip-address=true
    # 注2.0 必须为ip-address
    eureka.instance.instance-id=${spring.cloud.client.ip-address}:${server.port}
    #Eureka客户端向服务端发送心跳的时间间隔,单位为秒(客户端告诉服务端自己会按照该规则)
    eureka.instance.lease-renewal-interval-in-seconds =3
    

    三、简单示例

    1. 在程序的启动类加上注解@EnableFeignClients开启Feign Client功能
    @SpringBootApplication
    @EnableFeignClients
    public class OrderServiceApplication {
        public static void main(String[] args) {
            SpringApplication.run(OrderServiceApplication.class, args);
        }
    }
    
    1. RPC调用
    @Component
    @FeignClient(value = "USER-SERVICE")
    public interface UserFeign {
        @GetMapping("/getUser")
        String getUser();
    }
    
    
    1. 测试
    @RunWith(SpringRunner.class)
    @SpringBootTest
    @Slf4j
    public class OrderServiceApplicationTests {
        @Autowired
        private UserFeign userFeign;
    
        @Test
        public void contextLoads() {
            String str = userFeign.getUser();
            log.info(str);
        }
    }
    
    

    四、Feign 基本使用

    @FeignClient 注解
    name:指定 Feign Client 的名称,如果项目使用了 Ribbon,name 属性会作为微服务的名称,用于服务发现。
    url:url 一般用于调试,可以手动指定 @FeignClient 调用的地址。
    decode404:当发生404错误时,如果该字段为 true,会调用 decoder 进行解码,否则抛出 FeignException。
    configuration:Feign 配置类,可以自定义 Feign 的 Encoder、Decoder、LogLevel、Contract。
    fallback:定义容错的处理类,当调用远程接口失败或超时时,会调用对应接口的容错逻辑,fallback 指定的类必须实现 @FeignClient 标记的接口。
    fallbackFactory:工厂类,用于生成 fallback 类示例,通过这个属性我们可以实现每个接口通用的容错逻辑,减少重复的代码。
    path:定义当前 FeignClient 的统一前缀。

    @FeignClient(name = "product-provider")  
    public interface ProductServiceFeign {  
      
        /** 
         * PathVariable 注解使用时,必须里面要有值,即@PathVariable("")或@PathVariable(name="") 
         * 
         * @param productId 
         * @return 
         */  
        @GetMapping("product/selectOne/{productId}")  
        ProductVO selectByProductId(@PathVariable("productId") String productId);  
      
        /** 
         * 去掉 @RequestParam 注解将变成post请求,加上为get请求 
         * 
         * @param params 
         * @return 
         */  
        @GetMapping("product/selectByProductIdAndName")  
        Map<String, Object> selectByProductIdAndNameMap(@RequestParam Map<String, Object> params);  
      
    //  程序启动报错,存在多个参数没有@requestParam注解  
    //  @GetMapping("product/selectByProductIdAndName")  
    //  Map<String, Object> selectByProductIdAndName(String productId, String productName);  
      
    //  程序启动报错,没有指定value的值,且参数不是map  
    //  @GetMapping("product/selectByProductIdAndName")  
    //  Map<String, Object> selectByProductIdAndName(@RequestParam String productId, String productName);  
      
    //  由于后面有一个参数没有加上@RequestParam注解,此时这个请求就变成了post请求发送,即使申明的是get请求  
    //  @GetMapping("product/selectByProductIdAndName")  
    //  Map<String, Object> selectByProductIdAndName(@RequestParam("productId") String productId, String productName);  
      
        @GetMapping("product/selectByProductIdAndName")  
        Map<String, Object> selectByProductIdAndName(@RequestParam("productId") String productId, @RequestParam("productName") String productName);  
      
        @PostMapping("product/addProduct")  
        Map<String, Object> addProduct(@RequestBody ProductVO productVO);  
      
        @PostMapping("product/updateProduct")  
        Map<String, Object> updateProduct(@RequestParam Map<String, Object> params);  
      
        @PostMapping("product/delete")  
        Map<String, Object> delteProduct(@RequestParam("productId") String productId);  
      
    }  
    

    五、Feign 自定义配置

    把配置文件放到ComponentScan扫描不到的地方。
    Configuration配置类

    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.context.annotation.Scope;
    
    import feign.Contract;
    import feign.Feign;
    
    @Configuration
    public class FeignConfig {
    
        //配置是在FeignClient中的接口中使用Feign自带的注解
        @Bean
        public Contract feignContract(){
            return new feign.Contract.Default();
       }
        
        //禁用Hystrix
        @Bean
        @Scope("prototype")
        public Feign.Builder feignBuilder() {
            return Feign.builder();
        } 
    }
    

    然后在UserFeignClient类中指定configuration:

    @FeignClient(value = "user-service",configuration = Configuration.class)
    

    第一个bean配置的是使用Feign的默认注解。
    注意,我们在此类中修改了Feign的Contract ,那么Contract 是什么呢。它叫做契约。因为Feign一开始使用的契约是SpringMVC,所以刚才我们SpringMVC的注解的时候直接成功了,但是你如果现在启动项目你就会发现已经启动不了了。因为Contract.Default()使用的契约是Feign自己的,也就是说我们要把SpringMVC的注解修改为Feign的注解。
    第二个bean配置的是是禁用Hystrix
    SpringMVC版本

    @GetMapping (value = "/user/getUser/{id}")
    public User getUser(@PathVariable("id")Long id);
    

    Feign版本

    @RequestLine("GET /user/getUser/{id}")
     public User getUser(@Param("id") Long id);
    

    六、Feign输出日志

    Feign日志输出说明
    Feign的日志是以下部分组成
    1、Feign的Level日志级别配置默认是:NONE,不要跟log日志级别混淆
    日志级别枚举类 Logger.Level
    NONE 不输出日志
    BASIC 只有请求方法、URL、响应状态代码、执行时间
    HEADERS基本信息以及请求和响应头
    FULL 请求和响应 的heads、body、metadata,建议使用这个级别
    2、log日志级别配置,默认是debug
    使用指定Feign类会包名配置日志打印级别
    此处使用spring logging配置 比如打印 UserFeign logging.level.com.example.feign.UserFeign=debug
    Feign日志输出-Logger.Level.FULL+log debug级别
    全局开启方式 使用spring java config 配置,注意该类一定要放到spring可以扫描到的包下:

    @Configuration
    public class FeignConfig {
        @Bean
        Logger.Level feignLevel() {
            return Logger.Level.FULL;
        }
    }
    

    application.properties 配置debug 日志输出级别

    user.url=http://localhost:8080/user
    logging.level.com.example.feign.UserFeign=debug
    

    运行 UserFeignTest.save方法 可以看到以下保存user输出的请求和响应日志

    2018-08-07 16:48:03.011 DEBUG 75661 --- [           main] com.example.feign.UserFeign              : [UserFeign#save] ---> POST http://localhost:8080/user HTTP/1.1
    2018-08-07 16:48:03.011 DEBUG 75661 --- [           main] com.example.feign.UserFeign              : [UserFeign#save] Content-Type: application/json;charset=UTF-8
    2018-08-07 16:48:03.011 DEBUG 75661 --- [           main] com.example.feign.UserFeign              : [UserFeign#save] Content-Length: 27
    2018-08-07 16:48:03.011 DEBUG 75661 --- [           main] com.example.feign.UserFeign              : [UserFeign#save] 
    2018-08-07 16:48:03.011 DEBUG 75661 --- [           main] com.example.feign.UserFeign              : [UserFeign#save] {"id":null,"name":"张三"}
    2018-08-07 16:48:03.012 DEBUG 75661 --- [           main] com.example.feign.UserFeign              : [UserFeign#save] ---> END HTTP (27-byte body)
    2018-08-07 16:48:03.041 DEBUG 75661 --- [           main] com.example.feign.UserFeign              : [UserFeign#save] <--- HTTP/1.1 200 (29ms)
    2018-08-07 16:48:03.042 DEBUG 75661 --- [           main] com.example.feign.UserFeign              : [UserFeign#save] content-length: 0
    2018-08-07 16:48:03.043 DEBUG 75661 --- [           main] com.example.feign.UserFeign              : [UserFeign#save] date: Tue, 07 Aug 2018 08:48:03 GMT
    2018-08-07 16:48:03.043 DEBUG 75661 --- [           main] com.example.feign.UserFeign              : [UserFeign#save] 
    2018-08-07 16:48:03.043 DEBUG 75661 --- [           main] com.example.feign.UserFeign              : [UserFeign#save] <--- END HTTP (0-byte body)
    
    

    七、配置请求用户名密码

    在项目中,微服务之间的通信也是通过Feign代理的HTTP客户端通信,为了保护我们的业务微服务不被其他非法未经允许的服务调用, 我们要进行访问授权配置!
    Feign是客户端配置,@FeignClient注解有个configuation属性,可以配置我们自定义的配置类,在此类中注入微服务认证拦截器

    1. 创建Feign的配置类
    @Configuration
    public class FeignConfiguration {
        @Bean
        public BasicAuthRequestInterceptor basicAuthRequestInterceptor(){
            return new BasicAuthRequestInterceptor("admin","123456");
        }
    }
    

    说明:@Bean配置是在请求接口中要进行基于Http Basic的认证后才能调用。

    1. FeignClient接口引用配置
    @Component
    @FeignClient(value = "USER-SERVICE", configuration= FeignConfiguration.class)
    public interface UserFeign {
        @GetMapping("/getUser")
        String getUser();
    }
    

    需要注意的是: FeignConfiguration类不能包含在主应用程序上下文的@ComponentScan中,否则该配置会被所有的@FeignClient共享。
    Ribbon的自定义配置中也需要注意这个问题。

    八、Feign超时设置

    feign.client.config.springApplicationName.connectTimeout=1000
    feign.client.config.springApplicationName.readTimeout=1000
    

    相关文章

      网友评论

        本文标题:SpringCloud--Feign RPC调用(五)

        本文链接:https://www.haomeiwen.com/subject/bhfqzqtx.html