[SpringCloud] Feign 与 Gateway 简介

目录

一、Feign 简介

1、RestTemplate 远程调用中存在的问题

2、定义和使用 Feign 客户端

3、Feign 自定义配置

4、Feign 性能优化

5、Feign 最佳实践

6、Feign 使用问题汇总

二、Gateway 网关简介

1、搭建网关服务

2、路由断言工厂

3、路由的过滤器配置

4、全局过滤器

5、过滤器链执行顺序

6、网关的 cors 跨域配置

7、Gateway 网关使用问题汇总


一、Feign 简介

Feign 是 Netflix 开发的声明式、模板化的 Http 客户端,Feign 可以帮助我们更快捷、优雅地调用Http API。

  • Feign 是在 Ribbon 的基础上进行了一次改进,是一个使用起来更加方便的 HTTP 客户端。
  • SpringCloud 对 Feign 进行了增强,使 Feign 支持了 Spring MVC 注解,并整合了 Ribbon 和 Eureka,集成了服务发现和负载均衡,从而让 Feign 的使用更加方便。

1、RestTemplate 远程调用中存在的问题

该远程调用的代码有以下问题:

  • 代码可读性差,编程体验不统一;
  • 参数复杂,URL 难以维护;

2、定义和使用 Feign 客户端

在 SpringCloud 中,使用 Feign 非常简单,创建一个 Interface,并在接口上添加一些注解,代码就完成了。 

(1)引入依赖

  • 给需要远程调用的模块,引入 openfeign 起步依赖;

(2)添加 @EnableFeignClients 注解

  • 给需要远程调用的启动类,添加 @EnableFeignClients 注解;

(3)编写 Feign 客户端

  • 添加 @FeignClient 和 @GetMapping 注解; 

上述注解声明了如下信息:

  • 服务名称:user-service;
  • 请求方式:get;
  • 请求路径:/user/{userId};
  • 请求参数:String userId;
  • 返回值类型:User;

(4)OrderService 远程调用过程

该远程调用过程,就好像平常处理请求的逆过程:

  • OrderService 将 userId 传递给接口中的方法;
  • queryUserById 方法再将参数赋值给请求路径中的占位符 {userId};
  • 然后再通过 get 请求向 UserController 发送请求;
  • 最终获得 User 对象信息;

3、Feign 自定义配置

Spring 虽然帮我们做好了配置,但是也允许我们修改默认配置。

配置 Feign 日志有 2 种方式:

(1)application/bootstrap 配置文件方式

  • 注意:logging.level 属性值需要改为 trace; 

(2)Java 代码方式

4、Feign 性能优化

Feign 底层客户端实现:

  • URLConnection:默认使用,java 原生 jdk,性能较差,不支持连接池;
  • Apache HttpClient:支持连接池;(推荐)
  • OKHttp:支持连接池;(推荐)

对 Feign 的性能优化,最重要的一点就是对底层实现的改变。

因此优化 Feign 的性能主要包括:

  • 使用连接池代替默认的 URLConnection;
  • 日志级别,最好用 basic 或 none;(因为开启日志也需要占用较多资源)

下面以 HttpClient 为例子,说明性能优化的步骤:

(1)引入依赖

  • httpClient 已经被 Spring 管理好版本了,不需要指定版本;

(2)修改 application 配置文件

  • 将 feign.httpclient.enabled 的属性值改为 true,表明底层使用 httpclient;

5、Feign 最佳实践

最佳实践是指,企业开发过程中,总结设计缺点,得出的一种相对比较好的使用方式。

通常会用到 2 种方式,各有利弊,按自己的需求使用:

  • 继承:给消费者的 FeignClient 和提供者的 controller 定义统一的父接口,然后实现接口;
  • 比如 order 模块要调用 user 模块的服务,就定义 UserClient 接口,继承 UserAPI;
  • 缺点:耦合度高;
  • 优点:遵从了契约;

  • 抽取:将 FeignClient 抽取为独立模块,并且把接口有关的 POJO、默认的 Feign 配置都放到这个模块中,提供给所有消费者使用;
  • 缺点:消费者可能会引入很多用不到的 api;
  • 优点:耦合度低,层次分明;

我个人更喜欢第二种抽取的方式,下面使用这种方式做一个例子。

(1)创建 feign-api 模块

  • 在 feign-api 模块中引入 feign 的起步依赖;

  • 将消费者模块中对提供者模块所需的 Client、Pojo,以及 Config 类都移动到 feign-api 中;

(2)引入 feign-api 模块

  • 在消费者 order-service 模块中,引入 feign-api 模块;

(3)消除报错

  • 将 order-service 模块内原来依赖 pojo、config、client 的类,修改为 feign-api 模块内的类;

  • 由于 order-service 模块的 application 启动类无法扫描到 feign-api 模块下的 Client 的 Bean 对象,因此会出现如下报错:

  • 需要在 @EnableFeignClients 中加入 clients 属性;

6、Feign 使用问题汇总

(1)Did you forget to include spring-cloud-starter-loadbalancer?

出错原因: 

  • SpringCloud Feign 在 Hoxton.M2 RELEASED 版本之后不再使用 Ribbon,而是使用 spring-cloud-loadbalancer,所以在不引入 spring-cloud-loadbalancer 的情况下会报错。

参考了很多解决方法,主要是要区别版本问题:

包含 RELEASE 关键字的版本中,只有下面这样操作才能使用:

  • 首先排除 ribbon 的起步依赖,排除位置在消费者依赖的服务提供者的 dependency;
  • 添加 loadBalancer 依赖;

而在 2021 及更新版本中(也就是没有 RELEASE 关键字的版本),只需要如下操作:

  • 引入 loadBalancer 依赖;
  • 不需要排除 ribbon 的起步依赖;

二、Gateway 网关简介

为什么需要网关?

  • 如果没有网关,那么所有人都可以访问我们所有的服务,但是有很多服务其实是不能对外公开的。
  • 需要使用网关进行身份验证,通过后才能访问敏感服务。
  • 一切请求需要先通过网关,才能到微服务。

因此,网关有如下功能:

  • 身份认证和权限校验;
  • 服务路由、负载均衡;(也就是能知道将请求送到哪个微服务)
  • 限制请求流量;

SpringCloud 提供了 2 个组件实现网关功能:

  • gateway;(新版本)
  • zuul;(早期版本)

Zuul 是基于 Servlet 的实现,属于阻塞式编程。

而 SpringCloudGateway 则是基于 Spring5 中提供的 WebFlux,属于响应式编程的实现,具备更好的性能。

1、搭建网关服务

(1)创建 gateway 模块

  • 创建一个新的模块专门用于网关:gateway-module,需要 SpringBoot 启动类;
  • 引入 SpringCloudGateway 起步依赖;
  • 引入 Nacos 服务发现依赖;

(2)编写路由配置以及 Nacos 地址

  • gateway 中的 routes 属性之前的配置,目的是为了让 gateway 将服务注册到 nacos;
  • gateway 中的 routes 属性之后的配置,指明了可以使用的 service 路由;
  • id:表示这个路由的名字,与其他属性无关;
  • uri:表示路由目标地址,其中 lb 表示负载均衡,后面跟着服务的 name;
  • predicates:指定路由规则,符合规则就可以放行;

(3)测试网关功能

  • 启动 gateway 的 application;
  • 发起请求:localhost:10010/order/queryOrderById/1;
  • 这个请求中,order-service 模块会调用 user-service 的服务;

  • 访问失败,说明我们的网关起作用了,因为我们没有为网关配置 order-service 的路由;

  • 添加对 order-service 的服务的路由后,就可以从 gateway 访问 order 的服务了;

2、路由断言工厂

路由断言工厂 Route Predicate Factory,作用是:

  • 我们在配置文件中写的 predicates 断言规则只是字符串,这些字符串会被 Predicate Factory 读取并解析为路由判断的条件。
  • 每种规则都有各自的断言工厂去解析,比如 Path 有 PathRoutePredicateFactory 断言工厂;

下面以 After 举一个例子:

(1)添加断言规则 After

  • After 断言规则,规定在这个时间之后,才能进行访问; 

(2)访问 order 的服务

  • 发送请求 /order/queryOrderById/1;
  • 理向情况应该是:无法访问;

  • 修改断言规则的时间为:2020-12-31,重启 gateway 模块;

  • 再次发起请求,就可以访问了;

3、路由的过滤器配置

路由过滤器是:GatewayFilter。

  • GatewayFilter 是网关中提供的一种过滤器,可以对进入网关的请求微服务返回的响应做处理。

在这个过程中,过滤器就可对请求和响应做出各种各样的处理,这样往下游传递的数据中,就包含了修改后的信息。

具体有什么样的操作,就要看使用哪个过滤器工厂:

下面我们以添加一个请求头为例子:

(1)修改 application 配置文件

  • 给 routes 中的一个路由添加 filters 属性;
  • filters 中添加:- AddRequestHeader=[key],[value];

(2)编写 Controller,获取请求头信息

  • 既然在 filter 中设置了 header:orderStatus=001;
  • 那么请求传递到 Controller 后,一定可以获取到这个头信息;

  • 访问 /order/queryOrderById/1,查看控制台输出;

(3)编写默认过滤器 default-filter

如果我们想为所有的请求都添加 header 信息,也不需要给每一个路由都写上 filters。

  • 只需要在 routes 同级下,添加 default-filter 属性;
  • 给 default-filter 属性添加:- AddRequestHeader=[key],[value] 等属性值;

  • 访问 /order/queryOrderById/1,查看控制台输出;

4、全局过滤器

全局过滤器是:GlobalFilter。

  • 全局过滤器的作用:处理一切进入网关的请求和微服务响应,与 GatewayFilter 的作用一样,只是作用范围不同。
  • 其中,GatewayFilter 通过配置定义,运行逻辑是固定的。(官方定义)
  • 而 GlobalFilter 的运行逻辑需要自己写代码实现。(自定义)

需求

定义全局过滤器,拦截请求,判断请求的参数是否满足下面条件:

  • 参数中是否有 authorization;
  • authorization 参数值是否为 admin;
  • 如果同时满足则放行,否则拦截;

(1)定义实现类

  • 创建 AuthorFilter 实现类,实现 GlobalFilter 接口;
  • exchange:请求上下文,里面可以获取Request. Response等信息;
  • chain:用来把请求委托给下一个过滤器;
  •  Nono<Void>:返回表示当前过滤器业务结束

(2)定义 filter 方法的运行逻辑

  • 添加 @Component;
  • 添加 @Order,方便多过滤器时指定顺序;
  • 网关中采用的都是基于 WebFlux(响应式编程)的 API,因此使用上与 ServletAPI 不同;
@Order(-1)
@Component
public class AuthorFilter implements GlobalFilter {@Overridepublic Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {// 1.获取请求参数ServerHttpRequest request = exchange.getRequest();MultiValueMap<String, String> params = request.getQueryParams();// 2.获取请求参数中的 author 参数值String author = params.getFirst("author");// 3.判断是否等于 adminif (author.equalsIgnoreCase("admin")) {// 放行return chain.filter(exchange);}// 4.不相等,获取响应ServerHttpResponse response = exchange.getResponse();// 4.1.设置状态码,401表示未登录response.setStatusCode(HttpStatus.UNAUTHORIZED);// 4.2.拦截请求return response.setComplete();}
}

(3)启动 gateway 的 application

  • 访问 /order/queryOrderById/1/author=admin111,先使用错误的 authon 参数值;
  • 发现返回了 401;

  • 访问 /order/queryOrderById/1/author=admin,使用正确的 authon 参数值;
  • 可以正常访问

5、过滤器链执行顺序

请求进入网关会碰到三类过滤器:当前路由的过滤器、DefaultFilter、GlobalFilter。

请求路由后,会将当前路由过滤器DefaultFilterGlobalFilter,合并到一个过滤器链(集合)中,排序后依次执行每个过滤器。

过滤器执行顺序:

  • 每一个过滤器都必须指定一个 int 类型的 order 值:order 值越小,优先级越高,执行顺序越靠前;
  • GlobalFilter 通过实现 Ordered 接口,或者添加 @Order 注解来指定 order 值,由我们自己指定;
  • 路由过滤器和 defaultFilter 的 order 由 Spring 官方指定,默认是按照声明顺序从 1 递增;
  • 当过滤器的 order 值一样时,会按照 defaultFilter > 路由过滤器 > GlobalFilter 的顺序执行;

6、网关的 cors 跨域配置

由于一些页面可能需要从微服务中获取某些数据,此时需要发起 AJAX 请求,那么这就可能属于跨域请求的范畴。(一般我们的 web 页面端口都不会与微服务的相同,所以请求基本上都是跨域请求)

跨域请求不需要在微服务中处理,只需要在网关中处理即可。

跨域:域名不一致就是跨域,主要包括:

  • 域名不同:www.taobao.com 和 www.taobao.org 和 www.jd.com 和 miaosha.jd.com;
  • 域名相同,端口不同:localhost:8080 和 localhost:8081

跨域问题的 2 个要素:

  • 浏览器、服务端:浏览器禁止请求向服务端发起跨域 ajax 请求,请求会被拦截;
  • 解决方案:CORS;

需求:

  • 使用 localhost:8090 发起请求,8090 发送的是 AJAX 的 GET 请求。
  • 8090 发起的 AJAX 请求需要通过网关,在 AJAX 的目标地址中要用 10010 端口;

(1)编写 gateway 的配置文件

  • 各个属性的作用都写在注释中了;

(2)端口 8090 的 AJAX 请求

  • 这里使用 JQuery 发起 AJAX 请求,传递参数为 author=admin;
  • 这里参数是必须传递的,因为发起的请求目标地址是要通过网关的,需要鉴权;
<head><meta charset="UTF-8"><title>Title</title><script type="text/javascript" src="JQuery-3.7.0.js"></script><script type="text/javascript">$(function() {$("#ajaxButton").click(function() {$.ajax({url:"http://localhost:10010/order/queryOrderById/1",data: {author : "admin"},type: "get",dataType: "json",success: function(data) {console.log(data);}});});});</script>
</head>
<body><a href="http://localhost:10010/order/queryOrderById/1?author=admin"> 发起普通GET请求 </a><input id="ajaxButton" type="button" value="发起 ajax 请求"/>
</body>
  • 发起请求,可以在控制台看到返回的数据;

  • 而如果我们用 <a> 来请求,是会跳转到 10010 得页面的,这就不算跨域请求了;

7、Gateway 网关使用问题汇总

(1)搭建网关服务时,报错 503

这是因为在 Spring Cloud 2020 版本以后,默认移除了对 Netflix 的依赖,其中就包括 Ribbon,官方默认推荐使用 Spring Cloud Loadbalancer 正式替换 Ribbon,并成为了 Spring Cloud 负载均衡器的唯一实现。

Loadbalancer 依赖:

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/156881.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

2023年免费CRM软件盘点:14款热门工具全面比较(含开源)

在初创企业或小型企业阶段&#xff0c;特别是在预算有限且客户管理需求较为基础的情境下&#xff0c;使用免费的CRM系统通常是一个理智的选择。这类系统虽然在功能上可能不如付费版本丰富&#xff0c;但基本的客户信息管理、销售跟踪和沟通记录等核心功能通常都能满足需求。 对…

SpringBootWeb案例——Tlias智能学习辅助系统(1)

目录 需求与准备环境搭建REST风格的API接口开发规范-统一响应结果 部门管理部门列表查询功能删除部门新增部门请求路径优化查询部门修改部门 员工管理分页查询分页插件PageHelper分页查询(带条件) (难点)删除员工 需求与准备 1、部门管理 包括&#xff1a; 查询部门列表 删除部…

微信小程序怎么制作?【小程序开发平台教学】

随着移动互联网的快速发展&#xff0c;微信小程序已经成为了人们日常生活中不可或缺的一部分。从购物、支付、出行到社交、娱乐、教育&#xff0c;小程序几乎涵盖了我们生活的方方面面。那么&#xff0c;对于有营销需求的企业商家来说&#xff0c;如何制作一个自己的微信小程序…

【DriveGPT学习笔记】自动驾驶汽车Autonomous Vehicle Planning

原文地址&#xff1a;DriveGPT - Lei Maos Log Book 自动驾驶汽车的核心软件组件是感知、规划和控制。规划是指在给定场景或一系列场景的情况下为自动驾驶汽车制定行动计划的过程&#xff0c;以实现安全和理想的自动驾驶。 用于规划的场景是从感知软件组件获得的。计划的行动将…

MongoDB——MongoDB删除系统自带的local数据库

一、MongoDB删除系统自带的local数据库 1.1、linux环境进入mongo客户端 输入 mongo 命令&#xff0c;进入命令行客户端 进入admin库&#xff0c;并登录&#xff0c;查看所有数据库 #进入admin库 use admin #并登录admin db.auth("username","password")…

【uniapp】html和css-20231031

我想用控件和样式来表达应该会更贴切&#xff0c;html和css的基础需要看看。 关于html&#xff1a;https://www.w3school.com.cn/html/html_layout.asp 关于css&#xff1a;https://www.w3school.com.cn/css/index.asp html让我们实现自己想要的布局&#xff08;按钮&#xff0…

云栖大会:通义灵码智能编码助手,全面公测

文章目录 核心场景一、什么事通义灵码&#xff1f;二、功能介绍1.引入插件2.如何使用 总结 核心场景 代码智能生成 经过海量优秀开源代码数据训练&#xff0c;可根据当前代码文件及跨文件的上下文&#xff0c;为你生成行级/函数级代码、单元测试、代码注释等。沉浸式编码心流&…

牛客网刷题-(9)

&#x1f308;write in front&#x1f308; &#x1f9f8;大家好&#xff0c;我是Aileen&#x1f9f8;.希望你看完之后&#xff0c;能对你有所帮助&#xff0c;不足请指正&#xff01;共同学习交流. &#x1f194;本文由Aileen_0v0&#x1f9f8; 原创 CSDN首发&#x1f412; 如…

C++ 赋值运算重载,const成员,取地址及const取地址操作符重载

C 赋值运算重载&#xff0c;const成员&#xff0c;取地址及const取地址操作符重载 1. 赋值运算符重载1.1 运算符重载1.2 赋值运算符重载1.3 前置/--和后置/--重载 2. const成员3. 取地址及const取地址操作符重载 所属专栏&#xff1a;C“嘎嘎" 系统学习❤️ &#x1f680;…

用前端框架Bootstrap和Django实现用户注册页面

01-新建一个名为“mall_backend”的Project 命令如下&#xff1a; CD E:\Python_project\P_001\myshop-test E: django-admin startproject mall_backend02-新建应用并注册应用 执行下面条命令依次创建需要的应用&#xff1a; CD E:\Python_project\P_001\myshop-test\mall…

MySQL系列-架构体系、日志、事务

MySQL架构 server 层 &#xff1a;层包括连接器、查询缓存、分析器、优化器、执行器等&#xff0c;涵盖 MySQL 的大多数核心服务功能&#xff0c;以及所有的内置函数&#xff08;如日期、时间、数学和加密函数等&#xff09;&#xff0c;所有跨存储引擎的功能都在这一层实现&am…

Linux中shell脚本练习

目录 1.猜数字 2.批量创建用户 3.监控网卡Receive Transmit 数据的变化 4.部署Linux 5.系统性能检测脚本 6.分区脚本 7.数据库脚本 1.猜数字 随机数的生成 使用环境变量RANDOM&#xff0c;范围是0&#xff5e;32767 编写guest.sh&#xff0c;实现以下功能&#xff1…