Feign入门

一、Feign的引入
1.1 什么是Feign

Feign是Netflix开发的声明式、模板化的HTTP客户端,其灵感来自Retrofit、JAXRS­2.0以及WebSocket。Feign 可帮助我们更加便捷、优雅地调用HTTP API。

在Spring Cloud中,使用Feign非常简单——只需创建接口,并在接口上添加注解即可。

Feign支持多种注解,例如Feign自带的注解或者JAX­RS注解等。Spring Cloud对Feign进行了增强,使其支持Spring MVC注解,另外还整合了Ribbon和Eureka,从而使得Feign的使用更加方便

以前我们调用dao的时候 ,我们是不是一个接口加 注解然后在service中就可以进行调用了

@Mapper
public interface OrderDao { 
  List<Order> queryOrdersByUserId(Integer userId);
}
1.2 Ribbon Vs Feign

①:Ribbon+RestTemplate进行微服务调用模式

@Bean
@LoadBalanced
public RestTemplate restTemplate() { 
  return new RestTemplate(); 
} 
ResponseEntity<List> responseEntity = restTemplate.getForEntity("http://order­ service/order/queryOrdersByUserId/"+userId,List.class); 

我们不难发现,我们构建上诉的URL 是比较简单的,假如我们业务系统十分复杂,类似如下节点 https://www.baidu.com/s? wd=asf&rsv_spt=1&rsv_iqid=0xa25bbeba000047fd&issp=1&f=8&rsv_bp=0&rsv_idx=2&ie=utf- 8&tn=baiduhome_pg&rsv_enter=1&rsv_sug3=3&rsv_sug1=2&rsv_sug7=100&rsv_sug2=0&inputT=328&rsv_sug4=328 那么我们构建这个请求的URL是不是很复杂,若我们请求的参数有可能变动,那么是否这个URL是不是很复杂

如果系统业务非常复杂,而你是一个新人,当你看到这行代码,恐怕很难一眼看出其用途是什么!此时,你 很可能需要寻求老同事的帮助(往往是这行代码的作者,哈哈哈,可万一离职了呢?),或者查阅该目标地 址对应的文档(文档常常还和代码不匹配),才能清晰了解这行代码背后的含义!否则,你只能陷入蛋疼的境地!

二、在工程中添加Feign

案例背景:要在store-service服务中利用Feign调用order-service服务

第一步,我们采取开发中常用的套路 定义一个clyu-shop-feign-api工程,然后引入Feign依赖

<dependency>
	<groupId>org.springframework.cloudgroupId>
	<artifactId>spring‐cloud‐starter‐openfeignartifactId>
dependency>

第二步:修改打包方式(因为该工程式一个普通的jar 不需要打可执行的jar)

<build>
	<plugins> 
		<plugin>
			<groupId>org.apache.maven.pluginsgroupId>
			<artifactId>maven‐jar‐pluginartifactId>
		plugin>
	plugins>
 build>

第三步:编写声明式接口

@FeignClient(name = "order-service")
public interface OrderFeignApi {
    @RequestMapping(method = RequestMethod.GET,value = "/order/getOrder" )
    JsonResult getOrder();
}

第四步:在store-service服务中引入依赖包 clyu-shop-feign-api

<dependency>
    <groupId>com.clyu.shopgroupId>
    <artifactId>clyu-shop-feign-apiartifactId>
    <version>1.0-SNAPSHOTversion>
    <scope>compilescope>
dependency>

第五步:在store-service服务启动类上加上注解@EnableFeignClients

@SpringBootApplication
@EnableDiscoveryClient
@EnableFeignClients(basePackages="com.clyu.shop.*")
public class StoreApplication {
    public static void main( String[] args ){
        SpringApplication.run(StoreApplication.class, args);
    }
}

第六步:调用方式(像调用本地方式一样调用远程服务)

@RestController
@RequestMapping("store")
public class StoreController {

    @Autowired
    private OrderFeignApi orderFeignApi;

    @GetMapping("getOrderById2/{orderNo}")
    public JsonResult getOrderById2(@PathVariable String orderNo){
        return orderFeignApi.getOrder();
    }
}

我们服务提供者order-service 的controller 需要实现我们的OrderFeignApi接口,防止修改(比如我们的OrderFeignApi修改了,若没有实现该接口,服务提供者感知不到)

第一步:在order-service服务中引入依赖clyu-shop-feign-api

<dependency>
    <groupId>com.clyu.shopgroupId>
    <artifactId>clyu-shop-feign-apiartifactId>
    <version>1.0-SNAPSHOTversion>
    <scope>compilescope>
dependency>

第二步:我们的OrderController实现OrderFeignApi接口

@RestController
@RequestMapping("order")
public class OrderController implements OrderFeignApi {}
三、Feign的自定义配置
3.1 日志

默认情况下,Feign的调用式不打印日志,我们需要通过自定义来打印我们的Feign的日志 (basic适 用于生产环境)

级别 打印内容
NONE(默认值) 不记录任何日志
BASIC 仅记录请求方法、URL、响应状态代码以及执行时间(生产环境最好使用这个)
HEADERS 记录BASIC级别的基础上,记录请求和响应的header
FULL 记录请求和响应的header、body和元数据

第一种:基于java代码的配置

我们在clyu-shop-feign-api工程中添加Feign的自定义配置

/**
 * 这个类上千万不要添加@Configuration,不然会被作为全局配置文件共享
 * @author clyu
 * @since 2022-02-12 01:00
 */
public class OrderFeignConfig {
    //这个是控制打印日志的
    @Bean
    public Logger.Level level() {
        return Logger.Level.BASIC;
    }

}

OrderFeignApi 不用指定configuration的选项

@FeignClient(name = "order-service",configuration = OrderFeignConfig.class)
public interface OrderFeignApi {


    @RequestMapping(method = RequestMethod.GET,value = "/order/getOrder" )
    JsonResult getOrder();
}

针对调用端工程store-service针对日志com.tuling.feignapi 包下的日志级 别必须调整为DEBUG级别的 不然是不会打印日志的

#这个是控制输出日志的
logging:
  level:
    com:
      clyu:
        shop:
          feign:
            api: debug

注意,这个配置是看OrderFeignApi所在位置
Feign入门_第1张图片

第二种:基于yml文件细粒度配置

这种方法OrderFeignApi 不用指定configuration的选项

@FeignClient(name = "order-service")
public interface OrderFeignApi {

    @RequestMapping(method = RequestMethod.GET,value = "/order/getOrder" )
    JsonResult getOrder();
}

在调用方: store-service 通过feign:client:config:微服务名称:loggerLevel: 日志级别来指定

#设置调用方日志级别
logging:
  level:
    com:
      clyu:
        shop:
          feign:
            api: debug
            
#设置feign端order‐center日志级别
feign: 
  client: 
    config: 
      order-service: 
        loggerLevel: full

从上面可以看出如果我们要查看Feign日志,那么我们要设置2个地方,一个在feign端,一个在调用方store-service。这个就好比有个水管,在feign端设置日志级别是控制日志水流流入的大小,在调用方store-service设置日志级别是控制日志水流流出的大小。

3.2 @RequestLine的使用

第一种方法:在feign端clyu-shop-feign-api配置

public class OrderFeignConfig {

    @Bean
    public Contract feiContract() {
        return new Contract.Default();
    }
}

FeignClient类OrderFeignApi使用Feign原生的注解

@FeignClient(name = "order-service",configuration = OrderFeignConfig.class)
public interface OrderFeignApi {

    @RequestLine("GET /order/getOrder")
    JsonResult getOrder();
}

第二种方法:在调用方store-service,通过配置文件的形式来指定我们的契约

feign: 
  client: 
    config: 
      order-service: 
        loggerLevel: full
        contract: feign.Contract.Default #指定默认契约
3.3 拦截器的应用配置

在这里插入图片描述
上面的图是一个请求的部分过程,这个请求底层是store-service会通过http方式请求order-service,浏览器调用store-service的接口时,会传入一个请求头token,目前有个需求,这个token,store-service在调用order-service接口时,会把这个token作为请求头一并传给order-service的接口

RequestInterceptor的实现,在feign端clyu-shop-feign-api

public class TulingRequestInterceptor implements RequestInterceptor {
    @Override
    public void apply(RequestTemplate template) {
        template.header("token", UUID.randomUUID().toString());
    }
}

拦截器的注册,在feign端clyu-shop-feign-api

public class OrderFeignConfig {
  @Bean
  public TulingRequestInterceptor tulingRequestInterceptor(){
      return new TulingRequestInterceptor();
  }
}
@FeignClient(name = "order-service",configuration = OrderFeignConfig.class)
public interface OrderFeignApi {

    @RequestMapping(method = RequestMethod.GET,value = "/order/getOrder" )
    JsonResult getOrder();

    @RequestMapping(method = RequestMethod.GET,value = "/order/getToken4Header" )
    String getToken4Header(@RequestHeader("token") String token);
}
四、Feign调用优化方案
4.1 开启连接池配置
feign:
  client:
    config:
      order-service:
        httpclient:
          enabled: true 
          max‐connections: 200 #最大连接数 11 max‐connections‐per‐route: 50 #为每个url请求设置最大连接数
4.2 Feign的超时设置

Feign的底层用的是Ribbon,但是如果我们同时配置了Feign的超时和Ribbon超时,那么以Feign的超时设置说了算

案例:服务提供方模拟耗时 睡眠3S

@GetMapping("getToken4Header")
@Override
public String getToken4Header(String token) throws InterruptedException {
    Thread.sleep(3000);
    return token;
}

配置文件

#配置这个 ribbon肯定会超时 
ribbon: 
  connectTimeout: 2000 
  readTimeout: 2000 

#(Feign不会超时) 
feign:
  client: 
    config:
      default:
        connectTimeout: 5000 
        readTimeout: 5000

正好调用是3S 说明以Feign的超时说了算
在这里插入图片描述

你可能感兴趣的:(微服务,Feign)