SpringCloud集成HTTP客户端工具OpenFeign

引言:

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})
  • 8
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

旧歌*

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值