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 技术开发时的应用部署结构
在使用 Openfeign 技术开发 Spring Cloud 微服务时,需要先抽取要注册发布的服务标准,将这套标准通过接口的形式定义出来
在 Application Service 端开发中,依赖抽取的服务标准接口工程,并对接口给予实现
予实现。 在 Application Client 端开发中,依赖抽取的服务标准接口工程,并应用接口信息和 Openfeign 技术,实现远程服务的调用
在整体微服务开发中,Eureka Server 作为注册中心必不可少,注册中心的作用不变,仍旧是注册和发现服务
2.Openfeign 的请求参数处理
2.1 创建 Eureka Client 工程
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
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
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 协议中的压缩传输简介
第一:客户端向服务器请求头中带有: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)