Feign是一个http客户端,用来替代我们前面在 '实用篇-Eureka注册中心' 学的RestTemplate
前提: 去掉前面环境隔离的代码,还有建议别使用集群搭建,尽可能避免因为其它之前代码的原因无法进行下面的实验
先来看RestTemplate发起远程调用的示例代码
//2.利用RestTemplate发起http请求,查询用户
//2.1 url路径
String url = "http://UserService:8081/user/" + order.getUserId();
//这个方法第一个参数是访问路径,第二个参数是把响应得到的Json数据封装成实体类对象
User user = restTemplate.getForObject(url, User.class);
RestTemplate方式调用存在的问题:
Feign的介绍
Feign是一个声明式的http客户端,作用是帮助我们优雅的实现http请求的发送,解决RestTemplate复杂URL的问题。官网如下
https://github.com/OpenFeign/feign
如何定义和使用Feign,具体操作如下:
第一步: 在order-service微服务项目的pom.xml添加如下
org.springframework.cloud
spring-cloud-starter-openfeign
第二步: 在order-service微服务项目的OrderApplication启动类添加如下
@EnableFeignClients
第三步: 在order-service微服务项目的src/main/java/cn/itcast/order目录新建clients.UserClient接口写入如下
package cn.itcast.order.clients;
import cn.itcast.order.pojo.User;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
@FeignClient("UserService")
public interface UserClient {
@GetMapping("/user/{id}")
User findById(@PathVariable("id") Long id);
}
第四步: 把order-service微服务项目的service目录的OrderService类,修改为如下
package cn.itcast.order.service;
import cn.itcast.order.clients.UserClient;
import cn.itcast.order.mapper.OrderMapper;
import cn.itcast.order.pojo.Order;
import cn.itcast.order.pojo.User;
import org.checkerframework.checker.units.qual.A;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
import java.sql.PreparedStatement;
@Service
public class OrderService {
@Autowired
private OrderMapper orderMapper;
@Autowired
private UserClient userClient;
public Order queryOrderById(Long orderId){
//查询订单
Order order = orderMapper.findById(orderId);
//用Feign远程调用
User user = userClient.findById(order.getUserId());
//封装
order.setUser(user);
//返回
return order;
}
// @Autowired
// private RestTemplate restTemplate;
//
// public Order queryOrderById(Long orderId) {
// // 1.查询订单
// Order order = orderMapper.findById(orderId);
// //2.利用RestTemplate发起http请求,查询用户
// //2.1 url路径
// String url = "http://UserService:8081/user/" + order.getUserId();
// //这个方法第一个参数是访问路径,第二个参数是把响应得到的Json数据封装成实体类对象
// User user = restTemplate.getForObject(url, User.class);
// //3.封装user到Order
// order.setUser(user);
// // 4.返回
// return order;
// }
}
第五步: 测试。浏览器访问http://localhost:8081/order/102,看是否能请求到UserService服务的数据
注意一定要注释掉前面配置的命名空间,我这里是把上一节创建的namespace给删除了,但是order-service的配置文件中并没有修改配置,这里我们需要把这里注释掉
访问到了
这是因为Feign已经帮我们实现好了负载均衡
Feign运行自定义配置来覆盖默认配置,可以修改的配置如下表
类型 |
作用 |
说明 |
feign.Logger.Level |
修改日志级别 |
包含四种不同的级别: NONE、BASIC、HEADERS、FULL |
feign.codec.Decoder |
响应结果的解析器 |
http远程调用的结果做解析,例如把json字符串转为java对象 |
feign.code.Encoder |
请求参数编码 |
将请求参数编码,便于通过http请求发送 |
feign.Contract |
支持的注解格式 |
默认是SpringMVC的注解 |
feign.Retryer |
失败重试机制 |
请求失败的重试机制,默认是没有,不过会使用Ribbon的重试 |
一般我们需要配置的就是日志级别。日志的四种日志级别的作用如下
修改日志级别有两种方式
第一种方式:修改配置文件
第一步:在order-service微服务项目的application.yml添加如下
# 第一种自定义feign配置的方式
feign:
client:
config:
# default表示OrderService微服务向任意微服务发送请求时,都会以这种生效feign配置
# 除了default还可以写具体的微服务名,表示当OrderService微服务向特定微服务发送请求时,这种feign配置才会生效
default:
# loggerLevel是固定写法,不能改其它名字
loggerLevel: FULL
第二步: 重启order-service微服务,浏览器访问 http://localhost/order/105,打开控制台,看日志
第二种方式: java代码方式
为避免第一种方式的影响,我们需要先把第一种配置文件的相关代码注释掉
第一步: 在order-service微服务项目的src/main/java/cn/itcast/order目录新建config.DefaultFeignConfiguration类,写入如下
package cn.itcast.order.config;
import feign.Logger;
import org.springframework.context.annotation.Bean;
public class DefaultFeignConfiguration {
@Bean
public Logger.Level logLevel(){
//日志级别不演示FULL,看一下BASIC级别的日志信息是什么样的
return Logger.Level.BASIC;
}
}
第二步: 分两种,一种是局部。另一种是全局
局部: 当OrderService微服务向特定微服务发送请求时,这种feign配置才会生效。
那么第二步就应该是在order-service微服务项目的UserClient接口的@FeignClient注解里面添加如下。只需要修改@FeignClient注解即可
package cn.itcast.order.clients;
import cn.itcast.order.config.DefaultFeignConfiguration;
import cn.itcast.order.pojo.User;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
/**
* @author 35238
* @date 2023/4/26 0026 17:19
*/
//这个接口就是用来发请求的客户端
@FeignClient(value ="UserService",configuration = DefaultFeignConfiguration.class)//括号里面写服务名称,要向哪个服务发送请求
public interface UserClient {
@GetMapping("/user/{id}")
User xxfindById(@PathVariable("id") Long id);
}
全局: 不管OrderService微服务向哪个微服务发送请求时,feign配置都会生效。
那么第二步就应该是在order-service微服务项目的OrderApplication启动类添加如下。只需要修改@EnableFeignClients注解即可
package cn.itcast.order;
import cn.itcast.order.config.DefaultFeignConfiguration;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.cloud.openfeign.EnableFeignClients;
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestTemplate;
@MapperScan("cn.itcast.order.mapper")
@SpringBootApplication
@EnableFeignClients(defaultConfiguration = DefaultFeignConfiguration.class)
public class OrderApplication {
public static void main(String[] args) {
SpringApplication.run(OrderApplication.class, args);
}
// /**
// * 创建RestTemplate并注入Spring容器
// * @return
// */
// @Bean
// @LoadBalanced
// public RestTemplate restTemplate(){
// return new RestTemplate();
// }
// /**
// * 配置负载均衡规则为随机
// * 注入spring容器
// * @return
// */
// @Bean
// public IRule randomRule(){
// return new RandomRule();
// }
}
这里我们仅演示全局配置,重启order-service,访问http://localhost:8081/order/102
在正常使用的情况下,日志级别建议设置为BASIC。在调试错误的信息,日志级别建议设置为FULL
Feign其实是有优化的空间,我们下面就学习如何把Feign进行性能优化。Feign是声明式的客户端,帮助我们把声明变成http请求,但是最终发送http请求时,还是会用到别的一些客户端,默认使用的是URLConnection这种客户端,URLConnection是jdk自带的
Feign的底层实现,或者说Feign的底层客户端实现:
因此优化Feign的性能主要包括:
例如我们把Feign底层客户端换成HttpClient。具体操作如下
第一步: 在order-service微服务项目的pom.xml,添加如下
io.github.openfeign
feign-httpclient
第二步: 在order-service微服务项目的application.yml,添加如下
# 更换feign客户端为httpClient
feign:
httpclient:
enabled: true #支持HttpClient的开关
max-connections: 200 #最大连接数
max-connections-per-route: 50 #请求路径的最大连接数
添加完后,application.yml文件如下
server:
port: 8080
spring:
datasource:
url: jdbc:mysql://localhost:3306/cloud_order?useSSL=false
username: root
password:
driver-class-name: com.mysql.jdbc.Driver
application:
# order的服务名称。也就是这个order-service注册到Eureka之后,这个order-service会叫什么名字
name: OrderService
# 因为nacos是spring的配置,所以要写在spring配置的下面
cloud:
nacos:
server-addr: localhost:8848
discovery:
cluster-name: HZ
# namespace: d8ec5ecb-2268-4551-ac2d-f08953292b28
# ephemeral: false # 是否是临时实例 false代表否,表示非临时实例
mybatis:
type-aliases-package: cn.itcast.user.pojo
configuration:
map-underscore-to-camel-case: true
logging:
level:
cn.itcast: debug
pattern:
dateformat: MM-dd HH:mm:ss:SSS
# Nacos的负载均衡策略,优先向本地集群发送请求,注意UserService是你本地集群对应的服务名称,也就是注册到注册中心时的服务名称
UserService:
ribbon:
# 负载均衡规则为NacosRule
NFLoadBalancerRuleClassName: com.alibaba.cloud.nacos.ribbon.NacosRule
#eureka:
# client:
# service-url:
# # eureka的服务地址。如果有多个的话,逗号隔开。也就是把当前这个order-service微服务注册给哪个Eureka
# defaultZone: http://localhost:8686/eureka
ribbon:
eager-load:
enabled: true # 开启饥饿加载
# 指定对UserService这个服务开启饥饿加载
#UserService是你注册到Eureka时的服务名称。如果有多个服务需要做饥饿加载,就-往下写
clients:
- UserService
# 第一种自定义feign配置的方式
feign:
httpclient:
enabled: true #支持HttpClient的开关
max-connections: 200 # 最大连接数 实际开发中要根据压测来进行参数设置
max-connections-per-route: 50 # 每一个请求路径下的最大连接数 实际开发中要根据压测来进行参数设置
启动效果跟前面一样的,这里只是改变了连接池
方式一:
继承。给消费者的FeignClient和提供者的controller定义统一的父接口作为标准
特点: 紧耦合,仅适用于面向契约编程的思想上来使用
操作: 让controller和FeignClient继承同一接口
方式二:
抽取。 将FeignClient抽取为独立模块,并且把接口有关的POJO、默认的Feign配置都放到这个模块中,提供给所有消费者使用
特点; 低耦合,但是高冗余
操作: 将FeignClient、POJO、Feign的默认配置都定义到一个项目中,供所有消费者使用
"抽取"方案演示
实现思路
1、首先创建一个module,命名为feign-api,然后引入feign的starter依赖
2、就可以把order-service中的UserClient、User、DefaultFeignConfiguration都复制到feign-api项目中
3、要使用怎么办,直接在order-service中引入feign-api的依赖即可
4、然后修改order-service中的所有与"UserClient、User、DefaultFeignConfiguration"有关的Import部分,改成导入feign-api中的包
具体操作步骤如下
第一步: 在cloud-demo总项目中新建一个项目,项目名为feign-api
第二步: 把feign-api微服务项目的pom.xml修改为如下
cloud-demo
cn.itcast.demo
1.0
4.0.0
feign-api
8
8
org.springframework.cloud
spring-cloud-starter-openfeign
第三步: 在feign-api项目的src/main/java目录下新建cn.itcast.feign包,接着把order-service项目的src/main/java/cn.itcast.order/clients目录、config目录、pojo目录复制到feign-api项目的src/main/java/cn.itcast.feign目录里面。注意粘贴后的clients目录的UserClient接口会爆红,改一下import即可
第四步: 以后哪个项目需要使用Feign时,就直接在pom.xml使用刚写好的feign-api项目即可。例如把order-service项目中使用,我们可以把在order-service项目中有关Feign配置的接口和类删掉(此时order-service项目有关Feign的功能类会报错,等下会解决),然后在order-service项目pom.xml中引入刚写好的feign-api项目依赖,接着在报错的有关Feign功能类里面重新引入一下即可解决报错
cn.itcast.demo
feign-api
1.0
第五步: 解决bug。当我们启动OrderApplication、UserApplication、UserApplication2服务时,会发现报错了,找不到feign-api项目的UserClient接口
在order-service项目的启动类,指定FeignClient字节码,也就是直接指定扫描哪个写好的UserClient,也可以指定多个
@EnableFeignClients(clients = UserClient.class,defaultConfiguration = DefaultFeignConfiguration.class)
第六步。测试(先确保你的nacos已经启动),重启OrderApplication、UserApplication、UserApplication2服务
浏览器访问:http://localhost:8080/order/102