微服务温习笔记(三)
本次温习主题:Feign
一、Feign 是什么?
Feign是Spring Cloud提供的声明式、模板化的HTTP客户端, 它使得调用远程服务就像调用本地服务一样简单,只需要创建一个接口并添加一个注解即可。
Spring Cloud集成Feign并对其进行了增强,使Feign支持了Spring MVC注解;Feign默认集成了Ribbon,所以Fegin默认就实现了负载均衡的效果。
简单示例图:
二、入门案例
2.1创建服务提供者
模块结构:
pom.xml
<artifactId>feign_provider</artifactId>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.exeicise</groupId>
<artifactId>springcloud_common</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
</dependencies>
application.yml
server:
port: 9090
spring:
cloud:
nacos:
discovery:
server-addr: 192.168.116.131:8848 #注册中心的地址
application:
name: feign-provider #注册到nacos的名字
UserService
public interface UserService {
User getUserById(Integer id);
User deleteUserById(Integer id);
User addUser(User user);
}
UserServiceImpl
@Service
public class UserServiceImpl implements UserService {
@Override
public User getUserById(Integer id) {
return new User(id,"曹公公1",18);
}
@Override
public User deleteUserById(Integer id) {
return new User(id,"删除了曹公公1",18);
}
@Override
public User addUser(User user) {
user.setName("新增了曹公公1");
return user;
}
}
ProviderController
@RestController
@RequestMapping("/provider")
public class ProviderController {
@Autowired
private UserService userService;
@RequestMapping("/getUserById/{id}")
public User getUserById(@PathVariable Integer id){
return userService.getUserById(id);
}
@RequestMapping("/deleteUserById")
public User deleteUserById(Integer id){
return userService.deleteUserById(id);
}
@RequestMapping("/addUser")
User addUser(@RequestBody User user){
return userService.addUser(user);
}
}
启动类:FeignProviderApp
@SpringBootApplication
@EnableDiscoveryClient//注册该服务,并发现其它服务
public class FeignProviderApp {
public static void main(String[] args) {
SpringApplication.run(FeignProviderApp.class,args);
}
}
2.2创建feign接口
模块结构:
pom.xml
<modelVersion>4.0.0</modelVersion>
<artifactId>feign_interface</artifactId>
<dependencies>
<!--Spring Cloud OpenFeign Starter -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<!--将springcloud_common引入进来-->
<dependency>
<groupId>com.exercise</groupId>
<artifactId>springcloud_common</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
</dependencies>
接口:UserFeign
@FeignClient("feign-provider")
@RequestMapping("/provider")
public interface UserFeign {
@RequestMapping("/getUserById/{id}")//拼接url
public User getUserById(@PathVariable("id") Integer id);//restful形式拼接参数
@RequestMapping("/deleteUserById")//拼接url
User deleteUserById(@RequestParam("id") Integer id);//?形式拼接参数
@RequestMapping("/addUser")
User addUser(@RequestBody User user);//@RequestBody(原本是将json串转对象,在这里是将对象转json串), pojo--->json(变成了json串)
}
2.3创建服务消费者
模块结构:
pom.xml
<artifactId>ribbon_consumer</artifactId>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.exercise</groupId>
<artifactId>springcloud_common</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<!--feign接口-->
<dependency>
<groupId>com.exercise</groupId>
<artifactId>feign_interface</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
</dependencies>
application.yml
server:
port: 80
spring:
cloud:
nacos:
discovery:
server-addr: 192.168.116.131:8848
application:
name: feign-consumer
ConsumerController
@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);
}
@RequestMapping("/deleteUserById")
public User deleteUserById(Integer id){
return userFeign.deleteUserById(id);
}
@RequestMapping("/addUser")
public User addUser(User user){
return userFeign.addUser(user);
}
}
启动类:ConsumerApp
@SpringBootApplication
@EnableDiscoveryClient
@EnableFeignClients//开启feign注解的扫描
public class FeignConsumerApp {
public static void main(String[] args) {
SpringApplication.run(FeignConsumerApp.class,args);
}
}
测试:
1.访问路径:127.0.0.1/consumer/getUserById/250
2.访问路径:127.0.0.1/consumer/deleteUserById?250
3.访问路径:127.0.0.1/consumer/addUser?id=250&name=cgg&age=18
根据入门案例可以看出Feign参数传递方式:
restful风格:
feign接口:@PathVarible
【拼接restful形式的url】
?传参
feign接口:@RequestParam
【拼接?形式的url】
pojo参数
provider:@RequestBody User user
【获取请求体中的json串】
三、Feign 工作原理
3.1 将Feign接口注入到Spring容器中
@EnableFeignClients注解开启Feign扫描,先调用FeignClientsRegistrar.registerFeignClients()方法扫描@FeignClient注解的接口,再将这些接口注入到Spring IOC容器中,方便后续被调用。
3.2 RequestTemplate封装请求信息
SynchronousMethodHandler.invoke(): 当定义的的Feign接口中的方法被调用时,通过JDK的代理方式为Feign接口生成了一个动态代理类,当生成代理时,Feign会为每个接口方法创建一个RequestTemplate。该对象封装了HTTP请求需要的全部信息,如请url、参数,请求方式等信息都是在这个过程中确定的。
3.3 发起请求
SynchronousMethodHandler.executeAndDecode():
通过RequestTemplate生成Request,然后把Request交给Client去处理,Client可以是JDK原生的URLConnection,Apache的HttpClient,也可以时OKhttp,最后Client结合Ribbon负载均衡发起服务调用。
四、Feign的优化
feign的优化大致分为四部分:
1.开启feign日志
feign:
client:
config:
default:
loggerLevel: full
logging:
level:
com.exercise.feign: debug
2、feign超时
1、方式一:
ribbon:
ConnectTimeout: 5000 #请求连接的超时时间
ReadTimeout: 5000 #请求处理的超时时间
2、方式二:
feign:
client:
config:
feign-provider:
ConnectTimeout: 5000 #请求连接的超时时间
ReadTimeout: 5000 #请求处理的超时时间
如果请求超时控制台会爆出如下异常:
Read timed out executing GET http://feigh-provider/provider/getUserById/250] with root cause
前端页面爆500:
3、http连接池
<dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-httpclient</artifactId>
</dependency>
4、gzip压缩
采用deflate算法data压缩,当gzip咋所到一个纯文本文件时,咋所过后可以减少70%以上的文件大小。
server:
compression:
enabled: true #开启gzip压缩
小结:
feign是spring cloud提供的声明式的http客户端(将注入接口注入给controller)工作在consumer端feign支持springmvc注解 feign集成了Ribbon,所以也支持负载均衡(优化后的ribbon和redistemplate
(ribbon+redistemplate)+ 优化 = feign