微服务开发(10)--Feign的使用

SpringCloud OpenFeign

什么是Feign

  • Feign是Netflix开发的声明式,模板化的Http客户端。Feign可以帮助我们更加快捷的,优雅的调用Http API
  • 与Ribbon对比来说,Feign的速度相对会慢一点,Feign的底层实际上就是使用了Ribbon进行调用的
  • Feign使用的是动态代理,而Ribbon使用的是拦截器
  • Ribbon借由RestTemplate来完成服务调用,调用方式麻烦,但是Feign使用非常简单,只需要创建接口,并在接口上添加注解就可以了。
  • Feign支持多种注解,例如,Feign自带的注解或者JAX-RS注解等。Spring Cloud对Feign进行了增强,使其支持Spring MVC注解,另外整合了Ribbon和Eureka,从而使得Feign的使用更加的方便。
  • 我们调用Feign的方式很像我们之前调用Dao的时候,我们使用Feign的时候就是在接口+注解,然后在controller中就可以对其进行调用。

Ribbon与Feign对比

(1)首先底层使用的就不一样,Ribbon使用拦截器对发送的方法(RestTemplate.getForEntity等方法)进行拦截,然后做相应的处理;而Feign使用的是动态代理的方法对其进行处理的。
(2)Feign底层还是使用Ribbon进行调用的。
(3)Feign的调用方式更加简单,采用接口+注解的方式就可以让用户无感知的完成HttpRestfulAPI方式进行调用。

构建Feign工程

1. 搭建服务的提供方

  • 搭建三个provider提供方,这里不做详细描述,可以参考微服务开发(9)的Ribbon的服务提供方的搭建Ribbon搭建

2. 搭建API工程
(1)新建feingn-api工程,这个工程的作用就是我们把所有的feign使用的service放到这里,这时候我们feign调用的时候,只需要继承该类,并在上面写注解即可
(2)看架构图
在这里插入图片描述
(3)pom文件

    <dependencies>

        <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-openfeign</artifactId>
        </dependency>
    </dependencies>

(4)主启动类

  • 虽然这里我们并不需要启动这个API,但是我们需要将其进行打包,我们在打包的时候,如果没有一个主启动类则会报错
package com.xiyou.api;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

/**
 * @author
 */
@SpringBootApplication
public class ApiApplication {
    public static void main(String[] args) {
        SpringApplication.run(ApiApplication.class, args);
    }
}

(5)API类书写

package com.xiyou.api.feign;

import feign.RequestLine;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.RequestMapping;


public interface OrderApi {

    /**
     *不认识@GetMapping 如果有参数记得传递,需要写全,不能省略
     * @return
     */
    @RequestMapping("/test")
    public String test();

	/**
	 * 这是个有参数的形式,必须在@PathVariable中写上名字,不能省略
	 */
	@RequestMapping("/queryOrdersByUserId/{userId}")
    public List<OrderVo> queryOrdersByUserId(@PathVariable("userId") Integer userId);
}

(6)将其打包成jar的形式,并且在调用方引入这个依赖

3. 创建服务的消费端
(1)架构图展示
在这里插入图片描述
(2)pom文件依赖

    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>
        <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>com.xiyou</groupId>
            <artifactId>my-feign-api</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>

    </dependencies>
  • 注意,记得除了引入feign相关的依赖外,还需要引入刚刚搭建的API的依赖

(3)配置文件

server.port=8000
spring.application.name=my-feign
eureka.client.service-url.defaultZone=http://localhost:9000/eureka
eureka.instance.instance-id=my-feign-8000
  • 也没有啥特殊的

(4)主启动类

package com.xiyou.feign;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.openfeign.EnableFeignClients;

/**
 * @author
 */
// 该注解必须设置扫描范围否则@FeignClient不起作用,也不会将其加入Spring IOC中
@EnableFeignClients(basePackages = "com.xiyou")
@SpringBootApplication
@EnableDiscoveryClient
public class FeignApplication {
    public static void main(String[] args) {
        SpringApplication.run(FeignApplication.class, args);
    }
}

  • 注意,主启动类上一定要加上@EnableFeignClients(basePackages = “com.xiyou”),这个表明了我们的@FeignClient注解的位置,我们会将其进行扫描,自动注入到Spring IOC的容器中

(5)Service层接口

package com.xiyou.feign.service;

import com.xiyou.api.feign.OrderApi;
import com.xiyou.feign.config.FeignConfig;
import org.springframework.cloud.openfeign.FeignClient;

/**
 * 将feign需要的接口全部抽象出来
 * @FeignClient中的name是要调用的服务的名字(application-name)path是其宽窄路径中的宽路径
 * @author
 */
@FeignClient(name = "my-provider", path = "/provider")
public interface FeignService extends OrderApi{
}

  • 注意:这个Service层我们也是一个接口,并且我们继承了API工程中定义的APIService
  • @FeignClient中的name属性表示的是服务提供方的名字
  • path属性表示的是服务的提供方的路径前缀

(6)服务的调用

package com.xiyou.feign.controller;

import com.xiyou.feign.service.FeignService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * @author
 */
@RestController
public class FeignController {

    @Autowired
    private FeignService feignService;

    @GetMapping("/testFeign")
    public String test() {
        String test = feignService.test();
        return test;
    }
}

  • 我们在这里直接将Service注入进来,然后以直接调用其方法就可以
  • 这就是Feign的注解 + 接口的方式进行远程调用

Feign的其他使用

Feign的自定义日志级别
  • 注意Feign的日志只对debug下起作用,所以设置Feign日志之前应该先设置log为debug模式
## “com.client.Client”为Feign接口的完整类名
## 将Feign接口的日志级别设置成DEBUG,因为Feign的logger.Level只对DEBUG做出响应
logging.level.com.xiyou.feign.service.FeignService = debug

1. 日志级别
(1)NONE:性能最佳,用于生辰,不记录任何日志。
(2)HEADERS:记录BASIC级别的基础上,记录请求和响应的header。
(3)FULL:比较适用于开发以及测试环境定位问题,记录和响应的header,body和元数据。
(4)BASIC:适用于生产环境追踪问题,仅仅记录请求方法,URL,响应状态代码以及执行时间。

2. 代码中如何实现

  • 在config类中写
  • 注意:不能写@Configuration,否则会全部的Feign都会生效
/**
 * 不加@Configuration 否则配置共享
 * @author
 */

public class FeignConfig {
    /**
     * 定义日志级别
     * @return
     */
    @Bean
    public Logger.Level feignLoggerLevel() {
        System.out.println("加载了.....................................");
        return Logger.Level.FULL;
    }
}

  • 在@FeignClient注解中加入Configuration属性指定该配置类
/**
 * 将feign需要的接口全部抽象出来
 * @FeignClient中的name是要调用的服务的名字(application-name)path是其宽窄路径中的宽路径
 * @author
 */
@FeignClient(name = "my-provider", configuration = FeignConfig.class, path = "/provider")
public interface FeignService extends OrderApi{
}

3. 在yml中配置

## “com.client.Client”为Feign接口的完整类名
## 将Feign接口的日志级别设置成DEBUG,因为Feign的logger.Level只对DEBUG做出响应
logging.level.com.xiyou.feign.service.FeignService = debug
## “my-provider”为feignName
feign.client.config.my-provider.loggerLevel= full
如何支持Feign的注解来替换SpringMVC的注解
  • 这个注解的意思就是我们Feign的@RequestLine注解,用该注解替换@RequestMappin注解
  • 比如:修改API中的Service API, @RequestLine(“GET /test”)
public interface OrderApi {

    /**
     *不认识@GetMapping 如果有参数记得传递,需要写全,不能省略
     * @return
     */
    @RequestLine("GET /test")
    public String test();

}

  • 如何让这个注解生效?

1. 代码配置

  • 修改FeignConfig
/**
 * 不加@Configuration 否则配置共享
 * @author
 */

public class FeignConfig {
    /**
     * 使用feign的注解代替springMVC @RequestLine
     * @return
     */
    @Bean
    public Contract feignContract() {
        return new Contract.Default();
    }
}

  • 在使用@FeignClient注解中的Configuration属性中指定该配置文件
/**
 * 将feign需要的接口全部抽象出来
 * @FeignClient中的name是要调用的服务的名字(application-name)path是其宽窄路径中的宽路径
 * @author
 */
@FeignClient(name = "my-provider", configuration = FeignConfig.class, path = "/provider")
public interface FeignService extends OrderApi{
}

2. yml文件中配置(yml文件方式配置后,代码中就不需要配置了)

feign.client.config.my-provider.contract=feign.Contract.Default
创建拦截器设置公用参数实现
  • 我们可以创建一个Feign的拦截器,在其中设置一些公用参数,比如,Token这样我们就不用在每一个调用方法中声明

1. 编写拦截器

/**
 * @author
 */
public class FeignIntercept implements RequestInterceptor{
    @Override
    public void apply(RequestTemplate requestTemplate) {
        System.out.println("自定义拦截器");
        requestTemplate.header("token", "This is zyf");
    }
}

2. 加入拦截器配置
(1)以代码的方式实现:

  • 配置类:
import com.xiyou.feign.intercept.FeignIntercept;
import feign.Contract;
import feign.Logger;
import feign.RequestInterceptor;
import org.springframework.context.annotation.Bean;

/**
 * 不加@Configuration 否则配置共享
 * @author
 */

public class FeignConfig {
    /**
     * 记载滴定仪拦截器
     * @return
     */
    @Bean
    public RequestInterceptor tulingInterceptor() {
        return new FeignIntercept();
    }
}

  • 在@FeignClient的Configuration属性上指定
/**
 * 将feign需要的接口全部抽象出来
 * @FeignClient中的name是要调用的服务的名字(application-name)path是其宽窄路径中的宽路径
 * @author
 */
@FeignClient(name = "my-provider", configuration = FeignConfig.class, path = "/provider")
public interface FeignService extends OrderApi{
}

我们无论是配置日志级别,拦截器,还是代替SpringMVC的注解,都不加@Configuration,避免使其全局生效,让所有的Feign共享这个配置

(2)在yml文件中完成拦截器配置,无需写配置代码

# 指明自定义拦截器地址
feign.client.config.my-provider.requestInterceptors[0]=com.xiyou.feign.intercept.FeignIntercept

3. 直接在服务的提供方可以取到这个拦截器中封装的token

  • 我们将token设置到了header中,去provider的header中取值
package com.xiyou.provider1.controller;

import org.springframework.web.bind.annotation.*;

/**
 * @author
 */
@RestController
@RequestMapping("/provider")
public class Provider1Controller {

    @RequestMapping("/test")
    public String test(@RequestHeader("token") String token) {
        System.out.println(token);
        return "provider-1";
    }

}

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值