1. feign的使用及原理
一:Feign介绍
Feign 的英文表意为“假装,伪装,变形”, 是一个http请求调用的轻量级框架,可以以Java接口注解的方式调用Http请求,而不用像Java中通过封装HTTP请求报文的方式直接调用。Feign通过处理注解,将请求模板化,当实际调用的时候,传入参数,根据参数再应用到请求上,进而转化成真正的请求,这种请求相对而言比较直观。
Feign被广泛应用在Spring Cloud 的解决方案中,是学习基于Spring Cloud 微服务架构不可或缺的重要组件。
Feign支持多种注解,例如Feign自带的注解或者JAXRS注解等。Spring Cloud对Feign进行了增强,使其支持Spring MVC注解,另外还整合了Ribbon和Eureka,从而使得Feign的使用更加方便.
二. Ribbon VS Feign
feign和ribbon是Spring Cloud的Netflix中提供的两个实现软负载均衡的组件,Ribbon和Feign都是用于调用其他服务的,方式不同。Feign则是在Ribbon的基础上进行了一次改进,采用接口的方式。
将需要调用的其他服务的方法定义成抽象方法即可,不需要自己构建 http 请求。
不过要注意的是抽象方法的注解、方法签名要和提供服务的方法完全一致
1.启动类使用的注解不同,Ribbon 用的是@RibbonClient,Feign 用的是@EnableFeignClients。
2.服务的指定位置不同,Ribbon 是在@RibbonClient 注解上声明,Feign 则是在定义抽象方法的接口中使用@FeignClient 声明。
3.调用方式不同,Ribbon 需要自己构建 http 请求,模拟 http 请求然后使用 RestTemplate 发送给其他服务,步骤相当繁琐。
个人觉得主要原因是这个
Feign
Feign 是在 Ribbon 的基础上进行了一次改进,是一个使用起来更加方便的 HTTP 客户端。采用接口的方式, 只需要创建一个接口,然后在上面添加注解即可 ,将需要调用的其他服务的方法定义成抽象方法即可, 不需要自己构建 http 请求。然后就像是调用自身工程的方法调用,而感觉不到是调用远程方法,使得编写 客户端变得非常容易。
Ribbon
Ribbon 是一个基于 HTTP 和 TCP 客户端 的负载均衡的工具。它可以 在客户端 配置 RibbonServerList(服务端列表),使用 HttpClient 或 RestTemplate 模拟 http 请求,步骤相对繁琐。
三. 在工程中引入feign
1. 加入feign依赖
<!-- 服务之间http调用, 引入feign -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
2. 添加注解
在启动类增加注解
@EnableFeignClients
3. 修改工程为普通的jar
我们即将创建的这个工程, 专门用来管理维护feign调用请求
因为该工程式一个普通的jar 不需要打可执行的jar
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId> 5
<artifactId>maven‐jar‐plugin</artifactId>
</plugin>
</plugins>
</build>
4. 编写声明式接口
@FeignClient(name="product‐center")
publicinterfaceProductCenterFeignApi{
/**
* 声明式接口,远程调用http://product‐center/selectProductInfoById/{productNo}
* @param productNo
* @return
*/
@RequestMapping("/selectProductInfoById/{productNo}")
ProductInfo selectProductInfoById(@PathVariable("productNo") String productNo);
}
在order项目中配置
四. 细粒度配置
4.1 设置日志打印级别
默认情况下,Feign的调用式不打印日志,我们需要通过自定义来打印我们的Feign的日志
一共有4种级别:
设置日志有2种方式, 一种是配置文件, 一种是实体类. 我们两种方式都试一下
我们来试一下四种日志级别的效果
1. FULL: 记录请求和响应的header,body,元数据
使用实体类的方式
第一步: 自定义一个ProductFeignConfig配置类
/**
* 这个类上千万不要添加@Configuration,不然会被作为全局配置文件共享 * Created by smlz on 2019/11/22.
*/
class ProductFeignConfig {
@Bean
public Logger.Level level() {
return Logger.Level.FULL;
}
}
注意: 这个配置类上面不要加@Configuration, 不然会被作为全局配置被所有文件共享
第二步: 在feignClient上指定configuration
@FeignClient(name = "product1", configuration = ProductFeignConfig.class)
public interface ProductClient {
/**
* 调用product项目的config接口
* @return
*/
@RequestMapping("/config")
String getconfig();
}
第三步: 设置配置文件,启动调试模式
这里如果不开启, 那么所有日志都不会被打印出. 也就是这里相当于一个开关, 这里设置了, 上面的自定义日志级别才会生效
logging:
level:
com:
lxl:
www: debug
第四步: 启动zuul-test-order 和zuul-test-product
第五步: 浏览器运行
http://localhost:8081/get/user
以上是日志级别为Full的时候, 打印的日志.
2 HEADERS: 记录basic的基础上, 记录请求和响应的header
这次使用配置文件的方式直接配置
第一步: 注释掉刚才的configuration配置项
@FeignClient(name = "product1" /*, configuration = ProductFeignConfig.class*/)
public interface ProductClient {
/**
* 调用product项目的config接口
* @return
*/
@RequestMapping("/config")
String getconfig();
}
注意红色字体, 是删除的部分
第二步: 在调用方: zuul-test-order 通过feign:client:config:微服务名称:loggerLevel: 日志级别来指定
feign:
client:
config:
product1:
loggerLevel: HEADERS
第三步: 启动zuul-test-order和zuul-test-product
第四步: 浏览器访问url
http://localhost:8081/get/user
咋一看,打印的内容和上面full的差不多, 不过,需要注意的是, 我们这里的这个请求是非常简单的get请求. 当请求的header较多, 请求类型为post的时候, 这里应该是有差别的.
3 BASIC: 仅记录请求方法, url, 响应状态码及执行时间(生产环境建议使用)
NONE: 不打印任何日志
这里就不一点点记录了, 直接看效果图
设置为basic
设置为none
没有任何日志
4.2 设置使用的注解风格
feign支持两种风格的注解, 一种是spring mvc的注解风格, 另一种是Feign原生的注解
@FeignClient(name="product‐center",configuration=ProductCenterFeignConfig.class)
public interface ProductCenterFeignApi {
/**
* 修改锲约为Feign的 那么就可以使用默认的注解
* @param productNo
* @return
*/
@RequestLine("GET /selectProductInfoById/{productNo}")
ProductInfo selectProductInfoById(@Param("productNo") String productNo);
}
上述案例使用的是feign如红色重点标出部分, 使用的是RequestLine的方法
通常我们还是使用spring mvc的更多一些, 如下:
@FeignClient(name = "product1" )
public interface ProductClient {
@RequestMapping("/config")
String getconfig();
}
4.3 自定义feign拦截器,实现参数的透传
第一步: 自定义拦截器, 该拦截器实现了RequestInterceptor接口
package com.lxl.www.order.interceptor;
import feign.RequestInterceptor;
import feign.RequestTemplate;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.http.HttpServletRequest;
@Component
public class ProductRequestIntercept implements RequestInterceptor {
@Override
public void apply(RequestTemplate requestTemplate) {
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request = attributes.getRequest();
String version = request.getHeader("version");
requestTemplate.header("version", version);
}
}
第二步: 启用自定义拦截器
在配置类中添加自定义拦截器
/**
* 这个类上千万不要添加@Configuration,不然会被作为全局配置文件共享 * Created by smlz on 2019/11/22.
*/
class ProductFeignConfig {
/*@Bean
public Logger.Level level() {
return Logger.Level.FULL;
}*/
/*@Bean
public Contract feignContract() {
return new Contract.Default();
}*/
// 启用自定义拦截器
@Bean
public RequestInterceptor getRequestInterceptor() {
return new ProductRequestIntercept();
}
}
第三步: 在order项目中写一个接口, 并打印参数version(前三步都是在order中设置)
@GetMapping("version")
public String getVersion(HttpServletRequest request){
String version = request.getHeader("version");
log.info("order获取传递过来的版本号:{}", version);
productClient.getconfig();
return this.config;
}
第四步: 在product项目中接收version
@GetMapping("config")
public String getconfig(HttpServletRequest request){
String version = request.getHeader("version");
log.info("product获取透传过来的版本号:{}", version);
log.info("调用product/config, {}", 3);
return this.config;
}
第五步:查看运行效果
我们看到version在两个项目中传递
五. Feign调用优化方案
第一步:开启连接池配置
feign:
client:
config:
product‐center:
loggerLevel: full
contract: feign.Contract.Default
httpclient:
#让feign底层使用HttpClient去调用
enabled: true
max‐connections: 200 #最大连接数 max‐connections‐per‐route: 50 #为每个url请求设置最大连接数
第二步:调整Feign的日志级别(强烈推荐使用Basic级别的)
- 帝国cms栏目别名如何调用?
- 数据库安全·保护表字段
- 【实践】伪造名人的脸—做一个小示例了解生成式对抗网络
- Spring 常见问题与解决方法
- 如何通过css控制内容显示顺序 第二行的内容优先显示
- 杨廷琨 - 用SQL解析神奇的扑克牌魔术
- Spring boot with Spring security
- Spring RestFul and RestTemplate
- Spring boot with Velocity template
- Oracle 11g R2 RAC 高可用连接特性 – SCAN 详解
- Spring boot · 链接池配置
- Linux下命令行图片格式转换
- 用SQL解一道有趣的数学题:Gauss和Poincare
- OpenSSL 转换证书格式
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- python实现线性回归之简单回归
- 走进STL - 空间配置器,STL背后的故事
- C++ 智能指针
- python实现线性回归之lasso回归
- 分页查询 offset 和 limit 和 limit 的区别
- mybatis文件映射之获取参数值时#和$的区别
- python实现线性回归之岭回归
- 操作系统实验之存储管理
- MySQL EXPLAIN 的使用
- mybatis文件映射之关联查询初探(一)
- python实现线性回归之弹性网回归
- 【原创】python倒排索引之查找包含某主题或单词的文件
- python实现逻辑回归
- Linux文件管理参考
- CloudBase Framework丨第一个 Deno 部署工具是如何打造的?