Sping Cloud 组件中的轻量级 RESTful 的 HTTP 服务客户端,实现了负载均衡和 Rest 调用的开源框架,封装了 Ribbon 和 RestTemplate,实现了 WebService 的面向接口编程,进一步降低了系统的耦合度。
Feign 内置了 Ribbon,用来做客户端负载均衡调用注册中心的服务
Feign 本身不支持 Spring MVC 注解,为了方便使用,Spring Cloud 孵化了 OpenFeign
Feign 是一种声明式、模板化的 HTTP 客户端(仅在消费者服务使用)
Feign 的支持和注解参考 spring.io 官方文档
Feign 使用方式: 使用 Feign 的注解定义接口,调用这个接口就可以调用注册中心的服务
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-openfeignartifactId>
dependency>
// 调用 oa 模块,默认等待响应 1s
@FeignClient("oa")
public interface IOAFeign {
// 调用的 oa 模块的 接口
@RequestMapping("/oa/meeting/getList")
public BaseResponse<?> getData(@RequestParam String name);
@RequestMapping("/product/create")
public Map createProduction(Object production);
}
@Service
public class PermissionService{
// 调用 OA 模块 接口
@Autowired
private IOAFeign iogFeign;
public BaseResponse test(){
// 通过 Feign 调用 OA 模块的接口
return ioaFeign.getData("111")
}
// 对象传参
public Map test3(Object production){
// 通过 Feign 调用 OA 模块的接口
return ioaFeign.createProduction(production)
}
}
@EnableFeignClients
@SpringBootApplication
public class WorkflowApplication {
public static void main(String[] args) {
SpringApplication.run(WorkflowApplication.class, args);
}
}
pom.xml
<dependency>
<groupId>com.netflix.ribbongroupId>
<artifactId>ribbon-loadbalancerartifactId>
<version>2.7.18version>
<scope>compilescope>
dependency>
Application.java
// 启动类 或者 配置类(configuration)中注入,这里可选择需要的负载均衡策略进行注入
@Bean
public RandomRule randomRule(){
return new RandomRule();
}
pom.xml
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-netflix-ribbonartifactId>
<version>2.2.10.RELEASEversion>
dependency>
application.yml
# 负载均衡 局部策略
provider:
ribbon:
NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule
**介绍:**Gzip 是一种数据格式,采用 deflate 算法压缩数据;gzip 是一种流行的文件压缩算法,应用十分广泛,尤其是在 linux 平台
**能力:**Gzip 压缩纯文件时,效果十分显著,大约可减少 70% 以上的文件大小
**作用:**网络数据经过压缩后实际上降低了网络传输的字节数,做明显的好处就是可以加快网页加载的速度,网页加载速度加快的好处不言而喻,除了节省流量,改善用户的浏览体验外,另一个潜在好处就是 Gzip 与 搜索引擎抓取工具有更好的关系,例如 Google 可通过 读取 gzip 文件 比 普通手工抓取更快的索引网页。
HTTP 关于 压缩传输的规定
- 客户端向服务器的请求中带有:Accept-Encoding:gzip,deflate 字段,向服务器表示客户端支持的压缩格式(gzip 或 deflate),如果不发该消息头,服务端默认不压缩
- 服务端接收到请求头,发现请求头还有 Accept-Encoding 字段,并支持该类型压缩,就会对响应报文进行压缩,并携带 Content-Encoding 消息头,表面响应报文时根据该格式压缩
- 客户端接收到响应后,先判断有无Content-Encoding 消息头,如果有,按该格式解压报文,否则按正常报文处理
修改 Application.yml 文件
对客户端的请求及 Consumer 对 Provider 的请求和响应都实现 Gzip 压缩
server:
port: 6061 # 端口
# 全局开启压缩
compression:
enabled: true
# 配置压缩支持的 MIME TYPE
mime-types: application/json,application/xml,text/html,text/xml,text/plain
只配置 Consumer 通过 Feign 到 Provider 的请求与响应的 Gzip 压缩
# 局部 通过 Feign 到 Provider 的请求 进行 Gzip 压缩
feign:
compression:
request:
min-request-size: 512 # 配置压缩数据大小的最小阈值,默认 2048
mime-types: text/xml,application/xml,application/json # 配置压缩文件支持的 MIME TYPE
enabled: true # 请求是否开启 Gzip 压缩
response:
enabled: true # 响应是否开启 Gzip 压缩
原理:
两个服务器 建立 HTTP 连接 需要 3 次握手、4 次握手,对于比较小的 HTTP 消息来说 开销很大。
采用连接池,可以节省大量握手时间,大大提升吞吐量
方案:
httpClient
pom.xml
<dependency>
<groupId>io.github.openfeigngroupId>
<artifactId>feign-httpclientartifactId>
dependency>
application.yml
feign:
httpclient:
enabled: true # 开始 httpClient
Consumer 服务添加 logback.xml 日志文件,内容如下(logback 日志的输出级别需要是DEBUG级别)
resource 下 logback.xml
<configuration scan="true" scanPeriod="10 seconds">
<contextName>my_logbackcontextName>
<property name="log.path" value="${catalina.base}/consumer-eureka-feign/logs"/>
<conversionRule conversionWord="clr"
converterClass="org.springframework.boot.logging.logback.ColorConverter"/>
<conversionRule conversionWord="wex"
converterClass="org.springframework.boot.logging.logback.WhitespaceThrowableProxyConverter"/>
<conversionRule conversionWord="wEx"
converterClass="org.springframework.boot.logging.logback.ExtendedWhitespaceThrowableProxyConverter"/>
<property name="CONSOLE_LOG_PATTERN"
value="${CONSOLE_LOG_PATTERN}:-%clr(%d{yyyy-MM-dd HH:mm:ss.SSS}){faint} %clr([%15.15t]){faint} %clr(%-40.40logger{39}){cyan} %clr(:){faint} %m%n${CONSOLE_LOG_PATTERN:-%wEx}}" />
<property name="FAIL_LOG_PATTERN"
value="%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n"/>
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<level>DEBUGlevel>
filter>
<encoder>
<charset>UTF-8charset>
encoder>
appender>
<appender name="DEBUG_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${log.path}/log_debug.logfile>
<encoder>
<pattern>${FILE_LOG_PATTERN}pattern>
<charset>UTF-8charset>
encoder>
appender>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>${log.path}/debug/log-debug-%d{yyyy-MM-dd}.%i.logfileNamePattern>
<timeBaseFileNamingAndTriggerPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
<maxFileSize>100MBmaxFileSize>
timeBaseFileNamingAndTriggerPolicy>
<maxHistory>15maxHistory>
rollingPolicy>
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>DEBUGlevel>
<onMatch>ACCEPTonMatch>
<onMismatch>DENYonMismatch>
filter>
<appender name="INFO_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${log.path}/log_info.logfile>
<encoder>
<pattern>${FILE_LOG_PATTERN}pattern>
<charset>UTF-8charset>
encoder>
appender>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>${log.path}/info/log-info-%d{yyyy-MM-dd}.%i.logfileNamePattern>
<timeBaseFileNamingAndTriggerPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
<maxFileSize>100MBmaxFileSize>
timeBaseFileNamingAndTriggerPolicy>
<maxHistory>15maxHistory>
rollingPolicy>
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>INFOlevel>
<onMatch>ACCEPTonMatch>
<onMismatch>DENYonMismatch>
filter>
<appender name="WARN_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${log.path}/log_warn.logfile>
<encoder>
<pattern>${FILE_LOG_PATTERN}pattern>
<charset>UTF-8charset>
encoder>
appender>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>${log.path}/warn/log-warn-%d{yyyy-MM-dd}.%i.logfileNamePattern>
<timeBaseFileNamingAndTriggerPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
<maxFileSize>100MBmaxFileSize>
timeBaseFileNamingAndTriggerPolicy>
<maxHistory>15maxHistory>
rollingPolicy>
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>WARNlevel>
<onMatch>ACCEPTonMatch>
<onMismatch>DENYonMismatch>
filter>
<appender name="ERROR_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${log.path}/log_error.logfile>
<encoder>
<pattern>${FILE_LOG_PATTERN}pattern>
<charset>UTF-8charset>
encoder>
appender>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>${log.path}/error/log-error-%d{yyyy-MM-dd}.%i.logfileNamePattern>
<timeBaseFileNamingAndTriggerPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
<maxFileSize>100MBmaxFileSize>
timeBaseFileNamingAndTriggerPolicy>
<maxHistory>15maxHistory>
rollingPolicy>
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>ERRORlevel>
<onMatch>ACCEPTonMatch>
<onMismatch>DENYonMismatch>
filter>
<logger name="myLog" level="INFO" adddivity="false">
<appender-ref ref="CONSOLE"/>
logger>
<root level="DEBUG">
<appender-re ref="CONSOLE"/>
<appender-re ref="DEBUG_FILE"/>
<appender-re ref="INFO_FILE"/>
<appender-re ref="WARN_FILE"/>
<appender-re ref="ERROR_FILE"/>
root>
configuration>
启动类 或者 配置类中加入
// feign 中的 Logger
@Bean
public Logger.Level getLog(){
return Logger.Level.FULL;
}
application.xml 中
# 日志局部定义
feign:
client:
config:
provider: # 需要调用的服务
loggerLevel: FULL
Feign 的负载均衡底层使用 Ribbon,这里的请求超时其实就是配置 Ribbon
分布式系统中,服务压力比较大的情况下,可能处理服务的过程需要花费一定的时间,而默认情况下的请求超时的配置是 1s,所以需要调整该配置,延长请求超时时间。
# 配置请求超时时间
feign:
client:
config:
# 全局 配置请求超时时间
default:
connectTimeout: 1000 # 请求连接超时时间 默认为 1s
readTimeout: 1000 # 请求处理的超时时间
feign:
client:
config:
# 局部 配置请求超时时间
provider: # 服务名
OkToRetryOnAllOperations: true # 对所有请求都进行重试
MaxAutoRetries: 2 # 对当前实例的重复次数
MaxAutoRetriesNextServer: 0 # 切换实例的重复次数
ConnectTimeOut: 3000 # 请求连接超时时间 默认为 1s
ReadTimeOut: 3000 # 请求处理的超时时间# 局部 配置 请求请求超时