Spring Cloud之Feign

资料来源: <<重新定义Spring Cloud>>以及博客和官网

源码地址:https://gitee.com/08081/hello-springcloud

什么是feign ?

fegin是一种声明式 模板化的HTTP客户端(仅在consumer中使用)

1. 什么是声明式呢?

  • 声明式调用就像调用本地方法一样调用远程方法,无感知远程http请求
  • Spring Cloud的声明式调用,可以做到使用HTTP请求远程服务时就像调用本地方法一样的体验,开发者完全感知不到这是远程调用,更加不知道这是一个http请求
  • 像dubbo一样,consumer直接调用接口方法调用provider,而不需要通过常规的httpclient构造在解析返回数据
  • 解决了.开发者调用远程和调用本地一样,无需关心远程交互的细节,更无需关注分布式环境

2. 工作原理

  • 在开发微服务应用时,我们会在主程序入口添加@EnableFeignClients注解开启对FeginClient扫描加载处理,
  • 当程序启动时,会进行扫描,扫描所有@FeginClients的注解类,并将这些信息,注入到Spring IOC容器中. 当定义的fegin接口中的方法被调用时,通过JDK代理的方式,来生成具体的RequestTemplate,当生成代理时,Fegin会为每一个接口方法创建一个RequestTemplate对象,该对象封装了HTTP请求需要的全部信息,如请求参数,请求方法等信息都是在这个过程中确定的.
  • 然后由RequestTemplate生成request,然后把request交给client去处理,这里说的client 可以是jdk中的urlConnection apache 的httpclient 最后client被封装到LoadBalanceClient类,这个类接口ribbon负载均衡发起服务之间的调用

入门案例

我们在父工程下创建ch4-fegin 这个父工程在他下面创建 3个模块

1. book-api: 主要作为一个接口给别的服务依赖,包含 实体对象,和api

2. book-service: 主要是作为book-api 的实现类模块

3.book-consumer: 调用book服务的类

下面是代码:

先看book-api的代码

实体类:

/**
 * Created by xiaodao
 * date: 2019/7/17
 */
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class Book {

    private int id;
    private String name;

}

api:

package com.xiaodao.api;

import com.xiaodao.entity.Book;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;

import java.util.List;

/**
 * Created by xiaodao
 * date: 2019/7/17
 */
@RequestMapping("/book")
public interface BookApi {

    @GetMapping(value = "list")
    List<Book> findList();
}

pom:也是空的pom

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>ch4-fegin</artifactId>
        <groupId>com.xiaodao</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>ch4-book-api</artifactId>

</project>

ch4-book-service的代码:

pom:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>ch4-fegin</artifactId>
        <groupId>com.xiaodao</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>ch4-book-service</artifactId>

    <dependencies>
        <dependency>
            <groupId>com.xiaodao</groupId>
            <artifactId>ch4-book-api</artifactId>
            <version>${project.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>
    </dependencies>
</project

bootstrap.yml:

spring:
  application:
    name: book-service
eureka:
  client:
    service-url:
      defaultZone: http://127.0.0.1:8888/eureka/
  instance:
    prefer-ip-address: true
server:
  port: 8000

bookserviceImpl:

package com.xiaodao.service;

import com.xiaodao.api.BookApi;
import com.xiaodao.entity.Book;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.bind.annotation.ValueConstants;

import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;

/**
 * Created by xiaodao
 * date: 2019/7/17
 */
@RestController
@RequestMapping("book")
public class BookServiceImpl implements BookApi {
    @GetMapping(value = "list")
    @Override
    public List<Book> findList() {

        Book book =Book.builder().id(1).name("第一本书").build();
        System.out.println(book);
       List<Book> list =  new  CopyOnWriteArrayList<>();
       list.add(book);
       return list;
    }
}

bookApplicatiton

package com.xiaodao;

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

/**
 * Created by xiaodao
 * date: 2019/7/17
 */
@SpringBootApplication
public class BookApplication {
    public static void main(String[] args) {
        SpringApplication.run(BookApplication.class,args);
    }
}

ch4-book-consumer 的代码:

pom:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>ch4-fegin</artifactId>
        <groupId>com.xiaodao</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>ch4-book-consumer</artifactId>
    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>
        <dependency>
            <groupId>com.xiaodao</groupId>
            <artifactId>ch4-book-api</artifactId>
            <version>${project.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>
    </dependencies>

</project>

bootstrap.yml

spring:
  application:
    name: book-consumer
eureka:
  client:
    service-url:
      defaultZone: http://127.0.0.1:8888/eureka/
  instance:
    prefer-ip-address: true
server:
  port: 8001

service:就是继承了book-api service的接口

name 是 book-service 的实例名

package com.xiaodao.service;

import com.xiaodao.api.BookApi;
import org.springframework.cloud.openfeign.FeignClient;

/**
 * Created by xiaodao
 * date: 2019/7/17
 */
@FeignClient(name = "book-service")
public interface BookService extends BookApi {
}

controller.调用service.->通过Fegin->book-service

package com.xiaodao.controller;

import com.xiaodao.entity.Book;
import com.xiaodao.service.BookService;
import lombok.AllArgsConstructor;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;

/**
 * Created by xiaodao
 * date: 2019/7/17
 */
@RestController
@RequestMapping("/book")
@AllArgsConstructor
public class BookController {

    private BookService bookService;

    @GetMapping("/findList")
    public List<Book> findList(){
      return   bookService.findList();
    }
}

最后我们启动eurekasever  book-service book-consumer 我们可以访问eurekaService

我们去调用book-consumer里的controller方法,看看能不能调用book-service的实现类:

以上就是一个简单的hello word 实现的方法.

传参数

1.单个参数时候没有问题的

2.多个参数用post方法 (@requestBody Book book) feign是不支持 get方法传递实体对象的

3.如果想用get方式传实体对象需要将feign默认的URLConnection连接换成httpClient

fegin 性能优化

gzip压缩

  1. gzip介绍:gzip是一种数据格式,采用defalte算法压缩data gzip是一种流行的文件压缩算法,应用十分广泛,尤其是在liunx平台
  2. gzip能力: 当gzip压缩一个纯文本文件时,效果是非常明显,大约可以减少70%以上的文件大小
  3. gzip作用: 网络数据通过压缩之后实际上降低了网络传输的字节数,最明显的好处时候加快网页的加载速度,网页加载速度加快的好处不言而喻,除了节省流量以外,改善用户体验外,另一个潜在的好处是Gzip与搜索引擎的抓取工具有着更好的关系,例如:Google 就可以通过读取Gzip文件来比普通手工抓取 更快的检索网页

HTTP协议中关于压缩传输的规定:

1. 客户端向服务器请求中带有:Accept-Encoding:Gzip,deflate字段,向服务器表示,客户端支持的压缩格式(gzip或者 deflate),如果不发送消息头,服务器是不会压缩的

2. 服务端收到请求之后,如果发现请求中含有Accept-Encoding字段,并且支持该类型的压缩,就对响应报文压缩之后在返回客户端,并且携带Content-Encoding:gzip消息头,表示报文是经过压缩的

3. 客户端接到服务器的响应之后,先判断是否有Content-Encoding消息头,如果有,按格式解压报文,否者按正常处理

我们在baidu搜索下发送一个消息

看到是百度服务器是经过压缩的

然后我们在看下我们刚才的代码.

可以看出是没有经过压缩的.

那我们使用fegin怎么做压缩呢?

spring:
  application:
    name: book-consumer
eureka:
  client:
    service-url:
      defaultZone: http://127.0.0.1:8888/eureka/
  instance:
    prefer-ip-address: true
server:
  port: 8001
### Feign 配置
feign:
  compression:
    request:
      # 开启请求压缩
      enabled: true
      # 配置压缩的 MIME TYPE
      mime-types: text/xml,application/xml,application/json
      # 配置压缩数据大小的下限
      min-request-size: 2048
    response:
      # 开启响应压缩
      enabled: true

这里有坑,当我在book-consumer 配置了压缩的时候,我发现我们请求是配置了gzip压缩了但是服务器并没有压缩.为什么呢?是当我们使用浏览器访问的时候没有压缩.

当我们在配置spring boot压缩之后:在看控制台就显示服务器也压缩过了

spring:
  application:
    name: book-consumer
eureka:
  client:
    service-url:
      defaultZone: http://127.0.0.1:8888/eureka/
  instance:
    prefer-ip-address: true
server:
  port: 8001
  #配置spring boot 压缩
  compression:
    enabled: true
#    mime-types:
### Feign 配置
feign:
  compression:
    request:
      # 开启请求压缩
      enabled: true
      # 配置压缩的 MIME TYPE
      mime-types: text/xml,application/xml,application/json
      # 配置压缩数据大小的下限
      min-request-size: 2048
    response:
      # 开启响应压缩
      enabled: true

也可以不写fegin的压缩,直接 就用springboot的压缩就好了.

HTTP连接池优化

为什么http连接池能提升性能?

http的背景原理

1.俩台服务器建立http的过程是很富在的工程,涉及到了多个数据包的交换,并且也耗费时间

2. http连接需要3次握手4次分手开销很大,这一开销对于大量比较小的http消息来说更大

优化解决方案

1.如果我们采用http连接池,节约了大量的3次握手4次分手,这样能大大的提高性能

2.fegin的http客户端支持3种框架 : HttpURLConnection httpclient okhttp 默认是HTTPURLConnection.

3.传统的HTTPURLConnection是JDK自带的,并不支持线程池,如果要实现连接池的机制,还需要自己管理连接对象,对于网络请求这种底层相对复杂的操作,如果有可用的方案,也没有必要自己去实现

4.HttpClient相比于JDK自带的URLConnection,它封装了访问http的请求头,参数,内容体响应等等,它不仅是客户端发送http请求变的容易,而且方便开发人员测试接口(基于http协议), 也提高的效率,也方便提高代码的健壮性,另外高并发大量请求网络的时候,还是使用"连接池"提升吞吐量的.

使用httpClient连接池

1.在book-consumer 的pom中:加入

     <!-- https://mvnrepository.com/artifact/org.apache.httpcomponents/httpclient -->
        <dependency>
            <groupId>org.apache.httpcomponents</groupId>
            <artifactId>httpclient</artifactId>
            <version>4.5.5</version>
        </dependency>
        <dependency>
            <groupId>io.github.openfeign</groupId>
            <artifactId>feign-httpclient</artifactId>
            <version>9.5.1</version>
        </dependency>

2.在bootstrap.yml中加入

spring:
  application:
    name: book-consumer
eureka:
  client:
    service-url:
      defaultZone: http://127.0.0.1:8888/eureka/
  instance:
    prefer-ip-address: true
server:
  port: 8001
  #配置spring boot 压缩
  compression:
    enabled: true
#    mime-types:
### Feign 配置
feign:
  compression:
    request:
      # 开启请求压缩
      enabled: true
      # 配置压缩的 MIME TYPE
      mime-types: text/xml,application/xml,application/json
      # 配置压缩数据大小的下限
      min-request-size: 2048
    response:
      # 开启响应压缩
      enabled: true
  #启动httpclient
  httpclient:
    enabled: true

日志输出:

bootstrap.yml

logging:
  level:
    #这个是使用feign的那个接口类
    com:
      xiaodao:
        service:
          BookService: DEBUG
@Configuration
public class LoggerConfig {
    @Bean
    public Logger.Level feignLoggerLevel(){
        return Logger.Level.FULL;
    }
}

Feign的优化配置

feign的负载均衡,是使用ribbon,就需要我们对ribbon的配置进行配置.

ribbon的配置分为全局配置,和局部配置.

先来看全局配置:

在consumer的bootstrap.yml中加入:

#全局配置
ribbon:
  #请求连接时间
  ConnectTimeout: 5000
  #请求处理时间
  ReadTimeout: 5000

在book-service的controller让他睡眠:

@RestController
@RequestMapping("book")
public class BookServiceImpl implements BookApi {
    @GetMapping(value = "list")
    @Override
    public List<Book> findList() {
        try {
            TimeUnit.SECONDS.sleep(6);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        Book book =Book.builder().id(1).name("第一本书").build();
        System.out.println(book);
       List<Book> list =  new  CopyOnWriteArrayList<>();
       list.add(book);
       return list;
    }

}

这个时候就超时了.

局部配置

但是我们一般不用全局配置,因为你的cosumer一般情况会调用很多provider,他们的情况不一.

#请求连接的超时时间
book-service.ribbon.ConnectTimeout=3000

这里有很多配置,有重试等可以去官网查看.

原文地址:https://www.cnblogs.com/bj-xiaodao/p/11201637.html

时间: 2024-10-08 22:32:18

Spring Cloud之Feign的相关文章

spring cloud 使用feign 遇到问题

spring cloud 使用feign 项目的搭建 在这里就不写了,本文主要讲解在使用过程中遇到的问题以及解决办法 1:示例 1 @RequestMapping(value = "/generate/password", method = RequestMethod.POST) 2 KeyResponse generatePassword(@RequestBody String passwordSeed); 3 在这里 只能使用 @RequestMapping(value = &qu

spring cloud 之 Feign 使用HTTP请求远程服务

一.Feign 简介 在spring Cloud Netflix栈中,各个微服务都是以HTTP接口的形式暴露自身服务的,因此在调用远程服务时就必须使用HTTP客户端.我们可以使用JDK原生的URLConnection.Apache的Http Client.Netty的异步HTTP Client, Spring的RestTemplate.但是,用起来最方便.最优雅的还是要属Feign了. Feign是一种声明式.模板化的HTTP客户端.在Spring Cloud中使用Feign, 我们可以做到使用

解决Spring Cloud中Feign/Ribbon第一次请求失败的方法

前言 在Spring Cloud中,Feign和Ribbon在整合了Hystrix后,可能会出现首次调用失败的问题,要如何解决该问题呢? 造成该问题的原因 Hystrix默认的超时时间是1秒,如果超过这个时间尚未响应,将会进入fallback代码.而首次请求往往会比较慢(因为Spring的懒加载机制,要实例化一些类),这个响应时间可能就大于1秒了.知道原因后,我们来总结一下解决放你. 解决方案有三种,以feign为例. 方法一 ? 1 hystrix.command.default.execut

Spring Cloud 之 Feign

新建Spring Boot工程,命名为feign 1.pom.xml添加依赖 <?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation=&quo

spring cloud学习--Feign

Feign简介: Feign是一个声明式的Web服务客户端,使用Feign可使得Web服务客户端的写入更加方便.它具有可插拔注释支持,包括Feign注解和JAX-RS注解.Feign还支持可插拔编码器和解码器.Spring Cloud增加了对Spring MVC注释的支持,并HttpMessageConverters在Spring Web中使用了默认使用的相同方式.Spring Cloud集成了Ribbon和Eureka,在使用Feign时提供负载平衡的http客户端.Fegin对Robbin进

Spring Cloud之Feign客户端调用工具

feign介绍 Feign客户端是一个web声明式http远程调用工具,提供了接口和注解方式进行调用. Spring Cloud 支持 RestTemplate  Fetin Feign客户端实际开发中用的最多 ,易读性比较强. 主要调用部分: pom: <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

解决Spring Cloud中Feign第一次请求失败的问题

在Spring Cloud中,Feign和Ribbon在整合了Hystrix后,可能会出现首次调用失败的问题 com.netflix.hystrix.exception.HystrixTimeoutException: null at com.netflix.hystrix.AbstractCommand$HystrixObservableTimeoutOperator$1.run(AbstractCommand.java:1142) ~[hystrix-core-1.5.18.jar:1.5.

spring cloud之Feign的使用

原始的调用客户端的方式是通过注入restTemplate的方式 restTemplate.getForObject("http://CLIENT/hello", String.class) 通过feign的方式 配置消费者项目cloud-consume pom.xml 依赖jar <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-clo

Spring Cloud之Feign 转发请求头(header参数)

在做接口请求时,我们经常会在header头中增加一些鉴权信息,如token 或 jwt,那么在通过fegin从A server去调用B server的接口时,如果B server的接口需要header信息,我们需要将A sever获取的header转发到B上. 解决方式 我们需要实现Feign提供的一个接口RequestInterceptor @Configuration public class FeignConfiguration implements RequestInterceptor{