引言:
SpringCloud Alibaba 系列目录
一、搭建SpringCloud工程,请前往:保姆级教程构建SpringCloud工程(图文结合)
二、集成服务注册配置管理中心Nacos,请前往:SpringCloud集成服务注册配置管理中心Nacos
三、集成HTTP客户端工具OpenFeign,请前往:SpringCloud集成HTTP客户端工具OpenFeign
四、集成统一网关SpringCloud Gateway,请前往:SpringCloud集成微服务API网关Gateway(详解)
使用HTTP客户端Feign代替RestTemplate
1.Feign入门
1.RestTemplate方式调用存在的问题
以前使用RestTemplate发起远程调用的代码
//2.查询订单的支付类型
String url = "http://localhost:8088/paymentservice/payment/findById/"+ sysOrder.getPaymentType();
SysPaymentType paymentType = restTemplate.getForObject(url,SysPaymentType.class);
存在以下问题:
- 参数复杂URL难以维护
- 代码可读性差,编码体验不统一
2.Feign的介绍
官网 https://github.com/OpenFeign/feign
Feign 是一个声明式客户端,受到 Retrofit、JAX-RS 2.0 和 WebSocket 启发的 Java 到 HTTP 客户端绑定器。Feign 的首要目标是降低将 Denominator 统一绑定到 HTTP API 的复杂性,而不管其是否符合 RESTful 规范。
其作用就是帮助我们优雅的实现http请求的发送,解决上面提到的问题。
3.引入Feign依赖
1.在orderservice服务中引入 openfeign依赖
<!-- Feign -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
2…在orderservice的启动类添加注解开启Feign的功能:
@SpringBootApplication
@MapperScan("${mybatis-plus.mapperPackage}")
@ComponentScan({"com.waves.order", "com.waves.core"})
//添加注解开启Feign的功能
@EnableFeignClients
public class OrderApplication {
public static void main(String[] args) {
SpringApplication.run(OrderApplication.class,args);
System.out.println("订单服务启动成功!o(* ̄▽ ̄*)ブ");
}
}
4.使用Feign客户端
使用Feign客户端步骤如下:
1.编写Feign客户端
- 主要是基于SpringMVC的注解来声明远程调用的信息,比如:
- 服务名称:paymentservice
- 请求方式:GET
- 请求路径:/findById/{payId}
- 请求参数:Long id
- 返回值类型:SysPaymentType
/**
* @author waves
* @date 2024/5/24 14:36
*/
@FeignClient("paymentservice")
public interface PaymentClient {
/**
* 查询支付类型
* @param payId
* @return
* 主要是基于SpringMVC的注解来声明远程调用的信息,比如:
* 服务名称:paymentservice
* 请求方式:GET
* - paymentservice 项目路径 - payment Controller上的@RequestMapping - /findById/{payId} 接口路径
* 请求路径:/paymentservice/payment/findById/{payId}
* 请求参数:Long id
* 返回值类型:SysPaymentType
*/
@GetMapping("/paymentservice/payment/findById/{payId}")
SysPaymentType findById(@PathVariable("payId") Long payId);
}
2.使用Feign客户端替代RestTemplate
@Autowired
private PaymentClient paymentClient;
/**
* 查询单个订单
* @return sysOrder
*/
@GetMapping("/getOrder/{orderId}")
public SysOrder getOrder(@PathVariable("orderId") Long orderId){
//1.查询订单
SysOrder sysOrder = sysOrderService.getById(orderId);
//2.查询订单的支付类型
SysPaymentType paymentType = paymentClient.findById(sysOrder.getPaymentType());
//3.封装支付类型信息
sysOrder.setPaymentTypeData(paymentType);
return sysOrder;
}
如果启动报错
org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'scopedTarget.sysOrderController': Unsatisfied dependency expressed through field 'paymentClient'; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'com.waves.order.web.feign.PaymentClient': Unexpected exception during bean creation; nested exception is java.lang.IllegalStateException: No Feign Client for loadBalancing defined. Did you forget to include spring-cloud-starter-loadbalancer?
在项目中引入 spring-cloud-starter-loadbalancer
<!-- 它提供了客户端负载均衡的功能。
在Spring Cloud中,当与Feign或RestTemplate等HTTP客户端结合使用时,
这个启动器允许你使用服务发现(如Eureka、Consul、Nacos等)来动态解析服务地址,并实现请求的负载均衡。-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-loadbalancer</artifactId>
</dependency>
2.自定义Feign配置
Feign在运行期间自定义配置可以覆盖默认配置,一般情况只需要配置日志级,其他默认就行了。
可以修改配置如下所示:
类型 | 作用 | 说明 |
---|---|---|
feign.Logger.Level | 修改日志级别 | 包含四种不同的级别:NONE、BASIC、HEADERS、FULL |
feign.codec.Decoder | 响应结果的解析器 | http远程调用的结果做解析,例如解析json字符串为java对象 |
feign.codec.Encoder | 请求参数编码 | 将请求参数编码,便于通过http请求发送 |
feign. Contract | 支持的注解格式 | 默认是SpringMVC的注解 |
feign. Retryer | 失败重试机制 | 请求失败的重试机制,默认是没有,不过会使用Ribbon的重试 |
1.自定义Feign日志
自定义Feign日志有两种形式:
第一种: 配置文件的方式
-
全局生效
-
feign: client: config: default: # 这里用default就是全局配置,如果是写服务名称,则是针对某个微服务的配置 loggerLevel: FULL # 日志级别
-
-
局部生效
-
feign: client: config: paymentservice: # 写服务名称,则是针对某个微服务的配置 loggerLevel: FULL # 日志级别
-
第二种:java Bean 形式
/**
* @author waves
* @date 2024/5/24 15:00
*/
public class FeignClientConfiguration {
@Bean
public Logger.Level feignLogLevel(){
return Logger.Level.BASIC;
}
}
全局配置,把FeignClientConfiguration.class放入
@EnableFeignClients(defaultConfiguration = FeignClientConfiguration.class)
局部配置
@FeignClient(value = "paymentservice", configuration = FeignClientConfiguration.class)
2.feign性能优化
Feign的底层客户端实现如下:
- URLConnection:时Feign的默认实现,不支持连接池
- Apache HttpClient: 支持连接池
- OKHttp:支持连接池
所以优化Feign的性能主要包括:
- 使用连接池代替默认的URLConnection
- 日志级别,最好用basic或者none
连接池配置
在项目中添加HttpClient的支持:
引入依赖:
<!-- HttpClient的依赖 -->
<dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-httpclient</artifactId>
</dependency>
配置连接池:
#Feign 日志配置
feign:
client:
config:
default: # 这里用default就是全局配置,如果是写服务名称,则是针对某个微服务的配置
loggerLevel: FULL # 日志级别
paymentservice: # 写服务名称,则是针对某个微服务的配置
loggerLevel: FULL # 日志级别
httpclient:
#开启Feign对httpclient的支持
enabled: true
#最大连接数
max-connections: 200
#每个路径最大连接数
max-connections-per-route: 50
3.Feignd的最佳实践
方式一(继承): 给消费者的FeignClient和提供者的Controller定义统一的接口作为标准
也就是说 定义一个公共接口
public interface PaymentApi{
@GetMapping("/findById/{payId}")
SysPaymentType findById(@PathVariable("payId") Long payId);
}
然后 PaymentClient 继承 PaymentApi,而SysPaymentController 实现PaymentApi
@FeignClient("paymentservice")
public interface PaymentClient extends PaymentApi {
}
@RestController
public class SysPaymentController implements PaymentApi {
/**
* 根据支付Id查询支付类型
* @param payId
* @return
*/
@Override
public SysPaymentType findById(@PathVariable("payId") Long payId){
return paymentService.getById(payId);
}
}
将 PaymentApi 类放在 cloud-common-core 下,order服务和payment服务都引入了core,所以可以客户端继承PaymentApi,服务端实现PaymentApi
但是Spring官方不推荐这种方式:
意思就是说:
一般情况下不推荐在服务和客户端之间去共享接口,因为会造成紧耦合,而且这种继承方案是不对SpringMVC起作用的,因为方案参数时继承不下来的,例如:@PathVariable(“payId”) Long payId
第二种(抽取):将FeignClient抽取作为独立模块,并且把接口有关的POJO,默认的Feign配置都放在这个模块中,提供给所有消费者使用
- 这里就不新建模块了,因为实体POJO都在cloud-common-core下,直接新建一个feign-api包
- 同时将 FeignClientConfiguration ,PaymentClient都复制到 cloud-common-core 项目中,引入Feign,httpclient依赖。
- orderservice 已经引入cloud-common-core ,不需要再引入了,修改orderservice中导入的包都改为cloud-common-core 中的
- 重启测试
当定义的FeignClient不在SpringBootApplication的扫描包范围内时,这些FeignClient无法使用。
启动报错为:
Description:
Field paymentClient in com.waves.order.web.controller.SysOrderController required a bean of type 'com.waves.core.web.feignapi.PaymentClient' that could not be found.
The injection point has the following annotations:
- @org.springframework.beans.factory.annotation.Autowired(required=true)
Action:
Consider defining a bean of type 'com.waves.core.web.feignapi.PaymentClient' in your configuration.
有两种方式可以解决:
方式一:指定FeignClient所在的包
@EnableFeignClients(basePackages = "com.waves.core.web.feignapi")
方式二:指定FeignClient字节码
@EnableFeignClients(clients = {PaymentClient.class})