Feign:熔断,@FeignClient注解参数简介,优化
Feign中本身已经集成了Ribbon依赖和自动配置,因此我们不需要额外引入依赖,也不需要再注册 RestTemplate 对象。由代码可知,我们是使用拼接字符串的方式构造URL的,该URL只有一个参数。但是,在现实中,URL 中往往含有多个参数。SpringCloud中,使用Feign非常简单——创建一个接口,并在接口上添加。整合了hystrix,所以添加Feign依赖后就不用在添加hystri
相对于RestTemplate来说更加易懂 ,操作方面更加动态化。代码如下:
@GetMapping("/buy/{id}") public Product order() { Product product = restTemplate.getForObject("http://shop-serviceproduct/product/1",Product.class); return product; }
由代码可知,我们是使用拼接字符串的方式构造URL的,该URL只有一个参数。但是,在现实中,URL 中往往含有多个参数。
Feign简介
相关依赖:
<!--springcloud整合的openFeign-->
<dependency><groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
Feign是Netflflix开发的声明式,模板化的HTTP客户端。
SpringCloud中,使用Feign非常简单——创建一个接口,并在接口上添加@FeignClient注解。
在消费者的微服务启动类上加上@EnableFeignClients注解开启Spring Cloud Feign的支持功能。
package com.zb;
import com.zb.service.impl.TestServiceImpl;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.openfeign.EnableFeignClients;
import org.springframework.context.ConfigurableApplicationContext;
/**
* 启动类
*/
@EnableFeignClients
@SpringBootApplication
public class UserApplication {
public static void main(String[] args) {
SpringApplication.run(UserApplication.class,
}
}
Feign本身不直接支持负载均衡策略,而是通过集成Ribbon来实现负载均衡。因此,Feign可以使用Ribbon支持的各种负载均衡策略,包括轮询、随机、权重、最佳可用等。
服务熔断
SpringCloud Fegin默认已为Feign整合了hystrix,所以添加Feign依赖后就不用在添加hystrix。
yml配置开启熔断
feign:
hystrix:
enabled: true
package com.zb.client;
import com.zb.entity.User;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
/**
* feign接口
*/
@FeignClient(value = "user-server",fallback = UserFeignClientImpl .class)
public interface UserFeignClient {
@GetMapping("/user/all")
String all(@RequestParam("name")User user);
}
package com.zb.client.impl;
import com.zb.client.UserFeignClient;
import com.zb.entity.User;
import org.springframework.stereotype.Component;
/**
* 熔断方法
*/
@Component
public class UserFeignClientImpl implements UserFeignClient {
@Override
public String all(User user) {
return "异常!";
}
}
@FeignClient 注解
注解中参数
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//
package org.springframework.cloud.openfeign;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.core.annotation.AliasFor;
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface FeignClient {
@AliasFor("name")
String value() default "";
/** @deprecated */
@Deprecated
String serviceId() default "";
String contextId() default "";
@AliasFor("value")
String name() default "";
String qualifier() default "";
String url() default "";
boolean decode404() default false;
Class<?>[] configuration() default {};
Class<?> fallback() default void.class;
Class<?> fallbackFactory() default void.class;
String path() default "";
boolean primary() default true;
}
参数介绍
- value 与 name 两者都可指定提供者服务名称。@FeignClient("user-server")默认指定服务名称。
@FeignClient(value = "user") @FeignClient(name = "user") @FeignClient("user")
- fallback 与 fallbackFactory
fallback 定义容错的处理类,当调用远程接口失败或超时时,会调用对应容错方法
fallbackFactory是可以捕获到Feign接口所有发生的异常,并且同样可以实现fallback相关接口来进行自定义回滚代码或者日志记录等等。还可以减少重复的代码@FeignClient(value = 'user', fallback = UserFeignClientImpl.class) @FeignClient(value = 'user', fallbackFactory = UserFallbackFactory.class)
-
path这个参数完全不影响Feign接口的使用。
@FeignClient(value = 'user',path = '/user')
定义当前FeignClient的统一前缀
-
primary 默认值为true。可以将本类转化成Bean的
- url: 用于调试,可以手动指定@FeignClient调用的地址
@FeignClient(value = 'user',url = "http://localhost:9090/pay/create")
- decode404 : 默认false,当发生http 404错误时,如果该字段位true,会调用decoder进行解码,否则抛出FeignException。
@FeignClient(value = "user-server",decode404 = true)
- configuration: Feign配置类,可以自定义Feign的Encoder、Decoder、LogLevel、ContractcontextId:可以位服务名相同的feign接口设定别名来解决同名问题
// 第一个feign接口 @FeignClient(value = "user",contextId = "aa") // 第二个feign接口 @FeignClient(value = "user",contextId = "bb")
Feign性能优化
主要包括:
- 使用带连接池的http请求替代默认的URLConnection
- 日志级别,用打印最少的(basic或者none)
1.使用日志 与 超时问题
Feign中本身已经集成了Ribbon依赖和自动配置,因此我们不需要额外引入依赖,也不需要再注册 RestTemplate 对象。负载均衡底层用的就是Ribbon,所以这里的请求超时配置其实就是配置Ribbon,当出现请求超时会出现以下报错。
feign:
client:
config:
default: #指定feignclients对应的名称 如果指定的是default 表示全局所有的client 的超时时间设置
connectTimeout: 10000 #连接超时时间
readTimeout: 10000 #读取超时时间
loggerLevel: basic #设置日志记录级别
spring:
main:
#声明spring框架是否允许定义重名的bean对象覆盖原有的bean(默认是false)
allow-bean-definition-overriding: true
- FULL:记录基本信息以及请求和响应头信息、请求和响应体信息
- NONE:没有日志记录
- BASIC:记录请求方法、URL以及响应状态代码和执行时间
- HEADERS:记录基本信息以及请求和响应头信息
注意 :当开启日志配置之后,feign超时问题不会出现报错情况。
2.使用连接池
HTTP连接需要经过三次握手,四次挥手的过程,这是很耗费性能的;所以HTTP连接池帮助我们节省了这一步。
同时Feign的HTTP客户端支持三种框架:
HttpURLConnection、HttpClient、OkHttp;默认是HttpURLConnection
- pom.xml中引入Apache的HttpClient依赖
<dependency> <groupId>io.github.openfeign</groupId> <artifactId>feign-httpclient</artifactId> </dependency>
- 配置连接池
在yml文件中添加如下配置:(连接池引入之后默认是开启的,配合可以不写)feign: client: config: default: loggerLevel: BASIC httpclient: enabled: true #开启feign对httpClient的支持 max-connections: 200 #最大的连接数 max-connections-per-route: 50 #每个路径的最大连接数
3.gzip压缩
Gzip 是GNUzip的缩写,最早用于UNIX系统的文件压缩。gzip压缩比率在3到10倍左右,可以大大节省服务器的网络带宽。而在实际应用中,并不是对所有文件进行压缩,通常只是压缩静态文件。
server:
compression:
enabled: true #开启压缩
Gzip解压缩代码实现
1.运行虚拟机
2.在Idea的Maven中打包代码
3.将打包文件拖入虚拟机中
4.执行jar包
客户端和服务器之间通信支持gzip图
常见问题
Feign 的 maven 依赖报红,或者主启动类上@EnableFeignClients不识别,一直报红,或者 feign 接口注入到控制器报红。
可能的原因及处理方案:
1.maven 包没有成功导入,或者 maven 包下载不完整,可以先clean在compile一下删掉依赖包后重新下载导入。
2.版本冲突,可以在 maven 包导入时指定版本号,尝试其他可用的版本。
面试题
1.Feign与RestTemplate之间有什么区别?
Feign和RestTemplate都用于进行HTTP请求,但它们有一些区别:
Feign:
Feign是声明性的HTTP客户端,通过接口和注解来定义服务调用。
Feign自动生成HTTP请求代码,开发者只需定义接口和方法。
集成了Ribbon和Hystrix,支持负载均衡和服务降级。
更具有声明性和简洁性,适用于微服务架构中的服务调用。
RestTemplate:
RestTemplate是经典的HTTP客户端,需要手动编写HTTP请求代码。
开发者需要构建HTTP请求、处理请求和响应、处理异常等。
可以用于调用任何HTTP服务,不仅限于微服务架构。
更灵活,适用于需要更多控制的场景。
选择使用Feign还是RestTemplate取决于项目的需求和开发团队的偏好。在微服务架构中,Feign通常更受欢迎,因为它提供了更高级别的抽象和更简洁的代码。
2.使用Feign时怎样传递Header的参数?
// 1.通过 @RequestHeader(name = "name") 来传递
@FeignClient(name = "xxx-service-name")
public interface XxxFeignClient {
@RequestMapping(value = "/user/info")
String userTicket(@RequestHeader(name = "token") String token);
}
这种方式的缺点和明显,就是如果传递多个Header参数,则需要在接口方法定义的时候都添加进去,显得比较繁琐。
第二种方式,就是通过定义一个配置类实现RequestInterceptor接口,然后将这个配置类添加进@FeignClient注解中即可,代码示例如下:
@Configuration
public class HeaderConfiguration implements RequestInterceptor {
@Override
public void apply(RequestTemplate requestTemplate) {
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request = attributes.getRequest();
Enumeration headerNames = request.getHeaderNames();
if (headerNames != null) {
// 循环取所有header,将header的内容都放入requestTemplate
while (headerNames.hasMoreElements()) {
String name = headerNames.nextElement();
String values = request.getHeader(name);
requestTemplate.header(name, values);
}
}
}
}
// 然后将自定义的配置类配置到Feign中,实现该Feign中接口Header参数的传递
@FeignClient(value = "user-server", configuration = HeaderConfiguration.class)
public interface UserFeignClient {
@RequestMapping(value = "/user/info")
String userInfo();
}
开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!
更多推荐
所有评论(0)