一淘邻、Feign的定義
1.什么是Feign窑眯?
Feign 是一種聲明式屏积、模板化的 HTTP 客戶端(僅在 consumer 中使用)。
Feign 的英文表意為“假裝磅甩,偽裝炊林,變形”, 是一個http請求調(diào)用的輕量級框架卷要,可以以Java接口注解的方式調(diào)用Http請求铛铁,而不用像Java中通過封裝HTTP請求報文的方式直接調(diào)用。Feign通過處理注解却妨,將請求模板化饵逐,當(dāng)實際調(diào)用的時候,傳入?yún)?shù)彪标,根據(jù)參數(shù)再應(yīng)用到請求上倍权,進(jìn)而轉(zhuǎn)化成真正的請求,這種請求相對而言比較直觀捞烟。
Feign被廣泛應(yīng)用在Spring Cloud 的解決方案中薄声,是學(xué)習(xí)基于Spring Cloud 微服務(wù)架構(gòu)不可或缺的重要組件。
- Feign解決了什么問題:
裝了Http調(diào)用流程题画,更適合面向接口化的變成習(xí)慣默辨。
在服務(wù)調(diào)用的場景中,我們經(jīng)常調(diào)用基于Http協(xié)議的服務(wù)苍息,而我們經(jīng)常使用到的框架可能有HttpURLConnection缩幸、Apache HttpComponnets壹置、OkHttp3 、Netty等等表谊,這些框架在基于自身的專注點提供了自身特性钞护。而從角色劃分上來看,他們的職能是一致的提供Http調(diào)用服務(wù)爆办。
- 調(diào)用流程:
調(diào)用方Client框架服務(wù)方構(gòu)造Http請求URL填寫Http請求頭信息填寫消息報文信息發(fā)送Http請求處理請求难咕,返回結(jié)果返回報文提取報文信息,轉(zhuǎn)換成對應(yīng)的Java bean根據(jù)Bean中的定義距辆,業(yè)務(wù)處理調(diào)用方Client框架服務(wù)方余佃。
2.什么是聲明式?
聲明式調(diào)用就像調(diào)用本地方法一樣調(diào)用遠(yuǎn)程方法;無感知遠(yuǎn)程 http 請求跨算。
- 聲明式的作用:
Spring Cloud 的聲明式調(diào)用, 可以做到使用 HTTP 請求遠(yuǎn)程服務(wù)時能就像調(diào)用本地 方法一樣的體驗爆土,開發(fā)者完全感知不到這是遠(yuǎn)程方法,更感知不到這是個 HTTP 請求漂彤。
它像 Dubbo 一樣雾消,consumer 直接調(diào)用接口方法調(diào)用 provider灾搏,而不需要通過常規(guī)的 Http Client 構(gòu)造請求再解析返回數(shù)據(jù)挫望。
- 解決了什么問題:
它解決了讓開發(fā)者調(diào)用遠(yuǎn)程接口就跟調(diào)用本地方法一樣,無需關(guān)注與遠(yuǎn)程的交互細(xì) 節(jié)狂窑,更無需關(guān)注分布式環(huán)境開發(fā)媳板。
二、Feign簡單案例
1.實現(xiàn)Feign的簡單操作:
需求實現(xiàn)對商品的基本操作泉哈。
1.1創(chuàng)建Product-Service項目:
使用Eclipse創(chuàng)建Maven項目蛉幸。使用Eureka的集群注冊中心。
- 修改POM文件添加依賴:
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Dalston.SR5</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
</dependency>
</dependencies>
- 創(chuàng)建Service接口:
/**
* 服務(wù)的接口
* @author zhang
*/
@RequestMapping("/product")
public interface ProductService {
// 查詢所有
@RequestMapping(value = "/findAll", method = RequestMethod.GET)
public List<Product> findAll();
}
- 創(chuàng)建實體類(getter和setter方法)
public class Product {
private Integer id;
private String name;
public Product() {
super();
}
public Product(Integer id, String name) {
super();
this.id = id;
this.name = name;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
1.2創(chuàng)建Product-Provider項目:
- 修改POM文文件添加依賴:
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Dalston.SR5</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
</dependency>
<dependency>
<groupId>com.zlw</groupId>
<artifactId>springcloud-feign-service</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
</dependencies>
- 添加全局配置文件:
spring.application.name=product-provider
server.port=9093
#設(shè)置服務(wù)注冊的中心地址丛晦,指向另一個注冊中心
eureka.client.serviceUrl.defaultZone=http://admin:123456@eureka1:8761/eureka/,http://admin:123456@eureka2:8761/eureka/
- 創(chuàng)建Controller:
/**
* Provider服務(wù)
* @author zhang
*
*/
@RestController
public class ProductController implements ProductService{
@Override
public List<Product> findAll() {
List<Product> list=new ArrayList<Product>();
list.add(new Product(1,"電腦"));
list.add(new Product(2,"手機(jī)"));
list.add(new Product(3,"電視"));
return list;
}
}
- 創(chuàng)建啟動類:
@EnableEurekaClient
@SpringBootApplication
public class ProductApplication {
public static void main(String[] args) {
SpringApplication.run(ProductApplication.class, args);
}
}
1.3創(chuàng)建Product-Consumer:
- 修改POM文件添加依賴:
添加Product-Service的坐標(biāo)奕纫;添加Feign的坐標(biāo)。
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Dalston.SR5</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
</dependency>
<!-- 添加 Feign 坐標(biāo) -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-feign</artifactId>
</dependency>
<!-- 添加 product-service 坐標(biāo) -->
<dependency>
<groupId>com.zlw</groupId>
<artifactId>springcloud-feign-service</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
</dependencies>
- 添加全局配置文件:
spring.application.name=product-consumer
server.port=9094
#設(shè)置服務(wù)中心地址烫沙,指向另一個注冊中心
eureka.client.serviceUrl.defaultZone=http://admin:123456@eureka1:8761/eureka/,http://admin:123456@eureka2:8761/eureka/
- 創(chuàng)建Service接口:
@FeignClient(name = "product-provider")
public interface ConsumerService extends ProductService{
}
- 創(chuàng)建Controller:
/**
* Consumer服務(wù)
*
* @author zhang
*
*/
@RestController
public class ConsumerController {
@Autowired
ConsumerService consumerService;
// 查詢所有
@RequestMapping(value = "list", method = RequestMethod.GET)
public List<Product> list() {
List<Product> list = consumerService.findAll();
return list;
}
}
- 創(chuàng)建啟動類:
@EnableFeignClients
@EnableDiscoveryClient
@SpringBootApplication
public class ConsumerApplication {
public static void main(String[] args) {
SpringApplication.run(ConsumerApplication.class, args);
}
}
2.Feign的單個參數(shù)處理:
基于上面的項目環(huán)境進(jìn)行修改匹层;添加在Service中添加參數(shù)傳遞的方法。
- 修改Product-Service:
// 傳遞單個參數(shù)
@RequestMapping(value = "findById", method = RequestMethod.GET)
public Product findById(@RequestParam("id") Integer id);
- 修改Product-Provider:
@Override
public Product findById(Integer id) {
return new Product(id,"ProductName");
}
- 修改Product-Consumer:
// 單個參數(shù)傳遞
@RequestMapping(value = "findById", method = RequestMethod.GET)
public Product findById(@RequestParam("id") Integer id) {
return consumerService.findById(id);
}
3.Feign的多個參數(shù)傳遞:
3.1基于GET的提交方式:
- 修改Product-Service
// 方式一:使用GET請求锌蓄,傳遞多個參數(shù)升筏,(傳遞對象必須拆分為多個單個參數(shù))
@RequestMapping(value = "addProduct", method = RequestMethod.GET)
public Product addProduct(@RequestParam("id") Integer id, @RequestParam("name") String name);
- 修改Product-Provider:
@Override
public Product addProduct(Integer id, String name) {
return new Product(id,name);
}
- 修改Product-Consumer:
// 多個參數(shù)傳遞,方式一:GET請求
@RequestMapping(value = "add", method = RequestMethod.GET)
public Product addProduct(Product product) {
return consumerService.addProduct(product.getId(), product.getName());
}
3.2基于POST的提交方式:
- 修改 Product-Service :
//方式二:使用POST請求瘸爽,傳遞多個參數(shù)
@RequestMapping(value = "addProduct2",method = RequestMethod.POST)
public Product addProduct2(@RequestBody Product product);
- 修改 Product-Provider:
@Override
public Product addProduct2(@RequestBody Product product) {
return product;
}
- 修改 Product-Consumer :
//方式二:POST請求您访,多參數(shù)傳遞
@RequestMapping(value = "add2",method = RequestMethod.GET)
public Product add(Product product) {
return consumerService.addProduct2(product);
三、Feign操作案例使用MyBatis操作數(shù)據(jù)庫
實現(xiàn)對數(shù)據(jù)庫的增查操作剪决,演示Feign中的不同參數(shù)傳遞灵汪。
使用Eclipse創(chuàng)建項目搭建環(huán)境檀训。
- 數(shù)據(jù)庫:
CREATE TABLE `users` (
`userid` int(11) NOT NULL AUTO_INCREMENT,
`username` varchar(30) DEFAULT NULL,
`userage` int(5) DEFAULT NULL,
PRIMARY KEY (`userid`)
) ENGINE=InnoDB AUTO_INCREMENT=15 DEFAULT CHARSET=utf8;
1.創(chuàng)建MyBatis-Service項目:
- 修改POM文件添加相關(guān)依賴:
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Dalston.SR5</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
- 創(chuàng)建接口:
@FeignClient(name = "users-provider")
public interface UserServiceFeign {
@RequestMapping("/user")
public List<User> findAll();
@RequestMapping("/user/{userid}")
public User findById(@PathVariable("userid") int userid);
@RequestMapping("/findOne")
public User findOne(@RequestParam("userid") int userid);
@RequestMapping(value = "/add", method = RequestMethod.POST)
public int addUser(@RequestBody User user);
}
- 創(chuàng)建實體類(get和set):
private int userid;
private String username;
private int userage;
2.創(chuàng)建MyBatis-Provider:
- 修改POM文件,添加相關(guān)依賴:
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Dalston.SR5</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<!-- Mybatis 啟動器 -->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>1.1.1</version>
</dependency>
<!-- mysql 數(shù)據(jù)庫驅(qū)動 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<!-- druid 數(shù)據(jù)庫連接池 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.0.9</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
<!--配置資源拷貝插件 -->
<resources>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.xml</include>
</includes>
</resource>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>**/*.yml</include>
<include>**/*.properties</include>
</includes>
</resource>
</resources>
</build>
- 修改全局配置文件:
spring.application.name=user-provider
#配置端口
server.port=9096
#mybatis
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/ssm
spring.datasource.username=root
spring.datasource.password=root
spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
mybatis.type-aliases-package=com.zlw.pojo
#配置eureka信息
eureka.client.serviceUrl.defaultZone=http://admin:123456@eureka1:8761/eureka/,http://admin:123456@eureka2:8761/eureka/
- 創(chuàng)建實體類:
private int userid;
private String username;
private int userage;
- 創(chuàng)建mapper接口和映射文件:
public interface UserMapper {
public List<User> findAll();
public User findById(int userid);
public int AddUser(User user);
}
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.zlw.mapper.UserMapper">
<select id="findAll" resultType="user">
select * from users
</select>
<select id="findById" resultType="user">
select *from users where userid=#{userid}
</select>
<insert id="addUser" parameterType="user">
insert into users(username,userage) values(#{username},#{userage})
</insert>
</mapper>
- 創(chuàng)建Service接口和實現(xiàn)類:
public interface UserService {
// 查詢所有
public List<User> findAll();
// 查詢指定
public User findById(int userid);
// 添加
public int addUser(User user);
}
@Service
public class UserServiceImpl implements UserService {
@Autowired
UserMapper userMapper;
@Override
public List<User> findAll() {
return userMapper.findAll();
}
@Override
public User findById(int userid) {
return userMapper.findById(userid);
}
@Override
public int addUser(User user) {
return userMapper.AddUser(user);
}
}
- 創(chuàng)建Controller:
@RestController
public class UserController {
@Autowired
UserService userService;
// 查詢所有
@RequestMapping("/user")
public List<User> findAll() {
List<User> list = userService.findAll();
return list;
}
// 查詢指定
@RequestMapping("/user/{userid}")
public User findById(@PathVariable int userid) {
User user = userService.findById(userid);
return user;
}
// 添加
@RequestMapping(value = "/add", method = RequestMethod.POST)
public int add(User user) {
int num = userService.addUser(user);
return num;
}
}
- 創(chuàng)建啟動類:
@EnableEurekaClient
@MapperScan({"com.zlw.mapper"})
@SpringBootApplication
public class ProviderApplication {
public static void main(String[] args) {
SpringApplication.run(ProviderApplication.class, args);
}
}
3.創(chuàng)建MyBatis-Consumer:
- 修改POM文件识虚,添加相關(guān)依賴:
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Dalston.SR5</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-feign</artifactId>
</dependency>
<dependency>
<groupId>com.zlw</groupId>
<artifactId>springcloud-feign-mybits-service</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
</dependencies>
- 修改配置文件:
spring.application.name=user-consumer
server.port=9097
eureka.client.serviceUrl.defaultZone=http://admin:123456@eureka1:8761/eureka/,http://admin:123456@eureka2:8761/eureka/
- 創(chuàng)建Controller:
@RestController
public class UserController {
@Autowired
UserServiceFeign userService;
@RequestMapping("/user")
public List<User> findAll() {
return userService.findAll();
}
@RequestMapping("/user/{userid}")
public User findById(@PathVariable int userid) {
return userService.findById(userid);
}
@RequestMapping("/findOne")
public User findOne(int userid) {
return userService.findOne(userid);
}
@RequestMapping("/addUser")
public int addUser(User user) {
return userService.addUser(user);
}
}
- 頁面跳轉(zhuǎn):
@Controller
public class PageShow {
@RequestMapping("/{page}")
public String show(@PathVariable String page) {
return page;
}
}
- 在templates目錄下創(chuàng)建addUser.html:
<html xmlns:th="www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form th:action="@{/addUser}" method="post">
<p>
用戶名:<input type="text" name="username" />
</p>
<p>
年齡:<input type="text" name="userage" />
</p>
<p>
<input type="submit" value="添加" />
</p>
</form>
</body>
</html>
- 創(chuàng)建啟動類:
@EnableFeignClients(basePackages = "com.zlw.service")
@EnableDiscoveryClient
@SpringBootApplication
public class ConsumerApplication {
public static void main(String[] args) {
SpringApplication.run(ConsumerApplication.class, args);
}
}
四肢扯、Feign的性能優(yōu)化
1.使用Gzip壓縮算法:
- 什么是gzip?
gzip 是一種數(shù)據(jù)格式担锤,采用用 deflate 算法壓縮 data蔚晨;gzip 是一種流行的文件 壓縮算法,應(yīng)用十分廣泛肛循,尤其是在 Linux 平臺铭腕。
當(dāng) Gzip 壓縮到一個純文本文件時,效果是非常明顯的多糠,大約可以減少 70%以上的文件大小累舷。
- gzip的作用:
網(wǎng)絡(luò)數(shù)據(jù)經(jīng)過壓縮后實際上降低了網(wǎng)絡(luò)傳輸?shù)淖止?jié)數(shù),最明顯的好處就是可 以加快網(wǎng)頁加載的速度夹孔。網(wǎng)頁加載速度加快的好處不言而喻被盈,除了節(jié)省流量,改善用戶的瀏 覽體驗外搭伤,另一個潛在的好處是 Gzip 與搜索引擎的抓取工具有著更好的系只怎。例如 Google 就可以通過直接讀取 gzip 文件來比普通手工抓取 更快地檢索網(wǎng)頁。
- HTTP協(xié)議中對壓縮傳輸?shù)囊?guī)定:
(1)客戶端向服務(wù)器請求中帶有:Accept-Encoding:gzip, deflate 字段怜俐,向服務(wù)器表示身堡, 客戶端支持的壓縮格式(gzip 或者 deflate),如果不發(fā)送該消息頭拍鲤,服務(wù)器是不會壓縮的贴谎。
(2)服務(wù)端在收到請求之后,如果發(fā)現(xiàn)請求頭中含有 Accept-Encoding 字段季稳,并且支 持該類型的壓縮擅这,就對響應(yīng)報文壓縮之后返回給客戶端,并且攜帶 Content-Encoding:gzip 消 息頭景鼠,表示響應(yīng)報文是根據(jù)該格式壓縮過的仲翎。
(3)客戶端接收到請求之后,先判斷是否有 Content-Encoding 消息頭莲蜘,如果有谭确,按該 格式解壓報文。否則按正常報文處理票渠。
2.測試支持Gzip壓縮的案例:
使用Feign簡單案例中的測試環(huán)境逐哈,創(chuàng)建新的Product-Consumer項目。
2.1通過 Feign 到 Provider 的請求與相應(yīng)的 Gzip 壓縮:
- 修改Consumer的配置文件:
spring.application.name=product-consumer
server.port=9095
eureka.client.serviceUrl.defaultZone=http://admin:123456@eureka1:8761/eureka/,http://admin:123456@eureka2:8761/eureka/
#-----------------------------feign gzip
#配置請求 GZIP 壓縮
feign.compression.request.enabled=true
#配置響應(yīng) GZIP 壓縮
feign.compression.response.enabled=true
#配置壓縮支持的 MIME TYPE
feign.compression.request.mime-types=text/xml,application/xml,application/json
#配置壓縮數(shù)據(jù)大小的最小閥值问顷,默認(rèn) 2048
feign.compression.request.min-request-size=512
2.2對客戶端瀏覽器的請求以及 Consumer 對 provider 的請求與響應(yīng)做 Gzip 壓縮:
- 修改Consumer的配置文件:
#-----------------------------spring boot gzip
#是否啟用壓縮
server.compression.enabled=true
server.compression.mime-types=application/json,application/ xml,text/html,text/xml,text/plain
3.使用Http連接池:
- Http的背景原理:
(1)兩臺服務(wù)器建立 http 連接的過程是很復(fù)雜的一個過程昂秃,涉及到多個數(shù)據(jù)包的交換禀梳,并 且也很耗時間。
(2)Http 連接需要的 3 次握手 4 次分手開銷很大肠骆,這一開銷對于大量的比較小的 http 消 息來說更大算途。
- 優(yōu)化性能的解決方案:
(1)如果我們直接采用 http 連接池,節(jié)約了大量的 3 次握手 4 次分手蚀腿;這樣能大大提升吞 吐率嘴瓤。
(2)feign的http 客戶端支持3種框架;HttpURLConnection莉钙、httpclient廓脆、okhttp;默認(rèn)是 HttpURLConnection磁玉。
(3)傳統(tǒng)的 HttpURLConnection 是 JDK 自帶的停忿,并不支持連接池,如果要實現(xiàn)連接池的 機(jī)制蚊伞,還需要自己來管理連接對象席赂。對于網(wǎng)絡(luò)請求這種底層相對復(fù)雜的操作,如果有可用的 其他方案时迫,也沒有必要自己去管理連接對象颅停。
(4)HttpClient 相比傳統(tǒng) JDK 自帶的 HttpURLConnection,它封裝了訪問 http 的請求頭别垮, 參數(shù)便监,內(nèi)容體扎谎,響應(yīng)等等碳想;它不僅使客戶端發(fā)送 HTTP 請求變得容易,而且也方便了開發(fā)人 員測試接口(基于 Http 協(xié)議的)毁靶,即提高了開發(fā)的效率胧奔,也方便提高代碼的健壯性;另外 高并發(fā)大量的請求網(wǎng)絡(luò)的時候预吆,還是用“連接池”提升吞吐量龙填。
4.1使用HttpClient客戶端工具:
使用Feign簡單案例中的測試環(huán)境,創(chuàng)建新的Product-Consumer項目拐叉。
- 修改POM配置文件添加HTTPClient的坐標(biāo):
<!-- 使用Apache HttpClient替換Feign原生httpURLConnection -->
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
</dependency>
<dependency>
<groupId>com.netflix.feign</groupId>
<artifactId>feign-httpclient</artifactId>
<version>8.17.0</version>
</dependency>
- 修改配置文件開啟HttpClient的使用:
spring.application.name=product-consumer
server.port=9098
eureka.client.serviceUrl.defaultZone=http://admin:123456@eureka1:8761/eureka/,http://admin:123456@eureka2:8761/eureka/
#啟用 httpclient
feign.httpclient.enabled=true
- 注意:
如果使用 HttpClient 作為 Feign 的客戶端工具岩遗。那么在定義接口上的注解是需要 注意,如果傳遞的是一個自定義的對象(對象會使用 json 格式來專遞)凤瘦。需要制定類型宿礁。
- 修改Product-Service:
// 添加商品傳遞多個參數(shù) 方式二 :POST 方式
@RequestMapping(value="/add2",method=RequestMethod.POST,consumes=MediaType.APPLICATION_JSON_VALUE)
public ProductaddProduct2(@RequestBody Product product);
//使用 HttpClient 工具 添加商品傳遞多個參數(shù) :基于GET 方式
@RequestMapping(value="/add3",method=RequestMethod.GET,consumes=MediaType.APPLICATION_JSON_VALUE)
public Product addProduct3(Productproduct);
4.查看微服務(wù)日志中記錄每個接口 URL,狀態(tài)碼和耗時信息 :
- 日志的級別:
級別 | 說明 |
---|---|
NONE | 不做任何記錄 |
BASIC | 只記錄輸出Http 方法名稱蔬芥、請求URL梆靖、返回狀態(tài)碼和執(zhí)行時間 |
HEADERS | 記錄輸出Http 方法名稱控汉、請求URL、返回狀態(tài)碼和執(zhí)行時間 和 Header 信息 |
FULL | 記錄Request 和Response的Header返吻,Body和一些請求元數(shù)據(jù) |
- 創(chuàng)建Consumer:
使用Feign簡單案例中的環(huán)境姑子。創(chuàng)建Consumer項目。
- 添加logback.xml配置文件:
<?xml version="1.0" encoding="UTF-8" ?>
<configuration>
<!--定義日志文件的存儲地址 勿在 LogBack 的配置中使用相對路徑-->
<property name="LOG_HOME" value="${catalina.base}/logs/" />
<!-- 控制臺輸出 -->
<appender name="Stdout" class="ch.qos.logback.core.ConsoleAppender">
<!-- 日志輸出編碼 -->
<layout class="ch.qos.logback.classic.PatternLayout">
<!--格式化輸出:%d表示日期测僵,%thread表示線程名街佑,%-5level:級別從左顯示5個字符寬度%msg:日志消息,%n是換行符-->
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n
</pattern>
</layout>
</appender>
<!-- 按照每天生成日志文件 -->
<appender name="RollingFile" class="ch.qos.logback.core.rolling.RollingFileAppender">
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!--日志文件輸出的文件名-->
<FileNamePattern>${LOG_HOME}/server.%d{yyyy-MM-dd}.log</FileNamePattern>
<MaxHistory>30</MaxHistory>
</rollingPolicy>
<layout class="ch.qos.logback.classic.PatternLayout">
<!--格式化輸出:%d表示日期捍靠,%thread表示線程名舆乔,%-5level:級別從左顯示5個字符寬度%msg:日志消息,%n是換行符-->
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n
</pattern>
</layout>
<!--日志文件最大的大小-->
<triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
<MaxFileSize>10MB</MaxFileSize>
</triggeringPolicy>
</appender>
<!-- 日志輸出級別 -->
<root level="DEBUG">
<appender-ref ref="Stdout" />
<appender-ref ref="RollingFile" />
</root>
<!--日志異步到數(shù)據(jù)庫 -->
<!-- <appender name="DB" class="ch.qos.logback.classic.db.DBAppender">
日志異步到數(shù)據(jù)庫
<connectionSource class="ch.qos.logback.core.db.DriverManagerConnectionSource">
連接池
<dataSource class="com.mchange.v2.c3p0.ComboPooledDataSource">
<driverClass>com.mysql.jdbc.Driver</driverClass>
<url>jdbc:mysql://127.0.0.1:3306/databaseName</url>
<user>root</user>
<password>root</password>
</dataSource>
</connectionSource>
</appender> -->
</configuration>
- 在啟動類中添加方法:
// NONE:不記錄任何信息剂公,默認(rèn)值
// BASIC:記錄請求方法希俩、請求 URL、狀態(tài)碼和用時
// HEADERS:在 BASIC 基礎(chǔ)上再記錄一些常用信息
// FULL:記錄請求和相應(yīng)的所有信息
@Bean
public Logger.Level getLog(){
return Logger.Level.FULL;
}
5.配置Feign負(fù)載均衡請求超時時間:
Feign 的負(fù)載均衡底層用的就是 Ribbon纲辽。
5.1修改Consumer中的配置文件:
- 全局配置:
#全局配置
# 請求連接的超時時間 默認(rèn)的時間為 1 秒 ribbon.ConnectTimeout=5000
# 請求處理的超時時間
ribbon.ReadTimeout=5000
- 局部配置:
#局部配置
# 對所有操作請求都進(jìn)行重試
ego-product-provider.ribbon.OkToRetryOnAllOperations=true
# 對當(dāng)前實例的重試次數(shù)
ego-product-provider.ribbon.MaxAutoRetries=2
# 切換實例的重試次數(shù)
ego-product-providert.ribbon.MaxAutoRetriesNextServer=0
# 請求連接的超時時間
ego-product-provider.ribbon.ConnectTimeout=3000
# 請求處理的超時時間
ego-product-provider.ribbon.ReadTimeout=3000