小工具      在线工具  汉语词典  css  js  c++  java

【Spring Cloud阿里巴巴】(二)微服务调用组件Feign原理+实战

Spring Cloud Alibaba 微服务实战,微服务,spring cloud,java,spring,云原生 额外说明

收录于:40天前

CSDN成就一亿技术人

系列目录

【Spring Cloud阿里巴巴】(一)微服务介绍及Nacos注册中心实际实现



前言

通过上文,我们掌握了Spring Cloud Alibaba微服务框架的初始环境搭建,并能通过Nacos注册中心的服务注册和发现,配合RestTemplate和Ribbon,实现2个服务之间通过服务名进行远程调用。
实际上,微服务之间的调用还有更简单、更方便、更强大的调用方式,那就是RPC调用!本文所讲的微服务调用组件Feign,正是RPC框架之一!
本文会循序渐进的从 Feign 到 OpenFeign,并会讲到Feign核心原则实际项目中使用OpenFeign的常见做法

  • 单独使用Feign实现两个服务之间的RPC调用;
  • Feign核心源码解读;
  • Feign整体设计架构预览;
  • Spring Cloud Ali快速集成Feign,实现两个服务之间通过Nacos服务注册发现的RPC调用;
  • Feign在实际项目中常用的做法。

什么是RPC?

RPC(Remote Produce Call),即远程过程调用,目的是:调用[远程服务方法]像调用[本地方法]一样!


Feign和OpenFeign都是什么?

Feign是RPC框架中的一种,是Netflix开发的声明式、模板化的HTTP客户端,底层依然是走的HTTP调用,但表现形式是接口调用,可以帮助我们更加便捷、优雅地调用HTTP API,就像调用本地方法一样方便。
它通过扩展集成了Netflix 功能区,从而拥有负载均衡的功能,默认是基于配置来提供服务实例列表

OpenFeign是指Spring Cloud OpenFeign,是Spring Cloud开发的,对Feign进行了增强,使其支持Spring MVC 注释,还整合了Spring Cloud Netflix 功能区,从登记中心获取服务实例(在Spring Cloud Alibaba框架中的注册中心默认是纳科斯),从而使得Feign与Spring Cloud整合。


HTTP调用 vs Feign(RPC)调用

回顾一下RestTemplate方式的服务调用(gg-user是服务名):

@Autowired
private RestTemplate restTemplate;

@GetMapping("/getUserName")
public String getUserName(@RequestParam("id") Integer id) {
    
    String url = String.format("http://%s/user?id=%s", "gg-user", id);
    return restTemplate.exchange(url, HttpMethod.GET, null, String.class).getBody();
}

换成Feign调用,感受一下效果:

@Autowired
private UserService userService;

@GetMapping("/getUserName")
public String getUserName(@RequestParam("id") Integer id) {
    
    return userService.getUserName(id);
}

What? Feign这个是远程调用?

是的,这是远程调用,你根本看不出来,它就像调用本地方法一样滑溜!

怎么实现的?如果你用过Mybatis,可以往Mybatis接口的实现类上思考,也许你能想到答案!


单独使用Feign实战

那么接下来我们就来看看如果不是Spring Cloud框架下Feign是如何实现RPC调用的!

这个实际案例的调用者:普通的SpringBoot程序。

调用地址:http://gg-user/user?id=123

gg-user是被调用的服务名

  • 1.引入Feign依赖
<!-- Feign核心 -->
<dependency>
    <groupId>io.github.openfeign</groupId>
    <artifactId>feign-core</artifactId>
    <version>10.12</version>
</dependency>
<!-- Feign集成Netflix Ribbon -->
<dependency>
    <groupId>io.github.openfeign</groupId>
    <artifactId>feign-ribbon</artifactId>
    <version>10.12</version>
</dependency>
  • 2.定义接口UserService
import feign.Param;
import feign.RequestLine;

public interface UserService {
    
    @RequestLine("GET /user?id={id}")
    String getUserName(@Param("id") Integer id);
}

虽然不是SpringMVC的注解,但从@RequestLine注解能看出这是一个GET请求,路径是/user?id={id},{id}是占位符,通过 @Param("id")注解获取实际传入的id值。

  • 3.生成UserService接口的实现类

这里通过JavaConfig配置@Bean来实现注入:

@Configuration
public class FeignConfig {
    
    @Bean
    public UserService userService() {
    
        return Feign.builder()
                .client(RibbonClient.create())
                .target(UserService.class, "http://gg-user");
    }
}
  • 4.配置Ribbon服务实例

这里配置到应用程序属性,因为不在Spring Cloud环境,所以没有注册中心,需要从配置文件中获取服务实例。

gg-user.ribbon.listOfServers=localhost:8081,localhost:8082

调用代码:

@Autowired
private UserService userService;

@GetMapping("/getUserName")
public String getUserName(@RequestParam("id") Integer id) {
    
    return userService.getUserName(id);
}

这样我们通过Feign就实现了非常优雅的RPC调用,最终GET请求url可能是:
http://localhost:8081/user?id=123
http://localhost:8082/user?id=123


Feign核心源码解读

Feign的核心代码写得非常好,非常值得我们学习和参考,所以推荐大家研究一下源码。本文仅阐述两个核心点作为指导。

  • Feign是如何动态生成的实现类?
return Feign.builder()
                .client(RibbonClient.create())
                .target(UserService.class, "http://gg-user");

通过这段代码,可以看出是通过target泛型方法生成的UserService实现类。

target会先build()出Feign的实现类反射假象, 再调用ReflectiveFeign.newInstance方法,如下图:
Feign的target源码分析

反射假象.newInstance方法内会调用Proxy.newProxyInstance生成动态代理类,如下图:
ReflectiveFeign.newInstance源码分析-动态代理

  • Feign是如何集成的Ribbon?

对于JDK动态代理,会在InvocationHandler里最终发起HTTP请求。而Feign把HTTP请求这部分做到了动态可插拔,封装成了Client接口,可以支持JDK 原生的 URLConnection、Apache HttpClient、OkHttp等等各类Client的实现。上面builder().client方法,就是用来指定Client的实现类。

这里指定的Client是:功能区客户端,它并不是HTTP调用的直接实现,从名子可以看出它主要整合Ribbon提供的是负载均衡功能。从实现上来看,它使用的是装饰器设计模式,就是为了在提供负载均衡功能的同时,还可以灵活指定其它Client,从而达到动态扩展的目的。
Feign通过RibbonClient集成Ribbon源码


Feign整体设计架构

Feign设计架构

通过上面的源码解读,我想你应该可以看懂架构图的上部和下部,Feign实际在设计上考虑了很多扩展功能,像客户端、日志、拦截器、合约等等,非常灵活,非常强大,给了我们足够的扩展空间,它对所有的组件都提供了接口,如果对接口的实现类不满意,还可以基于Feign的接口来自定义实现。
所以,春云正是通过假装扩展,将Feign完美整合到Spring Cloud框架中,形成了Spring Cloud OpenFeign


Spring Cloud OpenFeign实战

单独使用Feign的时候,我们还需要做一些配置,可一旦被Spring Cloud整合,那么一切就会变得非常非常简单,只需要加依赖+加注解

接下来,基于多于Spring Cloud 阿里巴巴工程环境,我们改造demo-a服务,将休息模板调用改成开放Feign调用。

只需三步即可达到效果。

  • 第一步:介绍 OpenFeign 组件

额外增加包spring-cloud-starter-openfeign,不用加版本,都在父工程定义了,上文已经说了版本。

<!-- Spring Cloud Open Feign -->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<!-- Spring Cloud Nacos Service Discovery 这是支持Nacos时加过的-->
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
  • 第二步:定义远程API接口并添加@FeignClient

界面上额外增加@FeignClient注解,value=服务名
方法上换成SpringMVC注解,代替Feign注解

@FeignClient(value = "demo-b")
public interface UserService {
    
    @GetMapping("/user?id={id}")
    String getUserName(@RequestParam("id") Integer id);
}
  • 第三步:启动类并添加@EnableFeignClients

启动类上再增加注解@EnableFeignClients,用于扫描@FeignClient并生成动态代理类等

注意:如果UserService接口与启动类不在一个包package下,可以通过basePackages指定@FeignClient所在包路径.

@SpringBootApplication
@EnableDiscoveryClient
@EnableFeignClients
public class DemoARunner {
    
    public static void main(String[] args) {
    
        SpringApplication.run(DemoARunner.class, args);
    }
}

通过以上三步操作,我们就达到了调用[远程服务方法]像调用[本地方法]一样! 调用代码如下:

@Autowired
private UserService userService;

@GetMapping("/getUserName")
public String getUserName(@RequestParam("id") Integer id) {
    
    return userService.getUserName(id);
}

Feign在实际项目的通常做法

在实际的Spring Cloud项目中,接口定义往往不是由消费者定义的。通常的做法是:

服务提供方负责定义、发布接口包,供消费方使用。

  • 益处:
  1. 一个服务可能被多个服务调用,这样可以避免为每个消费者定义接口
  2. 服务提供者最清楚如何调用接口
  • 对于消费者:
  1. 引入接口Jar包
  2. 添加@EnableFeignClients注解,保证Jar包可以被扫描到

这样消费者就可以方便的直接调用了。你明白了吗?


最后

通过本文,我们掌握了Feign的基本使用、核心原理,以及Spring Cloud Alibaba如何快速整合Feign,真的太简单了!你是不是觉得这样就够了?但在实际项目使用OpenFeign时,我们常常会遇到各种需求,需要用到它提供的扩展,例如日志分析、自定义统一拦截器、客户端组件配置、GZIP压缩等等,这也是我计划将在下面分享的内容,如果感觉不错,欢迎订阅本专栏,后面还有更多的【Spring Cloud 阿里巴巴】实用知识陆续放出。

关注我 天罡格 分享更多干货: https://blog.csdn.net/scm_2008
大家的「关注❤️+点赞-+收藏⭐」就是我创作的最大动力!谢谢大家的支持,我们下文见!


. . .

相关推荐

额外说明

七夕节到了——程序员的浪漫

七夕节,又称七巧节、七姐节、女儿节、乞巧节、七娘会、七夕祭、牛公牛婆日、巧夕等,是中国民间的传统节日。七夕节由星宿崇拜衍化而来,为传统意义上的七姐诞,因拜祭“七姐”活动在七月七晩上举行,故名“七夕”。拜七姐,祈福许愿、乞求巧艺、坐看牵牛织女星、祈祷姻缘、

额外说明

Flask 入门教程

Flask是一个使用 Python 编写的轻量级 Web 应用框架。 本章教程,介绍一下Flask的简单入门。 目录 一、安装依赖 二、入门案例 三、访问地址  一、安装依赖 pip install flask 二、入门案例 三、访问地址 

额外说明

使用java进行补零操作

很多情况下都会用到补零操作,譬如说工号01,0003什么的。以下代码用for循环和while循环实现简单的补零操作(在数字之前补零),可以补一个零,也可以补多个零。 public class AddZero { public static void m

额外说明

appium爬取微信朋友圈信息 真机测试

坏境: 安卓9,荣耀10,微信7.0.11 代码: from appium import webdriver from selenium.webdriver.support.ui import WebDriverWait from selenium.we

额外说明

C#基础知识 之 ✨ ref 和 out 之间的江湖趣闻

ref 和 out 之间的江湖趣闻 本片文章将详细介绍一下在C#中的关键字:ref 和 out 的使用和区别 话不多说,下面开始介绍啦 引用参数和输出参数 按照国际惯例,要了解一个东西的时候,首先明白它是什么,然后明白它能做什么,最后要知道为什么。 所以

额外说明

【Python 随练】学用 line 函数画直线

题目: 画图,学用line函数画直线。 简介: 在本篇博客中,我们将介绍如何使用Python的绘图库来画直线。我们将使用line函数来绘制直线,并提供一个完整的代码示例来演示其用法。 绘制直线: 要绘制直线,我们可以使用绘图库中的line函数。line函

额外说明

C语言刷题随记 —— 自由落体的球

文章目录 题目 思路 题解 样例输出 题目 一球从 100 米高度自由落下,每次落地后反跳回原高度的一半;再落下,求它在第10 次落地时,共经过多少米?第 10 次反弹多高? 思路 利用循环不断求高度的一半并相加即可。 题解 #include <stdi

额外说明

SpringBoot之异常处理

文章目录 前言 一、默认规则 二、定制异常处理处理 自定义错误页面 @ControllerAdvice+@ExceptionHandler处理全局异常 @ResponseStatus+自定义异常 自定义实现 HandlerExceptionResolve

额外说明

如何在Eclipse中配置Tomcat

过程如下图所示: 1、windows -> Preferences 2、Preferences -> Server -> Runtime Environment 点击右边 Add 3、 选择Tomcat的版本。点击Next -> 4、点击Browse -

额外说明

模拟hibernate的注解来创建数据表,内置注解

目录 导读 注解释义 注解定义 内置三大注解 override注解 Deprecated注解 SuppressWarnings注解 元注解 SOURCE和RUNTIME的区别 SOURCE RUNTIME 注解创建SQL表 注意事项 原生SQL语句 定义

ads via 小工具