声明式服务调用Feign


前言

例如:为什么用Feign?

一、Feign 概述

        1.1 为什么要用Feign

        当我们通过RestTemplate调用其它服务的API时,所需要的参数在请求URL中进行拼接,如果参数少的话或许我们还可以忍受,一旦有多个参数的话,这时拼接请求字符串就会效率低下,并且显得好傻。 所有Netflix为我们提供了 Feign

        1.2  什么是feign

         FeignSpring Cloud 提供的声明式,模块化HTTP客户端 , 它使得调用远程服务就像调用本地服务一样简单,只需要创建一个接口添加一个注解即可。

        Spring Cloud集成Feign并对其进行了增强,使Feign支持Spring MVC注解;Feign默认集成了Ribbon,所以Fegin默认实现负载均衡的效果。

二、Feign 入门

        2.1 入门案例

        创建工程:

         2.2.1 feign_parent

                pom.xml 依赖

  <dependencies>
            <!--Spring Boot-->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-dependencies</artifactId>
                <version>2.3.2.RELEASE</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
            <!--spring cloud Netflix-->
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>Hoxton.SR9</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
            <!--spring cloud 阿里巴巴-->
            <dependency>
                <groupId>com.alibaba.cloud</groupId>
                <artifactId>spring-cloud-alibaba-dependencies</artifactId>
                <version>2.2.6.RELEASE</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>

     2.2.2 feign_provider 服务提供者

        pom.xml 依赖

   <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!--nacos客户端-->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
        </dependency>
        <dependency>
            <groupId>com.bjpowernode</groupId>
            <artifactId>feign_interface</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
    </dependencies>

        application.yml 配置文件

server:
  port: 9090
spring:
  cloud:
    nacos:
      discovery:
        server-addr: 192.168.184.131 #nacos服务地址
  application:
    name: feign-provider #向注册中心注册的名字

         2.2.3 创建feign接口 feign_interface

        pom依赖

<dependencies>
        <dependency>
            <groupId>com.bjpowernode</groupId>
            <artifactId>springcloud_common</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
        <!--Spring Cloud OpenFeign Starter -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>

        feign接口类

import com.bjpowernode.pojo.User;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;

@FeignClient(value = "feign-provider") //要调用的服务
@RequestMapping("/provider")
public interface UserFeign {

    @RequestMapping("/getUserById/{id}") 
    public User getUserById(@PathVariable(value="id")Integer id);

 
}

        2.2.4 创建 消费者feign_consumer

        pom依赖

   <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>com.bjpowernode</groupId>
            <artifactId>springcloud_common</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
        </dependency>
        <!--feign接口-->
        <dependency>
            <groupId>com.bjpowernode</groupId>
            <artifactId>feign_interface</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
    </dependencies>

controller层

import com.bjpowernode.feign.UserFeign;
import com.bjpowernode.pojo.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

@RestController
@RequestMapping("/consumer")
public class ConsumerController {
    
    // 注入feign接口
    @Autowired
    private UserFeign userFeign;

    @RequestMapping("/getUserById/{id}")
    public User getUserById(@PathVariable Integer id){
        return userFeign.getUserById(id);
    }
}

         启动类

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


@SpringBootApplication
@EnableDiscoveryClient //向注册中心注册该服务,并可以获取到其他服务的调用地址
@EnableFeignClients//开启feign接口扫描
public class FeignConsumerApp {
    public static void main(String[] args) {
        SpringApplication.run(FeignConsumerApp.class,args);

    }
}

        测试

三、Feign原理

        3.1.将Feign接口注入到Spring容器中

        @EnableFeignClients注解开启Feign扫描,先调FeignClientsRegistrar.registerFeignClients()方法扫描@FeignClient注解的接口,再将这些接口注入到Spring IOC容器中,方便后续被调用。

 

         3.2 RequestTemplate封装请求信息

         SynchronousMethodHandler.invoke():

         当定义的的Feign接口中的方法被调用时,通过JDK的代理方式为Feign接口生成了一个动态代理类,当生成代理时,Feign会为每个接口方法创建一个RequestTemplate。该对象封装了HTTP请求需要的全部信息,如请url、参数,请求方式等信息都是在这个过程中确定的。

         3.3 发起请求

        SynchronousMethodHandler.executeAndDecode():

        发出请求:
        代理类会通过RequestTemplate创建Request,然后client(URLConnetct、HttpClient、OkHttp)使用Request发送请求

RestTemplate 是从 Spring3.0 开始支持的一个 HTTP 请求工具,它提供了常见的REST请求方案的模版,例如 GET 请求、POST 请求、PUT 请求、DELETE 请求

四、Feign参数传递

服务消费者

import com.bjpowernode.feign.UserFeign;
import com.bjpowernode.pojo.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

@RestController
@RequestMapping("/consumer")
public class ConsumerController {

    @Autowired
    private UserFeign userFeign;
    //restful
    @RequestMapping("/getUserById/{id}")
    public User getUserById(@PathVariable Integer id){
        return userFeign.getUserById(id);
    }
    // refin 路径拼接
    @RequestMapping("/deleteUserById")
    public User deleteUserById( Integer id ){
        return userFeign.deleteUserById(id);
    }
    //pojo参数
    @RequestMapping("/addUser")
    public User addUser(User user){
        return userFeign.addUser(user);
    }
}

feign接口

import com.bjpowernode.pojo.User;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;

@FeignClient(value = "feign-provider")
@RequestMapping("/provider")
public interface UserFeign {

    @RequestMapping("/getUserById/{id}") //restful风格 路径传参
    public User getUserById(@PathVariable(value="id")Integer id);

    @RequestMapping("/deleteUserById") // feign接口 路径拼接传参
    public User deleteUserById(@RequestParam("id") Integer id);

    @RequestMapping("/addUser")
    User addUser(@RequestBody User user); //pojo --->json
}

服务提供者:

controller层

import com.bjpowernode.pojo.User;
import com.bjpowernode.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

@RestController
@RequestMapping("/provider")
public class ProviderController {

    @Autowired
    private UserService userService;

    @RequestMapping("/getUserById/{id}")
    public User getUserById(@PathVariable Integer id){
        return userService.getUserById(id);
    }

    @RequestMapping("/deleteUserById")
    public User deleteUserById(@RequestParam Integer id){
        return userService.deleteUserById(id);
    }

    @RequestMapping("/addUser")
    public User addUser(@RequestBody User user){ // json--->pojo
        return userService.addUser(user);
    }
}

service层

import com.bjpowernode.pojo.User;

public interface UserService {
    User getUserById(Integer id);

    User deleteUserById(Integer id);

    User addUser(User user);
}
import com.bjpowernode.pojo.User;
import org.springframework.stereotype.Service;

@Service
public class UserServiceImpl implements UserService {
    @Override
    public User getUserById(Integer id) {
        return new User(id,"大傻子---1",18);
    }

    @Override
    public User deleteUserById(Integer id) {
        return  new User(id,"删除了大傻子---1",18);
    }

    @Override
    public User addUser(User user) {
        user.setName("新增了大傻子");
        return user;
    }
}

  • restful风格:

        feign接口:@PathVarible【拼接restful形式的url】

  • ? feign接口 :@RequestParam【拼接feign形式的url】

  • pojo参数

        provider: @RequestBody User user【获取请求体中的json串】

  五、feign优化

        feign为什么要优化,feign用的是HTTP请求,没有dubbo快

原因是feign可以优化

5.1 开启fegin日志

Feign        FeignFeign默认日志级别

  • NONE:不记录任何日志信息,这是默认值。

  • BASIC:仅记录请求的方法,URL以及响应状态码和执行时间

  • HEADERS:在BASIC的基础上,额外记录了请求和响应的头信息

  • FULL:记录所有请求和响应的明细,包括头信息、请求体、元数据。

 修改服务消费者配置文件:

server:
  port: 80
spring:
  cloud:
    nacos:
      discovery:
        server-addr: 192.168.184.131:8848 #注册服务的端口号
  application:
    name: feign-consumer #向注册中心注册的地址
feign:
  client:
    config:
     default:
        loggerLevel: full # 记录所有请求和响应的明细,包括头信息、请求体、元数据。
logging:
  level:
    com.bjpowernode.feign: debug #日志级别

 日志输出

 5.2 http连接池

两台服务器建立HTTP连接的过程涉及到多个数据包的交换,很消耗时间。采用HTTP连接池可以节约大量的时间提示吞吐量。

Feign的HTTP客户端支持3种框架:HttpURLConnection、HttpClient、OkHttp。

观察源码可以发现,Feign 默认是采用java.net.HttpURLConnection 的,每次请求都会建立、关闭连接。

为了性能考虑,我们可以引入httpclientokhttp作为底层的通信框架。

例如将 FeignHTTP 客户端工具修改为 HttpClient

 <dependency>
      <groupId>io.github.openfeign</groupId>
      <artifactId>feign-httpclient</artifactId>
 </dependency>
feign:
  client:
    config:
      default:
        loggerLevel: full # 记录所有请求和响应的明细,包括头信息、请求体、元数据。
  httpclient:
    enabled: true # 开启HTTP客户端HTTPClient 默认是开启的

修改之前

修改之后

5.3 gzip 压缩

        5.3.1什么是gzip压缩

        gzip 是一种数据格式,采用用 deflate 算法压缩 data;gzip 是一种流行的文件 压缩算法,应用十分广泛,尤其是在 Linux 平台。

        5.3.2 gzip压缩有什么功能

        当 Gzip 压缩到一个纯文本文件时,效果是非常明显的,大约可以减少 70% 以上的文件大小。

        5.3.3怎么使用gzip压缩

        在服务提供者配置文件配置

server:
  port: 80
  compression:
    enabled: true # 开启gzip压缩
    mime-types: text/html, text/xml, text/plain, text/css, text/javascript,application/javascript, application/json, application/xml # 默认有配置,可以不配

效果

5.4 fegin 超时

        Feign 底层内置了 Ribbon 框架,并且使用了 Ribbon 的请求连接超时时间和请求处理超时时间作为其超时时间,而 Ribbon 默认的请求连接超时时间和请求处理超时时间都是 1s,如下源码所示:

当我们调用Feign接口超过一秒就会报一下错误:

有两种方式可以解决超时:

第一种:是设置Ribbon超时时间

ribbon: # 设置超时的第一种方式
  ConnectTimeout: 5000 # 设置请求连接的超时时间
  ReadTimeout: 5000 #设置请求响应的超时方式

第二种:是设置Feign超时时间

feign:
  client:
    config:
      feign-provider: # 超时的第二种方式
        ConnectTimeout: 5000 # 设置请求连接的超时时间
        ReadTimeout: 5000 #设置请求响应的超时方式
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值