文章目录
一、 Openfeign简介
Openfeign是一种声明式、模板化的HTTP客户端(仅在Application Client中使用)。声明式调用是指,就像调用本地方法一样调用远程方法,无需感知操作远程http请求。
Spring Cloud的声明式调用, 可以做到使用 HTTP请求远程服务时能就像调用本地方法一样的体验,开发者完全感知不到这是远程方法,更感知不到这是个HTTP请求。Openfeign的应用,让Spring Cloud微服务调用像Dubbo一样,Application Client直接通过接口方法远程调用Application Service,而不需要通过常规的RestTemplate构造请求再解析返回数据。它解决了让开发者调用远程接口就跟调用本地方法一样,无需关注与远程的交互细节,更无需关注分布式环境开发。
1 使用Feign技术开发时的应用部署结构
二、 Openfeign的实现
1 创建 springcloudopenfeign 项目
1.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="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>feigncommons</module>
<module>feignapi</module>
<module>feignservice</module>
<module>feignclient</module>
</modules>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.2.RELEASE</version>
</parent>
<groupId>com.bjsxt</groupId>
<artifactId>springcloudopenfeign</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 创建 feigncommons 子模块
2.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="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>springcloudopenfeign</artifactId>
<groupId>com.bjsxt</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>feigncommons</artifactId>
</project>
2.2 com.bjsxt.feign.entity 创建实体类 User
package com.bjsxt.feign.entity;
import java.io.Serializable;
import java.util.Objects;
public class User implements Serializable {
private Integer id;
private String name;
private String password;
public User(){}
//省略get set toString equals方法
3 创建 feignapi 子模块
3.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="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>springcloudopenfeign</artifactId>
<groupId>com.bjsxt</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>feignapi</artifactId>
<dependencies>
<dependency>
<groupId>com.bjsxt</groupId>
<artifactId>feigncommons</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
</project>
3.2 UserServiceAPI 接口
package com.bjsxt.feign.api;
import com.bjsxt.feign.entity.User;
import org.springframework.web.bind.annotation.*;
import java.util.Map;
/**
* 类型命名随意
* 类型是用于约束微服务特性的。
* 在约束请求参数的时候,Openfeign技术要特殊的限制要求:
* 1、 简单类型参数,必须使用@RequestParam注解描述,且需要提供请求参数命名。
* 2、 自定义类型参数,必须使用@RequestBody注解描述,且自定义类型参数数量唯一。
* 就是参数表中只有唯一的参数,且是自定义类型参数。
*
* Application Service需要提供什么服务。
* 服务1 - 保存用户,方法名、参数表、返回值类型和请求路径地址、请求方式。
* 服务2 - 更新用户,方法名、参数表、返回值类型和请求路径地址、请求方式。
* Application Client可以访问什么远程服务。
* 服务1 - 保存用户,方法名、参数表、返回值类型和请求路径地址、请求方式。
* 服务2 - 更新用户,方法名、参数表、返回值类型和请求路径地址、请求方式。
*/
public interface UserServiceAPI {
@PostMapping("/user/save") // 就是约束了只处理POST请求方式的RequestMapping注解。
Map<String, Object> saveUser(@RequestBody User user);
@PostMapping("/user/update")
Map<String, Object> updateUser(@RequestBody User user);
}
4 创建 feignservice 子模块 (Application server)
4.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="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>springcloudopenfeign</artifactId>
<groupId>com.bjsxt</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>feignservice</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.bjsxt</groupId>
<artifactId>feignapi</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
</dependencies>
</project>
4.2 UserController 控制类
package com.bjsxt.feign.controller;
import com.bjsxt.feign.api.UserServiceAPI;
import com.bjsxt.feign.entity.User;
import org.springframework.web.bind.annotation.RestController;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
@RestController
public class UserController implements UserServiceAPI {
@Override
public Map<String, Object> saveUser(User user) {
Map<String, Object> result = new HashMap<>();
System.out.println("保存用户数据:" + user);
result.put("message", "保存用户数据成功");
return result;
}
@Override
public Map<String, Object> updateUser(User user) {
Map<String, Object> result = new HashMap<>();
System.out.println("更新用户数据:" + user);
result.put("message", "更新用户数据成功");
return result;
}
}
4.3 application.yml 配置文件
server:
port: 8082
spring:
application:
name: feign-app-service
eureka:
client:
service-url:
defaultZone: http://localhost:8761/eureka/
4.4 启动类(集群)
package com.bjsxt.feign;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class FeignServiceApp {
public static void main(String[] args) {
SpringApplication.run(FeignServiceApp.class, args);
}
}
package com.bjsxt.feign;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class FeignServiceApp1 {
public static void main(String[] args) {
SpringApplication.run(FeignServiceApp1.class, args);
}
}
5 创建 feignclient 子模块 (Application client)
5.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="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>springcloudopenfeign</artifactId>
<groupId>com.bjsxt</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>feignclient</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.bjsxt</groupId>
<artifactId>feignapi</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
</dependencies>
</project>
5.2 UserController 控制类
package com.bjsxt.feign.client.controller;
import com.bjsxt.feign.client.service.UserService;
import com.bjsxt.feign.entity.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.Map;
@RestController
public class UserController {
@Autowired
private UserService userService;
@GetMapping("/user/save")
public Object save(User user){
Map<String, Object> result = this.userService.saveUser(user);
System.out.println(result);
return result;
}
@GetMapping("/user/update")
public Object update(User user){
Map<String, Object> result = this.userService.updateUser(user);
System.out.println(result);
return result;
}
}
5.3 UserService 接口
package com.bjsxt.feign.client.service;
import com.bjsxt.feign.api.UserServiceAPI;
import org.springframework.cloud.openfeign.FeignClient;
/**
* 本地服务,是用于远程访问Application Service的本地服务接口。
*/
@FeignClient("feign-app-service")
public interface UserService extends UserServiceAPI {
}
5.4 application.yml 配置文件
server:
port: 8081
compression:
spring:
application:
name: feign-app-client
eureka:
client:
service-url:
defaultZone: http://localhost:8761/eureka/
feign-app-service: # 配置负载均衡
ribbon:
NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule
5.5 启动类
package com.bjsxt.feign.client;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.openfeign.EnableFeignClients;
/**
* 需要提供一个新的注解
* @EnableFeignClients - 开启OpenFeign客户端技术。扫描@FeignClient注解。
* 默认扫描当前类所在包,及子包中所有的类型。
*/
@SpringBootApplication
@EnableFeignClients(basePackages = {"com.bjsxt.feign.client.service"})
public class FeignClientApp {
public static void main(String[] args) {
SpringApplication.run(FeignClientApp.class, args);
}
}
三、 Openfeign的请求参数处理
在Openfeign处理远程服务调用时,传递参数是通过HTTP协议传递的,参数存在的位置是请求头或请求体中。请求头传递的参数必须依赖@RequestParam注解来处理请求参数,请求体传递的参数必须依赖@RequestBody注解来处理请求参数。
/**
* 简单类型的请求参数,如:字符串,8种基本类型,包装类型,简单类型数组
* 可以通过请求头和请求体传递,可以使用GET|POST请求方式来进行处理
* Openfeign强制要求,简单类型的请求参数,必须使用@RequestParam注解描述。
*/
/**
* 自定义类型请求参数处理
* Openfeign强制要求,处理自定义类型请求参数的时候,必须使用@RequestBody注解描述。
* @RequestBody注解强制约束: 一个方法的参数表中,只能有唯一的一个参数使用此注解描述。
* Openfeign强制要求,所有的自定义类型请求参数,必须使用POST请求处理传递。
* Openfeign处理自定义类型参数的时候:只能处理唯一的一个自定义类型参数对象,必须使用POST
* 请求方式传递处理,对简单类型数据无约束。
*/
四、 Openfeign的性能优化
1 GZIP简介
gzip介绍:
gzip是一种数据格式,采用用deflate算法压缩数据;gzip是一种流行的数据压缩算法,应用十分广泛,尤其是在Linux平台。
gzip能力:
当Gzip压缩到一个纯文本数据时,效果是非常明显的,大约可以减少70%以上的数据大小。
gzip作用:
网络数据经过压缩后实际上降低了网络传输的字节数,最明显的好处就是可以加快网页加载的速度。网页加载速度加快的好处不言而喻,除了节省流量,改善用户的浏览体验外,另一个潜在的好处是Gzip与搜索引擎的抓取工具有着更好的关系。例如 Google就可以通过直接读取gzip文件来比普通手工抓取更快地检索网页。
2 HTTP协议中的压缩传输简介
第一:客户端向服务器请求头中带有:Accept-Encoding:gzip, deflate 字段,向服务器表示,客户端支持的压缩格式(gzip或者deflate),如果不发送该消息头,服务器是不会压缩的。
第二:服务端在收到请求之后,如果发现请求头中含有Accept-Encoding字段,并且支持该类型的压缩,就对响应报文压缩之后返回给客户端,并且携带Content-Encoding:gzip消息头,表示响应报文是根据该格式压缩过的。
第三:客户端接收到响应之后,先判断是否有Content-Encoding消息头,如果有,按该格式解压报文。否则按正常报文处理。
3 在Openfeign技术中应用GZIP压缩
在Spring Cloud微服务体系中,一次请求的完整流程如下:
在整体流程中,如果使用GZIP压缩来传输数据,涉及到两次请求-应答。而这两次请求-应答的连接点是Application Client,那么我们需要在Application Client中配置开启GZIP压缩,来实现压缩数据传输。
3.1 只配置Openfeign请求-应答中的GZIP压缩 (即Application Server 和 Application Client之间)
application.yml
server:
port: 8081
spring:
application:
name: feign-app-client
eureka:
client:
service-url:
defaultZone: http://localhost:8761/eureka/
feign-app-service: # 配置负载均衡
ribbon:
NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule
feign:
compression:
request:
enabled: true # 开启请求GZIP压缩
mime-types: # 什么请求类型开启GZIP压缩 a,b,c
- text/html
- text/plain
- text/xml
- application/json
min-request-size: 512 # 请求容量超过这个阀值的时候,开启压缩。默认值2048字节
response:
enabled: true # 开启响应GZIP压缩。
3.2 配置全局GZIP压缩 (即配置 浏览器与Application Client、Application Server 和 Application Client之间)
在全局配置文件中配置下述内容,来开启所有请求-应答中的GZIP压缩,这里使用的是Spring Boot中的GZIP技术。在Spring Boot中已经集成了GZIP压缩技术,并对所有的请求-应答实现GZIP数据压缩。工程中已经依赖了Spring Boot技术,所以在配置文件中可以开启Spring Boot中的GZIP压缩技术,对完整流程中所有相关的请求-应答开启GZIP压缩。
application.yml
server:
port: 8081
compression:
enabled: true # 开启全局GZIP压缩
mime-types: # 设置开启GZIP压缩的请求头类型
- text/html
- text/plain
- text/xml
- application/json
- application/javascript
- application/xml
min-response-size: 512 # 最小的响应容量阀值,超过这个容量,进行压缩,默认2048字节。
spring:
application:
name: feign-app-client
eureka:
client:
service-url:
defaultZone: http://localhost:8761/eureka/
feign-app-service: # 配置负载均衡
ribbon:
NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule
feign:
compression:
request:
enabled: true # 开启请求GZIP压缩
mime-types: # 什么请求类型开启GZIP压缩 a,b,c
- text/html
- text/plain
- text/xml
- application/json
min-request-size: 512 # 请求容量超过这个阀值的时候,开启压缩。默认值2048字节
response:
enabled: true # 开启响应GZIP压缩。
五、 配置Openfeign负载均衡请求超时时间
Openfeign技术底层是通过Ribbon技术实现的,那么在负载均衡和超时时间配置上,主要对Ribbon的配置。
超时时间配置-----在Application Client应用的配置文件 application.yml 上:
server:
port: 8081
compression:
enabled: true # 开启全局GZIP压缩
mime-types: # 设置开启GZIP压缩的请求头类型
- text/html
- text/plain
- text/xml
- application/json
- application/javascript
- application/xml
min-response-size: 512 # 最小的响应容量阀值,超过这个容量,进行压缩,默认2048字节。
spring:
application:
name: feign-app-client
eureka:
client:
service-url:
defaultZone: http://localhost:8761/eureka/
feign-app-service: # 配置负载均衡
ribbon:
NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule
feign:
compression:
request:
enabled: true # 开启请求GZIP压缩
mime-types: # 什么请求类型开启GZIP压缩 a,b,c
- text/html
- text/plain
- text/xml
- application/json
min-request-size: 512 # 请求容量超过这个阀值的时候,开启压缩。默认值2048字节
response:
enabled: true # 开启响应GZIP压缩。
ribbon:
ConnectTimeout: 1000 # 连接超时时间
ReadTimeout: 3000 # 操作超时时间。必须大于等于ConnectTimeout。