嘟嘟程序猿

SpringCloud--Feign RPC调用(五)

2019-07-14  本文已影响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
上一篇下一篇

猜你喜欢

热点阅读