---------------------------声明式服务调用Feign--------------------------
一、feign介绍
1、什么是feign?
feign是spring cloud提供的声明式的http客户端,工作在consumer端
feign支持springmvc注解
feign集成ribbon也支持负载均衡(restTemplate+ribbon=feign)
2、feign启动器
spring-cloud-starter-openfeign
二、feign入门案例
1、 创建服务提供者feign_provider
需要spring-*-web和spring-*-nacos-discovery依赖
服务提供者仅仅需要向nacos中注册即可 需要再启动类上面加上@EnableDiscoveryClient即可
1. pom.xml
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.bjpowernode</groupId>
<artifactId>springcloud_common</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<!--nacos客户端-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
</dependencies>
2.application.yml
server:
port: 8090
spring:
cloud:
nacos:
discovery:
server-addr: 192.168.126.132 #注册中心的地址
application:
name: feign-provider #注册到nacos的服务名
3.controller
@RestController
@RequestMapping("/provider")
public class ProviderController {
@Autowired
private UserService userService;
@RequestMapping("/getUserById/{id}")
public User getUserById(@PathVariable Integer id){
return userService.getUserById(id);
}
}
4.service
1) UserService
public interface UserService {
User getUserById(Integer id);
}
2) UserServiceImpl
@Service
public class UserServiceImpl implements UserService {
@Override
public User getUserById(Integer id){
return new User(id,"张三--2",19);
}
}
5.启动类(App)
@SpringBootApplication
@EnableDiscoveryClient
public class FeignPerviderApp {
public static void main(String[] args) {
SpringApplication.run(FeignPerviderApp.class,args);
}
}
2、创建feign_interface接口
接口需要openfeign依赖 然后开放注册到nacos中的provider的某一个接口
仅仅需要再interface上面添加一个@feignclient(注册到nacos的服务名)即可
1、pom.xml
<dependencies>
<!--Spring Cloud OpenFeign Starter -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<dependency>
<groupId>com.bjpowernode</groupId>
<artifactId>springcloud_common</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
</dependencies>
2、feign接口 (UserFeign)
@FeignClient("feign-provider")//这个名字需要跟前面注册到nacos中的provoder的注册服务名字保持一致
@RequestMapping("/provider")//保持与provider一致
public interface UserFeign {
@RequestMapping("/getUserById/{id}")
public User getUserById(@PathVariable("id") Integer id);
}
3、创建服务的消费者feign_consumer
需要spring-*-web和spring-*-nacos-discovery依赖 同时还要将interface类引入依赖
服务消费者需要向nacos中注册同时还需要 扫描onterface包 需要再启动
类上面加上@EnableDiscoveryClient和@EnableFeignClients
1、pom.xml
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<dependency>
<groupId>com.bjpowernode</groupId>
<artifactId>feign_interface</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
</dependencies>
2、application.yml
server:
port: 80
spring:
cloud:
nacos:
discovery:
server-addr: 192.168.126.132:8848 #注册中心地址
application:
name: feign-consumer #注册到nacos的服务名
3、controller
@RestController
@RequestMapping("/consumer")
public class ConsumerController {
@Autowired
private UserFeign userFeign;
@RequestMapping("/getUserById/{id}")
public User getUserById(@PathVariable Integer id){
System.out.println(userFeign.getClass());//代理类
return userFeign.getUserById(id);
}
}
4、启动类App
@SpringBootApplication
@EnableDiscoveryClient
@EnableFeignClients //开启feign接口扫描 注意consumer与interface里面的包名保持一致
public class FeignConsumerApp {
public static void main(String[] args) {
SpringApplication.run(FeignConsumerApp.class,args);
}
}
三、feign原理
@EnableFeignClients===》@ FeignClient ===》接口中方法上的注
解 @RequestMapping("/getUserById/{id}") // @RequestMapping 作用:拼接参数
1、扫描feign接口生成代理类并交给spring容器管理
当服务消费者的启动类启动时,通过注解 @EnableFeignClients 的这个类
(FeignClientsRegistrar )中的 (registerFeignClients)这个方法,扫
描被@ FeignClient标识的接口生成代理类, 然后放入 spring ioc容器中
2、为接口的方法创建RequestTemplate
当controller调用定义的Feign接口中的方法时,通过JDK的代理方式为Feign接口生成了一个动态代理类,
【代理类会调用SynchronousMethodHandler.invoke()创建RequestTemplate(url、requestMethod、body)】
当生成代理时,Feign会为每个接口方法创建一个RequestTemplate。该对象封装了HTTP请求需要的
全部信息,如请url、参数,请求方式等信息都是在这个过程中确定的。
3、发起请求
接着通过RequestTemplate创建Request,然后client(HttpClient、OkHttp、URLConnection)使用Request发送请求
四、feign接口三种传参方式
controller层 传参时 不需要写 @RequestParam 和 @RequestBody 这两个注解否则会报错
接口中三个注解必须都要写
1、?传参
@RequestParam("id") //拼接参数 url?=123
2、restful传参
@PathVariable("id") //拼接参数 url/123
3、pojo传参
@RequestBody 把对象转成串
五、feign优化
feign优化的原因:
Feign的HTTP客户端支持3种框架:HttpURLConnection、HttpClient、OkHttp,底层默认使用
的是URLConnection,这是jdk自带的发送http请求的包,不支持连接池;
这样在发送http请求时,每次都要建立连接(三次握手),发送数据,断开
连接(四次挥手),比较浪费性能、消耗时间。所以使用Apache HttpClient底层实
现;日志级别推荐设置full,
1、开启feign日志(feign_consumer==》application.yml)
feign的日志级别包含下面几个:
NONE:不输出日志
BASIC:输出请求方法及url,响应的状态码及响应时间
HEADERS:输出请求和响应的头信息
FULL:输出请求和响应的请求头,消息体及元数据
feign:
client:
config:
default:
loggerLevel: full #feign显示日志
首先需要设置属性logging.level.+包名:debug。
打开这个属性的原因?
因为feign的logger实现类Slf4jLogger在调用log方法时,
会判断是否开启了debug。
logging:
level:
com.bjpowernode.feign: debug #log4j的日志级别 这个包下com.bjpowernode.feign
2、GZIP压缩
使用GZIP的原因:
当 Gzip 压缩到一个纯文本文件时,效果是非常明显的,大约可以减
少 70% 以上的文件大小,网络数据经过压缩后实际上降低了网络传
输的字节数,最明显的好处就是可 以加快网页加载的速度。网页加载
速度加快的好处不言而喻,除了节省流量,改善用户的浏 览体验外,
另一个潜在的好处是 Gzip 与搜索引擎的抓取工具有着更好的关系。
例如 Google 就可以通过直接读取 gzip 文件来比普通手工抓取 更快地检索网页。
在spring-cloud-openfeign-core.jar文件中.默认对请求和响应
压缩是禁用的,需要我们手动开启
application.yml
server:
compression:
enabled: true #开启浏览器<----->consumer的gzip压缩
feign:
compression:
request:
enabled: true #开启feign<---->provider的gzip压缩
response:
enabled: true
#配置支持压缩的文本 使用默认的即可 deflate这个属性表示算法
mime-types
3、http连接池
在消费者consumer中引入依赖,
pom.xml
<dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-httpclient</artifactId>
</dependency>
4、feign超时
方式一:
application.yml
ribbon:
ConnectionTimeout: 5000 #请求连接的超时时间
ReadTimeout: 5000 #请求处理的超时时间
方式二:
测试:给指定的方法睡眠3000毫秒
@Service
public class UserServiceImpl implements UserService {
@Override
public List<User> addUsers(List<User> userList) {
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return userList;
}
}
application.yml
feign:
client:
config:
feign-provider: # feign-provider:指定的服务 default:也可以是所有的服务
ConnectionTimeout: 5000 #请求连接的超时时间
ReadTimeout: 5000 #请求处理的超时时间