SpringCloud OpenFeign

本文涉及的产品
传统型负载均衡 CLB,每月750个小时 15LCU
EMR Serverless StarRocks,5000CU*H 48000GB*H
应用型负载均衡 ALB,每月750个小时 15LCU
简介: SpringCloud OpenFeign

1.Openfeign 简介

openFeign是对ribbon做了进一步的封装,OpenFeign是Spring Cloud 在Feign的基础上支持了Spring

MVC的注解,如@RequesMapping等等。通过动态代理的方式产生实现类,实现类中做负载均衡并调用其他服务。


Openfeign 是一种声明式、模板化的 HTTP 客户端(仅在 Application Client 中使用)。声明式调用是指,就像调用本地方法一样调用远程方法,无需感知操作远程 http 请求。

Spring Cloud 的声明式调用, 可以做到使用 HTTP 请求远程服务时能就像调用本地方法一样的体验,开发者完全感知不到这是远程方法,更感知不到这是个 HTTP 请求。

Openfeign 的应用,让 Spring Cloud 微服务调用像 Dubbo 一样,Application Client 直接通过接口方法远程调用 Application Service,而不需要通过常规的 RestTemplate 构造请求再解析返回数据。它解决了让开发者调用远程接口就跟调用本地方法一样,无需关注与远程的交互细节,更无需关注分布式环境开发。


1.1 使用 Feign 技术开发时的应用部署结构

20210116232238353.png


在使用 Openfeign 技术开发 Spring Cloud 微服务时,需要先抽取要注册发布的服务标准,将这套标准通过接口的形式定义出来

在 Application Service 端开发中,依赖抽取的服务标准接口工程,并对接口给予实现

予实现。 在 Application Client 端开发中,依赖抽取的服务标准接口工程,并应用接口信息和 Openfeign 技术,实现远程服务的调用

在整体微服务开发中,Eureka Server 作为注册中心必不可少,注册中心的作用不变,仍旧是注册和发现服务


2.Openfeign 的请求参数处理

2.1 创建 Eureka Client 工程

20210116232248714.jpg


2.1.1 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">
  <modelVersion>4.0.0</modelVersion>
  <packaging>pom</packaging>
  <modules>
    <module>serviceapi</module>
  </modules>
  <parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.2.2.RELEASE</version>
  </parent>
  <groupId>com.dqcgm</groupId>
  <artifactId>cloud</artifactId>
  <version>1.0-SNAPSHOT</version>
  <dependencyManagement>
    <dependencies>
      <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-dependencies</artifactId>
        <version>Hoxton.SR1</version>
        <type>pom</type>
        <scope>import</scope>
      </dependency>
    </dependencies>
  </dependencyManagement>
</project>

2.2 创建 Service API 服务标准 Module

20210116232718414.jpg


2.3 开发服务标准 - Service API

2.3.1 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>cloud</artifactId>
    <groupId>com.dqcgm</groupId>
    <version>1.0-SNAPSHOT</version>
  </parent>
  <modelVersion>4.0.0</modelVersion>
  <artifactId>serviceapi</artifactId>
  <dependencies>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
  </dependencies>
</project>

2.3.2 测试自定义参数类型

package com.dqcgm.entity;
import java.io.Serializable;
import java.util.Objects;
public class User implements Serializable {
  private Integer id;
  private String username;
  private String remark;
  public User(){}
  @Override
  public boolean equals(Object o) {
    if (this == o) return true;
    if (o == null || getClass() != o.getClass()) return false;
    User user = (User) o;
    return Objects.equals(id, user.id) && Objects.equals(username, user.username) && Objects.equals(remark, user.remark);
  }
  @Override
  public int hashCode() { 
    return Objects.hash(id, username, remark); 
  }
  public Integer getId() { 
    return id; 
  }
  public void setId(Integer id) { 
    this.id = id; 
  }
  public String getUsername() { 
    return username; 
  }
  public void setUsername(String username) { 
    this.username = username; 
  }
  public String getRemark() { 
    return remark; 
  }
  public void setRemark(String remark) { 
    this.remark = remark; 
  }
}

2.3.3 服务接口定义

package com.dqcgm.serviceapi;
import com.dqcgm.entity.User;
import org.springframework.web.bind.annotation.*;
import java.util.List;
public interface FirstServiceAPI {
  //测试 GET 请求的方法
  //请求不传递任何的参数
  @RequestMapping(value="/test", method= RequestMethod.GET)
  public List<String> testFeign();
  //测试 GET 请求传递一个普通的参数。 /get?id=xxx&name=yyy
  //在为 Feign 定义服务标准接口的时候,处理请求参数的方法参数,必须使用 @RequestParam 注解描述
  //且,无论方法参数名和请求参数名是否一致,都需要定义@RequestParam 注解的 value/name 属性。
  @GetMapping(value="/get")
  public User getMultiParams(@RequestParam(value = "id") Integer id, @RequestParam("name") String name);
  //测试使用 POST 请求传递普通参数
  //如果使用 POST 方式发起请求,传递多个普通参数,是使用请求头传递的参数。可以使用@RequestParam 注解来处理请求参数
  @PostMapping(value="/post")
  public User postMultiParams(@RequestParam(value = "id") Integer id, @RequestParam("name") String name);
  //使用 POST 请求传递自定义类参数
  //必须使用@RequestBody 处理。
  @PostMapping(value="/postObjectParam")
  public User postObjectParam(@RequestBody User pojo);
}

2.4 创建 Application Service 服务提供者 Module

2.5 开发服务提供者 - Application Service

2.5.1 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>cloud</artifactId> 
    <groupId>com.dqcgm</groupId> 
    <version>1.0-SNAPSHOT</version>
  </parent> 
  <modelVersion>4.0.0</modelVersion>
  <artifactId>openfeignservice</artifactId> 
  <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>com.dqcgm</groupId> 
      <artifactId>serviceapi</artifactId> 
      <version>1.0-SNAPSHOT</version>
    </dependency>
  </dependencies>
</project>

2.5.2 服务标准实现

package com.dqcgm.controller;
import com.dqcgm.entity.User; 
import com.dqcgm.serviceapi.FirstServiceAPI; 
import org.springframework.web.bind.annotation.RestController;
import java.util.Arrays; 
import java.util.List;
//对外提供服务的 Application Service。
//不能随便的定义服务。如果想让 Application Client 可以通过 Openfeign 技术访问当前类型提供的服务
//则必须遵循服务标准 - Service API
@RestController
public class OpenfeignServiceController implements FirstServiceAPI {
  @Override
  public List<String> testFeign() { 
    return Arrays.asList("测试 Openfeign", "此为返回结果"); 
  }
  @Override
  public User getMultiParams(Integer id, String name) {
    System.out.println("getMultiParams method run, parameters is [ id : " + id + " ; name : " + name + " ]");
    User user = new User(); 
    user.setId(id); 
    user.setUsername(name); 
    return user;
  }
  @Override
  public User postMultiParams(Integer id, String name) {
    System.out.println("postMultiParams method run, parameters is [ id : " + id + " ; name : " + name + " ]");
    User user = new User();
    user.setId(id); 
    user.setUsername(name); 
    return user;
  }
  @Override
  public User postObjectParam(User pojo) {
    System.out.println("postObjectParam method run, parameters is [ " + pojo + " ]");
    return pojo;
  }
}

2.5.3 配置文件 application.yml

server: 
  port: 8081
spring: 
  application: 
    name: openfeign-service
eureka: 
  client: 
    service-url: 
      - http://localhost:8761/eureka/

2.5.4 启动类

package com.dqcgm;
import org.springframework.boot.SpringApplication; 
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication 
public class OpenfeignServiceApp {
  public static void main(String[] args) { 
    SpringApplication.run(OpenfeignServiceApp.class, args); 
  }
}

2.6 创建 Application Client 服务消费者 Module

2021011623265926.jpg


2.7 开发服务消费者 - Application Client

2.7.1 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>cloud</artifactId> 
    <groupId>com.dqcgm</groupId> 
    <version>1.0-SNAPSHOT</version>
  </parent>
  <modelVersion>4.0.0</modelVersion>
  <artifactId>openfeignclient</artifactId>
  <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>
    <dependency>
      <groupId>com.dqcgm</groupId> 
      <artifactId>serviceapi</artifactId> 
      <version>1.0-SNAPSHOT</version>
    </dependency>
  </dependencies>
</project>

2.7.2 本地服务接口

本地接口,继承服务标准接口 在接口上增加注解@FeignClient,代表当前接口是一个 Openfeign 技术中的服务消费端属性

name|value - 代表当前的 FeignClient 在请求 application service的时候,是访问哪一个服务所谓的哪一个服务,就是 application service 全局配置文件中的 spring.application.name 属性值

package com.dqcgm.service;
import com.dqcgm.serviceapi.FirstServiceAPI; 
import org.springframework.cloud.openfeign.FeignClient;
@FeignClient("openfeign-service")
public interface FirstClientService extends FirstServiceAPI {}

2.7.3 控制器开发

package com.dqcgm.controller;
import com.dqcgm.entity.User; 
import com.dqcgm.service.FirstClientService; 
import org.springframework.beans.factory.annotation.Autowired; 
import org.springframework.web.bind.annotation.GetMapping; 
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
@RestController
public class FirstClientController {
  @Autowired 
  private FirstClientService firstClientService;
  @GetMapping(value="/test") 
  public List<String> testFeign() { 
    return this.firstClientService.testFeign(); 
  }
  @GetMapping(value="/get") 
  public User getMultiParams(Integer id, String name) { 
    return this.firstClientService.getMultiParams(id, name); 
  }
  @GetMapping(value="/post") 
  public User postMultiParams(Integer id, String name) { 
    return this.firstClientService.postMultiParams(id, name); 
  }
  @GetMapping(value="/postObjectParam") 
  public User postObjectParam(User pojo) { 
    return this.firstClientService.postObjectParam(pojo); 
  }
}

2.7.4 配置文件 application.yml

server: 
  port: 8082
spring: 
  application: 
    name: openfeign-client
eureka: 
  client: 
    service-url: 
      - http://localhost:8761/eureka/

2.7.5 启动类

package com.dqcgm;
import org.springframework.boot.SpringApplication; 
import org.springframework.boot.autoconfigure.SpringBootApplication; 
import org.springframework.cloud.openfeign.EnableFeignClients;
//@EnableFeignClients - 描述当前应用是一个使用 Openfeign 技术开发的服务消费端。
//属性 backPackage - 扫描的包,即使用@FeignClient 描述的接口所在包。此属性可省略,默认扫描当前工程所有包
@SpringBootApplication 
@EnableFeignClients(basePackages = "com.dqcgm.service") 
public class OpenfeignClientApp {
  public static void main(String[] args) { 
    SpringApplication.run(OpenfeignClientApp.class, args); 
  } 
}

2.8 参数处理简单总结

在 Openfeign 处理远程服务调用时,传递参数是通过 HTTP 协议传递的,参数存在的位置是请求头或请求体中。请求头传递的参数必须依赖@RequestParam 注解来处理请求参数,请求体传递的参数必须依赖@RequestBody 注解来处理请求参数

3.Openfeign 的性能优化

3.1 GZIP 简介

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

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

gzip 作用:网络数据经过压缩后实际上降低了网络传输的字节数,最明显的好处就是可以加快网页加载的速度。网页加载速度加快的好处不言而喻,除了节省流量,改善用户的浏览体验外,另一个潜在的好处是 Gzip 与搜索引擎的抓取工具有着更好的关系。例如 Google 就可以通过直接读取 gzip 文件来比普通手工抓取更快地检索网页。

3.2 HTTP 协议中的压缩传输简介

202010240947564.png


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

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

第三:客户端接收到响应之后,先判断是否有 Content-Encoding 消息头,如果有,按该格式解压报文。否则按正常报文处理。

3.3 在 Openfeign 技术中应用 GZIP 压缩

在 Spring Cloud 微服务体系中,一次请求的完整流程如下:


在整体流程中,如果使用 GZIP 压缩来传输数据,涉及到两次请求-应答。而这两次请求-应答的连接点是 Application Client,那么我们需要在 Application Client 中配置开启 GZIP 压缩,来实现压缩数据传输

3.3.1 只配置 Openfeign 请求-应答中的 GZIP 压缩

只开启 Feign 请求-应答过程中的 GZIP,也就是浏览器-Application Client 之间的请求应答不开启 GZIP 压缩

在全局配置文件中,使用下述配置来实现 Openfeign 请求-应答的 GZIP 压缩

server: 
  port: 8082
spring: 
  application: 
    name: openfeign-client
eureka:
  client: 
    service-url:
      - http://localhost:8761/eureka/
feign: 
  compression: 
    request:
      # 开启请求 GZIP
      enabled: true
      # 设置支持 GZIP 压缩的 MIME 类型,即请求/响应类型。
      mime-types:
  csdn      - application/xml 
        - application/json
      # 配置启动压缩数据的最小阀值,单位字节。默认为 2048
      min-request-size: 512
    # 开启响应 GZIP
    response: 
      enabled: true

3.3.2 配置全局 GZIP 压缩

在全局配置文件中配置下述内容,来开启所有请求-应答中的 GZIP 压缩,这里使用的是 Spring Boot 中的 GZIP 技术。

在 Spring Boot 中已经集成了 GZIP 压缩技术,并对所有的请求-应答实现 GZIP 数据压缩。工程中已经依赖了 Spring Boot 技术,所以在配置文件中可以开启 Spring Boot 中的 GZIP 压缩技术,对完整流程中所有相关的请求-应答开启 GZIP 压缩。

server:
  port: 8082 
  compression:
    # 开启 GZIP
    enabled: true
    # 设置支持 GZIP 压缩的 MIME 类型,即请求/响应类型
    mime-types:
      - application/json 
      - application/xml 
      - text/html 
csdn    - text/plain
    # 配置启动压缩数据的最小阀值,单位字节。默认为 2048
    min-response-size: 512
spring:
  application: 
    name: openfeign-client
eureka: 
  client: 
    service-url: 
    - http://localhost:8761/eureka/
feign: 
  compression:
    request:
      # 开启请求 GZIP
      enabled: true
      # 设置支持 GZIP 压缩的 MIME 类型,即请求/响应类型
      mime-types:
  csdn      - application/xml 
        - application/json
      # 配置启动压缩数据的最小阀值,单位字节。默认为 2048
      min-request-size: 512
    # 开启响应 GZIP
    response:
      enabled: true

4.配置 Openfeign 负载均衡请求超时时间

Openfeign 技术底层是通过 Ribbon 技术实现的,那么在负载均衡和超时时间配置上,主要对 Ribbon 的配置。具体配置如下:

4.1 超时时间配置

ribbon:
  # 请求连接的超时时间,单位毫秒,默认的时间为 1 秒
  ConnectTimeout: 1000
  # 请求处理的超时时间,单位毫秒,默认的时间为 1 秒
  ReadTimeout: 1000

4.2 负载均衡配置

# 设置负载均衡策略。openfeign-service 为设置负载均衡的服务名称
openfeign-service:
  ribbon:
    NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule

5 日志记录

• Feign 只能记录 debug 级别的日志信息。

# 设置当前的日志级别 debug,feign只支持记录debug级别的日志
logging:
  level:
    com.itheima: debug

• 定义Feign日志级别Bean


FeignLogConfig

package com.itheima.consumer.config;
import feign.Logger;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class FeignLogConfig {
    /*
        NONE,不记录
        BASIC,记录基本的请求行,响应状态码数据
        HEADERS,记录基本的请求行,响应状态码数据,记录响应头信息
        FULL;记录完成的请求 响应数据
     */
    @Bean
    public Logger.Level level(){
        return Logger.Level.FULL;
    }
}

• 启用该Bean:


GoodsFeignClient

@FeignClient(value = "FEIGN-PROVIDER",configuration = FeignLogConfig.class)
相关实践学习
SLB负载均衡实践
本场景通过使用阿里云负载均衡 SLB 以及对负载均衡 SLB 后端服务器 ECS 的权重进行修改,快速解决服务器响应速度慢的问题
负载均衡入门与产品使用指南
负载均衡(Server Load Balancer)是对多台云服务器进行流量分发的负载均衡服务,可以通过流量分发扩展应用系统对外的服务能力,通过消除单点故障提升应用系统的可用性。 本课程主要介绍负载均衡的相关技术以及阿里云负载均衡产品的使用方法。
目录
相关文章
|
5月前
|
负载均衡 Java API
Java一分钟之-Spring Cloud OpenFeign:声明式服务调用
【6月更文挑战第9天】Spring Cloud OpenFeign是声明式服务调用库,简化了微服务间调用。通过动态代理,它允许开发者用Java接口调用HTTP服务,支持服务发现、负载均衡。本文介绍了OpenFeign的基本概念,展示了如何添加依赖、开启客户端和定义服务接口。还讨论了接口调用失败、超时重试和日志配置等问题及其解决方案,并提供了自定义Feign配置的代码示例。通过学习,读者可以更好地在微服务架构中使用OpenFeign进行服务通信。
314 4
|
5月前
springCloud之服务降级熔断Hystrix、OpenFeign
springCloud之服务降级熔断Hystrix、OpenFeign
327 0
|
1月前
|
负载均衡 Java API
【Spring Cloud生态】Spring Cloud Gateway基本配置
【Spring Cloud生态】Spring Cloud Gateway基本配置
37 0
|
3月前
|
Java Spring
【Azure Spring Cloud】Spring Cloud Azure 4.0 调用Key Vault遇见认证错误 AADSTS90002: Tenant not found.
【Azure Spring Cloud】Spring Cloud Azure 4.0 调用Key Vault遇见认证错误 AADSTS90002: Tenant not found.
|
3月前
|
Java Spring 容器
【Azure Spring Cloud】在Azure Spring Apps上看见 App Memory Usage 和 jvm.menory.use 的指标的疑问及OOM
【Azure Spring Cloud】在Azure Spring Apps上看见 App Memory Usage 和 jvm.menory.use 的指标的疑问及OOM
|
3月前
|
存储 Java Spring
【Azure Spring Cloud】Azure Spring Cloud服务,如何获取应用程序日志文件呢?
【Azure Spring Cloud】Azure Spring Cloud服务,如何获取应用程序日志文件呢?
|
3月前
|
SQL Java 数据库连接
【Azure Spring Cloud】Azure Spring Cloud connect to SQL using MSI
【Azure Spring Cloud】Azure Spring Cloud connect to SQL using MSI
|
3月前
|
Java 开发工具 Spring
【Azure Spring Cloud】使用azure-spring-boot-starter-storage来上传文件报错: java.net.UnknownHostException: xxxxxxxx.blob.core.windows.net: Name or service not known
【Azure Spring Cloud】使用azure-spring-boot-starter-storage来上传文件报错: java.net.UnknownHostException: xxxxxxxx.blob.core.windows.net: Name or service not known
|
3月前
|
NoSQL Java Redis
【Azure Spring Cloud】Java Spring Cloud 应用部署到Azure上后,发现大量的 java.lang.NullPointerException: null at io.lettuce.core.protocol.CommandHandler.writeSingleCommand(CommandHandler.java:426) at ... 异常
【Azure Spring Cloud】Java Spring Cloud 应用部署到Azure上后,发现大量的 java.lang.NullPointerException: null at io.lettuce.core.protocol.CommandHandler.writeSingleCommand(CommandHandler.java:426) at ... 异常
|
3月前
|
Java Spring
【Azure 应用服务】记一次Azure Spring Cloud 的部署错误 (az spring-cloud app deploy -g dev -s testdemo -n demo -p ./hellospring-0.0.1-SNAPSHOT.jar --->>> Failed to wait for deployment instances to be ready)
【Azure 应用服务】记一次Azure Spring Cloud 的部署错误 (az spring-cloud app deploy -g dev -s testdemo -n demo -p ./hellospring-0.0.1-SNAPSHOT.jar --->>> Failed to wait for deployment instances to be ready)