【SpringCloud】-OpenFeign实战及源码解析、与Ribbon结合

这篇具有很好参考价值的文章主要介绍了【SpringCloud】-OpenFeign实战及源码解析、与Ribbon结合。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

一、背景介绍

二、正文

OpenFeign是什么?

OpenFeign(简称Feign)是一个声明式的Web服务客户端,用于简化服务之间的HTTP通信。与Nacos和Ribbon等组件协同,以支持在微服务体系结构中方便地进行服务间的通信; OpenFeign在默认情况下集成了Hystrix,提供了服务容错和服务降级的功能。

OpenFeign的作用是什么?

按照单一职责,也为了满足可复用、可扩展的核心我们可以对整体业务拆分成不同的服务,这样涉及到的一个问题就是某一个服务的逻辑实现需要依托另一个服务的信息,这样服务和服务之间需要通信来进行消息传递,但是服务和服务之间如果直接通信的话耦合关系变强,无法达到高复用的作用。使用了OpenFeign技术来解决这个问题,如果其中一个类需要调用另一个类的某一个方法的话,直接通过OpenFeign这个第三方转发这个调用,减少直接通信带来的耦合关系
【SpringCloud】-OpenFeign实战及源码解析、与Ribbon结合,spring cloud,OpenFeign,服务调用通过使用@FeignClien注解标识UserApiService这个客户端,对【internetbar-provider-user】这个服务远程调用。OpenFeign会根据接口自动创建一个实现类,发起HTTP请求去用Get的方式访问远程服务的【/userManage/getUserInfoById/{userId}】这个接口

Feign和OpenFeign的关系?

OpenFeign是对Feign的增强,对mvc注解的增强,那如何理解这句话呢?
共同点:Feign和OpenFeign都是用于简化服务之间HTTP通信的Web服务客户端
不同点:
所属不同:Feign是Netflix开发HTTP客户端;OpenFeign是SpringCloud对Feign的重新实现和增强,OpenFeign引入了SpringMVC的注解,例如:@GetMapping等注解可以类似于SpringMVC的一些效果
Hystrix集成:OpenFeign默认集成Hystrix,提供服务容错和服务降级的功能;Feign中需要显式地添加Hystrix的依赖并配置
【SpringCloud】-OpenFeign实战及源码解析、与Ribbon结合,spring cloud,OpenFeign,服务调用


Java还有哪些服务调用方式?

Okhttp、HttpURLConnection、RestTemplate


使用OpenFeign和不使用的区别对比?

我们先从代码形式上来看一下两者如何使用:
基于RestTemplate使用HTTP请求调用服务
【SpringCloud】-OpenFeign实战及源码解析、与Ribbon结合,spring cloud,OpenFeign,服务调用

【SpringCloud】-OpenFeign实战及源码解析、与Ribbon结合,spring cloud,OpenFeign,服务调用

基于OpenFeign调用
【SpringCloud】-OpenFeign实战及源码解析、与Ribbon结合,spring cloud,OpenFeign,服务调用
Feign可以根据接口的定义生成客户端所需要的代码,这部分代码被Feign封装到底层,并且底层同步的实现了负载均衡和服务发现、服务容错等功能,不需要我们像使用RestTemplate一样额外的手动去配置,一个简单的API接口就可以帮助我们做了很多事情。



实战

引入依赖

<dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>

在启动类添加@EnableFeignClients注解

@EnableFeignClients(defaultConfiguration = FeignAutoConfiguration.class)

添加FeignClient接口

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;

/**
 * @BelongsProject: cloud-demo
 * @BelongsPackage: cn.itcast.order.clients
 * @CreateTime: 2023-03-17  12:29
 * @Description: TODO
 * @Version: 1.0
 */
@FeignClient("userservice")
public interface UserClient {
    @GetMapping("/user/{id}")
    User findById(@PathVariable("id") Long id);
}

修改逻辑代码

使用RestTemplate**

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.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class OrderService {

    @Autowired
    private OrderMapper orderMapper;

    @Autowired
    private RestTemplate template;

    public Order queryOrderById(Long orderId){
        //1、根据用户id查询用户的订单信息
        Order order = orderMapper.findById(orderId);

        //2、利用RestTemplate发起http请求,查询用户信息
        String url="http://userservice/user/"+order.getUserId();
        User forObject = template.getForObject(url, User.class);

        //3、将user信息封装到Order中
        order.setUser(forObject);

        // 返回
        return order;
    }
}

使用Feign接口

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.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class OrderService {

    @Autowired
    private OrderMapper orderMapper;

    @Autowired
    private UserClient userClient;

    public Order queryOrderById(Long orderId) {
        //1、查询订单
        Order order = orderMapper.findById(orderId);
        //2、用Feign远程调用
        User user = userClient.findById(order.getUserId());
        //3、封装user到Order
        order.setUser(user);
        //4、返回
        return order;
    }
}


自定义配置

配置日志(以debug级别输出 )

当我们进行服务调用的过程中有可能出现接口调用失败的问题,或者我们想要输出请求或响应的详细信息,我们可以配置日志

日志级别有哪些?

NONE:不记录任何日志(默认值)
BASIC:记录请求方法、URL、响应状态代码及执行时间
HEADERS:记录BASIC级别的基础上,记录请求和响应的header
FULL:记录请求和响应的header、body和元数据


方式一:配置文件

全局:所有服务生效
feign:
  client:
    config:
      default:
        loggerLevel: FULL
#某服务生效
feign:
  client:
    config:
      userservice:
        loggerLevel: FULL

方式二:配置类

添加配置类

package cn.itcast.order.config;

import feign.Logger;
import org.springframework.context.annotation.Bean;

/**
 * @BelongsProject: cloud-demo
 * @BelongsPackage: cn.itcast.order.config
 * @CreateTime: 2023-03-17  13:05
 * @Description: TODO
 * @Version: 1.0
 */
public class DefaultFeignConfiguration {

    @Bean
    public Logger.Level feignLogLevel() {
        return Logger.Level.BASIC;
    }
}

方式三:在启动类上添加注解@EnableFeignClients

全局配置:对所有生效

@EnableFeignClients(defaultConfiguration =FeignAutoConfiguration.class)

局部配置:只对userservice服务生效

@EnableFeignClients(value = “userservice”, defaultConfiguration = FeignAutoConfiguration.class)


性能优化

底层的客户端实现:
URLConnection:默认实现,不支持连接池
Apache HttpClient:支持连接池
OKHttp:支持连接池


优化包括
使用连接池代替默认的URLConnection
日志级别,最好用basic或none


实操——Feign工程化

引入feign-httpClient依赖

 <dependency>
       <groupId>io.github.openfeign</groupId>
       <artifactId>feign-httpclient</artifactId>
</dependency>

配置文件开启httpClient功能,设置连接池参数

feign:
  client:
    config:
      default:  #default全局的配置
        loggerLevel: BASIC  #日志级别(基本的请求和响应信息)
  httpclient:
    enabled: true #开启feign对HttpClient的支持
    max-connections: 200  #最大的连接数
    max-connections-per-route: 50 #每个路径的最大连接数

目的:将公共的配置抽取出来

新建一个Maven项目,服务名叫——feign-api

引入依赖

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>cloud-demo</artifactId>
        <groupId>cn.itcast.demo</groupId>
        <version>1.0</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>feign-api</artifactId>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>
    </dependencies>

</project>

第一步、公有接口——工程化核心

在项目中创建一个Feign客户端接口,通过使用@FeignClient注解来声明要调用的服务和服务接口

package cn.itcast.feign.clients;

import cn.itcast.feign.pojo.User;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;

/**
 * @BelongsProject: cloud-demo
 * @BelongsPackage: cn.itcast.order.clients
 * @CreateTime: 2023-03-17  12:29
 * @Description: TODO
 * @Version: 1.0
 */
@FeignClient("userservice")
public interface UserClient {
    @GetMapping("/user/{id}")
    User findById(@PathVariable("id") Long id);
}

第二步、配置类—用于配置Feign客户端的一些属性,如日志级别等

package cn.itcast.feign.config;

import feign.Logger;
import org.springframework.context.annotation.Bean;

/**
 * @BelongsProject: cloud-demo
 * @BelongsPackage: cn.itcast.order.config
 * @CreateTime: 2023-03-17  13:05
 * @Description: TODO
 * @Version: 1.0
 */
public class DefaultFeignConfiguration {

    @Bean
    public Logger.Level feignLogLevel() {
        return Logger.Level.BASIC;
    }
}

实体类

package cn.itcast.feign.pojo;

import lombok.Data;

@Data
public class User {
    private Long id;
    private String username;
    private String address;
}


orderservice服务

第三步、启动类—启用Feign客户端

在Spring Boot应用程序中,通过使用@EnableFeignClients注解启用Feign客户端,并指定要扫描的Feign客户端接口所在的包。

package cn.itcast.order;

import cn.itcast.feign.clients.UserClient;
import com.netflix.loadbalancer.IRule;
import com.netflix.loadbalancer.RandomRule;
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.cloud.openfeign.FeignAutoConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestTemplate;

@MapperScan("cn.itcast.order.mapper")
@SpringBootApplication
@EnableFeignClients(clients = UserClient.class,defaultConfiguration = FeignAutoConfiguration.class)

public class OrderApplication {

    public static void main(String[] args) {
        SpringApplication.run(OrderApplication.class, args);
    }

    /**
     * @Description: 将RestTemplate注册到容器中
     * @Date: 2023/3/13 11:28
     * @return: org.springframework.web.client.RestTemplate
     **/
    @Bean
    @LoadBalanced
    public RestTemplate restTemplate() {
        return new RestTemplate();
    }

    /**
     * @Description: 修改负载均衡策略方式
     * @Date: 2023/3/13 19:56
     * @return: com.netflix.loadbalancer.IRule
     **/
    @Bean
    public IRule randomRule() {
        return new RandomRule();
    }
}

实体类

package cn.itcast.order.pojo;

import cn.itcast.feign.pojo.User;
import lombok.Data;

@Data
public class Order {
    private Long id;
    private Long price;
    private String name;
    private Integer num;
    private Long userId;
    private User user;
}

Mapper类

package cn.itcast.order.mapper;

import cn.itcast.order.pojo.Order;
import org.apache.ibatis.annotations.Select;

public interface OrderMapper {

    @Select("select * from tb_order where id = #{id}")
    Order findById(Long id);
}

Controller类

package cn.itcast.order.web;

import cn.itcast.order.pojo.Order;
import cn.itcast.order.service.OrderService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/order")
public class OrderController {
   @Autowired
   private OrderService orderService;

    @GetMapping("/{orderId}")
    public Order queryOrderByUserId(@PathVariable("orderId") Long orderId) {
        // 根据id查询订单并返回
        return orderService.queryOrderById(orderId);
    }
}

第四步、使用Feign客户端—service类

在需要调用远程服务的地方,注入Feign客户端并使用

package cn.itcast.order.service;

import cn.itcast.feign.clients.UserClient;
import cn.itcast.feign.pojo.User;
import cn.itcast.order.mapper.OrderMapper;
import cn.itcast.order.pojo.Order;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class OrderService {

    @Autowired
    private OrderMapper orderMapper;

    @Autowired
    private UserClient userClient;

    public Order queryOrderById(Long orderId) {
        //1、查询订单
        Order order = orderMapper.findById(orderId);
        //2、用Feign远程调用
        User user = userClient.findById(order.getUserId());
        //3、封装user到Order
        order.setUser(user);
        //4、返回
        return order;
    }
}

通过上述的四个步骤(公有接口、配置类、启动类—启用Feign客户端、使用Feign客户端—service类)我们就实现了Feign的工程化,以声明式的方式定义了服务调用接口,配置了Feign客户端

基于工程化的思想,所有的服务调用被抽成公共的API,单独放在一个包中,我们只需要调用公共接口,不需要写太多重复代码,哪个服务需要调用引用对应的包就可以了,避免冗余代码,这也是设计模式思想(可复用)的体现。


集成Hystrix

我们需要先了解雪崩效应,通过OpenFeign进行多服务之间调用,如果服务提供者出现了故障将会导致服务消费者不可用。比方说login服务需要调用user服务获取登录用户的基本信息,此时user服务可能服务挂掉或者被攻击了,此时login服务可能将会处于等待过程,我们可以使用Hystrix进行熔断,当远程服务调用失败或超时时,熔断器会出发,并执行降级逻辑。
关于OpenFeign和Hystrix的集成使用请等待我之后关于Hystrix的博客详解哦~~~


源码解析

1、Spring启动时初始化-生成代理类

在服务启动时,Spring会加载配置并且初始化Spring容器、扫描并加载项目当中用到的组件,此时OpenFeign的配置也会被程序加载(包括:自定义Feign配置类、负载均衡配置、熔断器配置等),扫描带有@FeignClient注解的接口,Feign会使用动态代理生成代理类并注册到Spring容器中

2、服务发现和负载均衡

Nacos注册中心:如果项目中用到了Nacos注册中心,Feign会与Nacos集成通过服务名进行服务发现
Ribbon负载均衡:如果项目中使用了Ribbon,可以实现对服务实例的负载均衡
大家可以看下面这张图片,我们点进OpenFeign的依赖,会发现里面自动集成了Ribbon
【SpringCloud】-OpenFeign实战及源码解析、与Ribbon结合,spring cloud,OpenFeign,服务调用
那Feign和Ribbon、RestTemplate三者之间的关系是什么样的呢?
【SpringCloud】-OpenFeign实战及源码解析、与Ribbon结合,spring cloud,OpenFeign,服务调用

3、请求处理

场景:服务A调用服务B
当服务A通过OpenFeign的方式调用服务B时,实际上是通过代理类最终调用LoadBalancerFeignClient的execute方法,execute方法内部整合了Ribbon去实现负载均衡,最终会找到真实要请求的服务地址,从而发送RibbonRequest请求,返回响应内容给服务A
这也是Feign的核心代码:
【SpringCloud】-OpenFeign实战及源码解析、与Ribbon结合,spring cloud,OpenFeign,服务调用


为什么Feign第一次调用耗时很长?

这个问题是我在网上看到的一个问题,大家也可以参考参考图中说到的原因:
【SpringCloud】-OpenFeign实战及源码解析、与Ribbon结合,spring cloud,OpenFeign,服务调用



三、总结

OpenFeign帮助我们简化了服务之间的通信,同时继承了负载均衡、熔断器等功能,帮助我们更好的实现服务调用。之后我将会针对OpenFeign中集成的Hystrix做分享,大家敬请期待文章来源地址https://www.toymoban.com/news/detail-765985.html


如果有想要交流的内容欢迎在评论区进行留言,如果这篇文档受到了您的喜欢那就留下你点赞+收藏+评论脚印支持一下博主~


到了这里,关于【SpringCloud】-OpenFeign实战及源码解析、与Ribbon结合的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Spring Cloud OpenFeign:基于Ribbon和Hystrix的声明式服务调用

    💗wei_shuo的个人主页 💫wei_shuo的学习社区 🌐Hello World ! Spring Cloud OpenFeign是一个声明式的服务调用框架,基于Feign并整合了Ribbon和Hystrix;目标是简化分布式系统中编写服务间调用的代码,并提供一种更加优雅和便捷的方式来进行服务之间的通信 依赖导入 application.yml配置 启

    2024年02月05日
    浏览(51)
  • SpringCloud--Ribbon解析

    一、Spring Cloud Ribbon简介 Spring Cloud Ribbon是Spring Cloud生态系统中的一部分,是一套基于 Netflix Ribbon 实现的客户端负载均衡工具,由于Spring Cloud对其进行二次封装,可以将面向服务的Rest模板(RestTemplate)请求转换成客户端负载均衡的服务调用。且无需单独部署,只需要在项目中

    2024年02月20日
    浏览(31)
  • 【合集】Spring Cloud 组件——架构进化史话 & Eureka,Nacos,OpenFeign,Ribbon,Sentinel,Gateway ,Seata+事务. . .

    Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智能路由,微代理,控制总线,一次性令牌,全局锁,领导选举,分布式会话,集群状态)。 注意: 首先,尽管Spring Cloud带有“Cloud”这个单词,但它并不是云计算解

    2024年02月08日
    浏览(40)
  • 【合集】Spring Cloud 组件——架构进化史话 & Eureka,Nacos,Apollo,OpenFeign,Ribbon,Sentinel,Gateway ,Seata+事务. . .

    Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智能路由,微代理,控制总线,一次性令牌,全局锁,领导选举,分布式会话,集群状态)。 注意: 首先,尽管Spring Cloud带有“Cloud”这个单词,但它并不是云计算解

    2024年02月03日
    浏览(198)
  • SpringCloud入门(微服务调用 OpenFeign)——从RestTemplate到OpenFeign & OpenFeign的相关配置 & 源码的分析和请求流程拆解

    在之前的博客中,我们介绍了RestTemplate的使用,博客文章如下连接。但是在使用RestTemplate的时候,需要把生产者的路径拼出来,非常繁琐,另外参数的传递的也比较繁琐,解决方案就是使用openFeign。 SpringCloud入门(RestTemplate + Ribbon)——微服务调用的方式 RestTemplate的使用 使

    2024年04月11日
    浏览(37)
  • 【springcloud微服务】Spring Cloud Alibaba 整合dubbo与openfeign

    dubbo与springcloud都可以单独作为微服务治理框架在生产中进行使用,但使用过springcloud的同学大概了解到,springcloud生态的相关组件这些年已经逐步停更,这就导致在服务架构演进过程中的迭代断层,以至于一些新的技术组件引入困难重重,于是在国内的市场上就有了升级版的

    2024年02月07日
    浏览(64)
  • SpringCloud源码系列之(Ribbon、Hystrix超时正确配置)

    这块的代码debug了一个礼拜,一开始看Fegin创建源码的时候没注意到,是基于RxJava的响应式编程。分析栈桢的时候,有很多异步栈桢。由于只是想搞清楚如下配置的生效时机、以及失效情况,期间还看了一堆与此无关的源码,真的头大 先贴一下正确的超时配置 服务降级流程:

    2024年01月19日
    浏览(37)
  • 【springcloud 微服务】Spring Cloud Ribbon 负载均衡使用策略详解

    目录 一、前言 二、什么是Ribbon 2.1 ribbon简介 2.1.1  ribbon在负载均衡中的角色

    2024年02月02日
    浏览(64)
  • 基于 Eureka 的 Ribbon 负载均衡实现原理【SpringCloud 源码分析】

    目录 一、前言 二、源码分析 三、负载均衡策略 如下图,我们在 orderserver 中通过 restTemplate 向 usersever 发起 http 请求,在服务拉取的时候,主机名 localhost 是用服务名 userserver 代替的,那么该 url 是一个可访问的网络地址吗?   我们在浏览器中访问一下这个地址,果然不可用

    2024年02月03日
    浏览(47)
  • springcloud 中RestTemplate 是怎么和 ribbon整合,实现负载均衡的?源码分析

    RestTemplate 内置了一个 ClientHttpRequestInterceptor ,这个是一个拦截器操作,我们可以在请求的前后做一些事情。然后我们看一下这个类,这个类里面 有一个 intercept 方法。我们看下这个实现类,里面有一个 LoadBalancerInterceptor 实现类。 我们来看 LoadBalancerInterceptor 实现类。 然后我

    2024年02月09日
    浏览(42)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包