当前位置: 首页 > news >正文

服务网关工作原理,如何获取用户真实IP?

文章目录

  • 一、什么是网关
  • 二、网关工作原理 (★)
  • 三、SpringCloud Gateway
    • 3.1 Gateway 简介
    • 3.2 Gateway 环境搭建
    • 3.3 自定义路由规则 (★)
    • 3.4 局部过滤器
    • 3.5 全局过滤器(案例:获取用户真实IP地址) (★)
  • 补充1:不同类型的客户端如何设置网关
  • 补充2:多个全局多滤器的执行优先级
  • 补充3:局部过滤器和全局过滤器的执行优先级


一、什么是网关

在微服务架构中,⼀个系统会被拆分为很多个微服务。那么作为客户端要如何去调用这么多的微服务呢?如果没有网关的存在,我们只能在客户端记录每个微服务的地址,然后分别去调用。

在这里插入图片描述
这样的架构,会存在着诸多的问题:

  • 客户端多次请求不同的微服务,增加客户端代码或配置编写的复杂性。
  • 认证复杂,每个服务都需要独立认证。
  • 微服务做集群的情况下,客户端并没有负载均衡的功能。

上面的这些问题可以借助 API 网关来解决。

API 网关指系统的统一入口,它封装了应用程序的内部结构,为客户端提供统⼀服务,⼀些与业务本身功能无关的公共逻辑可以在这里实现,如:认证、鉴权、监控、路由转发等等

添加上 API 网关之后,系统的架构图变成了如下所示:
在这里插入图片描述


二、网关工作原理 (★)

网关本身也是个微服务,服务注册中心以后,将注册中心的服务列表拉取到本地缓存,并配置相应的路由规则,如下图所示:
在这里插入图片描述

网关组件提供统一请求入口,可以进行统一逻辑的处理,并提供网络隔离的能力(即请求只能通过网关来访问,不能通过某一个服务的IP地址绕过网关来访问)。

当浏览器发起请求http://192.168.10.115/product-serv/product/1
step1:网关获取到请求地址,默认截取域名后面的部分请求路径 /product-serv/product/1
step2:拿到路径信息和路由规则进行匹配,可以获取到对应的服务名product-service
step3:拿到服务名在本地的缓存列表中找到对应的IP地址列表,基于 Ribbon 实现负载均衡算法得出 IP 地址。
step4:进行 URL 地址拼接 http://192.168.10.110/product/1,最终进行网络的调用。


三、SpringCloud Gateway

3.1 Gateway 简介

  Spring Cloud Gateway 是 Spring 公司基于Spring 5.0,Spring Boot 2.0 和 Project Reactor 等技术开发的网关,它旨在为微服务架构提供⼀种简单有效的统⼀的 API 路由管理方式。它的目标是替代 Netflflix Zuul,其不仅提供统⼀的路由方式,并且基于 Filter 链的方式提供了网关基本的功能,例如:安全,监控和限流。

  Spring Cloud Gateway 存在诸多优点,如:性能强劲、功能强大、易扩展,其内置了很多实用的功能,例如转发、监控、限流等。但也存在缺点,如:不能将其部署在 Tomcat、Jetty 等 Servlet 容器里,只能打成 jar 包执行,需要 Spring Boot 2.0及以上的版本才支持等。


3.2 Gateway 环境搭建

1、创建一个 api-gateway 的模块,导入相关依赖:

<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/maven-v4_0_0.xsd"><modelVersion>4.0.0</modelVersion><parent><groupId>com.axy</groupId><artifactId>shop-parent</artifactId><version>1.0-SNAPSHOT</version></parent><artifactId>api-gateway</artifactId><name>api-gateway</name><dependencies><!--gateway⽹关--><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-gateway</artifactId></dependency><!--nacos客户端--><dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId></dependency>...</dependencies><build><finalName>api-gateway</finalName></build>
</project>

2、编写启动类

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

3、编写配置⽂件

server:port: 9000
spring:application:name: api-gatewaycloud:nacos:discovery:server-addr: localhost:8848gateway:discovery:locator:enabled: true # 让gateway可以发现nacos中的微服务

注:spring.cloud.gateway.discovery.locator.enabled=true 的作用是让 Gateway 开启服务注册和发现的功能,并且为每一个微服务创建一个默认的路由,该路由将以服务名开头的请求路径转发到对应的服务。如:/product-service/** 转发到 product-service 的服务下。

4、启动测试
在这里插入图片描述


3.3 自定义路由规则 (★)

gateway 提供了默认的路由规则,但也支持自定义规则,具体属性如下:
① id:路由标识符,区别于其他 Route。
② uri:路由指向的目的地 uri,即客户端请求最终被转发到的微服务
③ order:用于多个 Route 之间的排序,数值越小排序越靠前,匹配优先级越高
④ predicate:断言的作用是进行条件判断,只有断言都返回真,才会真正的执行路由
⑤ filters:过滤器用于修改请求和响应信息

server:port: 9000
spring:application:name: api-gatewaycloud:nacos:discovery:server-addr: localhost:8848gateway:discovery:locator:enabled: true # 让gateway可以发现nacos中的微服务routes: # 自定义路由配置- id: product_route # 路由名称,保证唯一uri: lb://product-service # 将符合条件的请求转发到哪个微服务,lb表示对服务进行负载均衡predicates: # 拦截哪些请求- Path=/product-serv/**filters:  # 前台访问:http://localhots:9000/product-service/product/1 去掉第一层以后路径以后,http://product-service/product/1- StripPrefix=1 # 在转发请求之前,将拦截到的第一层路径删除掉- id: order_routeuri: lb://order-servicepredicates:- Path=/order-serv/**filters:- StripPrefix=1

注:StripPrefix=1 表示在请求转发请求之前,将拦截到的第一层路径删除掉。
如:前台访问http://localhots:9000/product-service/product/1 去掉第一层以后路径以后变成 http://product-service/product/1

启动测试:
在这里插入图片描述


3.4 局部过滤器

局部过滤器是针对单个路由(或者单个服务)的过滤器,用以添加针对某一个服务的独立功能。下面以需求的形式讲解一下如何配置局部过滤器。

需求:统计订单服务调用耗时。

编写Filter类,注意名称是有固定格式 xxxGatewayFilterFactory。如下面设置 filters 的属性 Time=true,则编写局部过滤器的类名为 TimeGatewayFilterFactory
在这里插入图片描述
继承 AbstractGatewayFilterFactory<TimeGatewayFilterFactory.Config> 类,其中 Config 类是该类的内部类用于承接配置文件中配置的值,重写 shortcutFieldOrder、apply 两个方法。

/*** 局部过滤器*/
@Component
public class TimeGatewayFilterFactory extends AbstractGatewayFilterFactory<TimeGatewayFilterFactory.Config> {public TimeGatewayFilterFactory() {super(Config.class);}// 读取配置⽂件中的参数赋值到配置类 Config 中@Overridepublic List<String> shortcutFieldOrder() {return Arrays.asList("show");}// 拦截到之后就会调用 apply 方法,把创建对象时候反射创建出来的 Config 传入进来@Overridepublic GatewayFilter apply(Config config) {return new GatewayFilter() {@Overridepublic Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {// 前置的逻辑if (!config.show) {return chain.filter(exchange);}System.out.println("前置逻辑");long start = System.currentTimeMillis();// exchange相当于requestreturn chain.filter(exchange).then(Mono.fromRunnable(() -> {long end = System.currentTimeMillis();System.out.println("请求耗时:" + (end - start));// 后置的逻辑System.out.println("后置逻辑");}));}};}@Setter@Getterstatic class Config {private boolean show;}
}

注:其中 Config 作为内部类,是通过反射将配置文件里面的值设置到对应的属性上。如:配置文件中配置 Time=true,则 show 的值最终为 true。另外, shortcutFieldOrder() 方法的返回值是数组,其用于指定反射时的设置顺序。

如假设:配置文件Time=true,1,hello ,那么 Config 的设置应该有三个值,并且属性名与shortcutFieldOrder() 返回值的数组元素的 “名称” 一一对应,如下:

@Component
public class TimeGatewayFilterFactory extends AbstractGatewayFilterFactory<TimeGatewayFilterFactory.Config> {public TimeGatewayFilterFactory() {super(Config.class);}// 读取配置⽂件中的参数赋值到配置类 Config 中@Overridepublic List<String> shortcutFieldOrder() {return Arrays.asList("show", "xx", "yy");}...@Setter@Getterstatic class Config {private boolean show;private Long xx;private String yy;}
}

访问订单服务的时候打印时间如下:
在这里插入图片描述


3.5 全局过滤器(案例:获取用户真实IP地址) (★)

全局过滤器作用于所有路由,无需配置文件配置。通过全局过滤器可以实现对权限的统⼀校验,安全性验证等功能。下面以需求的形式讲解一下如何配置局部过滤器:

需求:实现统⼀鉴权的功能,需要在网关判断请求中是否包含token,如果有则还需要在请求头中设置用户的IP地址,则执行正常逻辑。实现代码如下:

/*** 全局过滤器*/
@Component
public class AuthGlobalFilter implements GlobalFilter {@Overridepublic Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {System.out.println("全局过滤器前置过滤器");// 获取到请求中的token信息,验证token是否有效,如果无效拦截请求String token = exchange.getRequest().getQueryParams().getFirst("token");if (StringUtils.isEmpty(token) || !"123".equals(token)) {System.out.println("鉴权失败");exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);return exchange.getResponse().setComplete();}ServerHttpRequest request = exchange.getRequest().mutate().header("REAL_IP", exchange.getRequest().getRemoteAddress().getHostString()).build();return chain.filter(exchange.mutate().request(request).build()).then(Mono.fromRunnable(new Runnable() {@Overridepublic void run() {System.out.println("全局过滤器后置过滤器");}}));}
}

这里解释以下,为何要在网关获取用户的地址,因为当请求通过网关转发到微服务后使用 Request.getRomteAddr()获取到的只是网关的地址,因此需要在网关侧获取真实IP。

在这里插入图片描述

另外:鉴权逻辑一般放在拦截器中实现,后面将会详细说明。


补充1:不同类型的客户端如何设置网关

由于不同类型客户端访问微服务时,有些公共逻辑会存在差异,如:第三方访问时有限流的操作等。因此建议针对不同端,设置不同的网关。

在这里插入图片描述


补充2:多个全局多滤器的执行优先级

多个全局多滤器最终都是通过Order值进行排序执行,Order值越小越先执行。

两个全局过滤器Order值相同时,根据文件名字母排序,文件名靠前的优先更高。原因是包扫描时是按照文件的顺序扫描的,然后封装到List集合的,通过Order值排序时如果Order值相同,文件名在前名的依然会排在前面。

但也可以实现Ordered接口,重写 getOrder() 方法,以 Order 进行排序,Order 越小优先级越高。

@Component
public class AuthGlobalFilter implements GlobalFilter, Ordered {@Overridepublic Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {...}@Overridepublic int getOrder() {return 2;}
}

补充3:局部过滤器和全局过滤器的执行优先级

多滤器最终都是通过Order值进行排序执行,Order值越小越先执行。

全局过滤器和局部过滤器Order值相同时,GlobalFilter类型优先更高。

原因是这两种过滤器最终会合并到一个过滤器集合中形成过滤器调用链,源码是通过 list.addAll(); 方法将局部过滤器加到了全局过滤器集合中,addAll()是末尾添加方式,所以 Order 值相同时局部过滤器会排在后面。

参考:https://blog.csdn.net/Anenan/article/details/114691488


文章参考:Java微服务商城高并发秒杀项目实战|Spring Cloud Alibaba真实项目实战+商城双11秒杀+高并发+消息+支付+分布式事物Seata

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • 分享6个我喜欢的常用网站,来看看有没有你感兴趣的!
  • fpga系列 HDL:全连接层InegrationFCpart.v的权重读取 $readmemh
  • Gemini vs Meta — 我给两个AI模型出了7个题,优胜者是...
  • 基于深度学习的基因组数据分析
  • 怎么抓住威士忌蓝海市场?
  • Unity Apple Vision Pro 开发(七):UI 交互 + 虚拟键盘
  • 2024年市场营销人员需要了解的16个Snapchat用户数据
  • 独立产品灵感周刊 DecoHack #067 - 摸鱼神器与AI视频创作工具
  • Java项目: 基于SSM框架实现的蛋糕甜品店管理系统分前后台(含源码+数据库+开题报告+毕业论文)
  • 记录RL过程中踩过的坑(更)
  • 快来看,图书《人工智能怎么学》电子书上线啦!
  • 云计算之网络
  • 迭代器模式iterator
  • EI会议推荐-第二届大数据与数据挖掘国际会议(BDDM 2024)
  • USB转百兆网卡芯片CH397在多平台下使用说明
  • [分享]iOS开发 - 实现UITableView Plain SectionView和table不停留一起滑动
  • canvas绘制圆角头像
  • download使用浅析
  • ECMAScript入门(七)--Module语法
  • EOS是什么
  • fetch 从初识到应用
  • interface和setter,getter
  • javascript从右向左截取指定位数字符的3种方法
  • log4j2输出到kafka
  • Netty 框架总结「ChannelHandler 及 EventLoop」
  • PHP 程序员也能做的 Java 开发 30分钟使用 netty 轻松打造一个高性能 websocket 服务...
  • scala基础语法(二)
  • Swift 中的尾递归和蹦床
  • Unix命令
  • Vue ES6 Jade Scss Webpack Gulp
  • 爱情 北京女病人
  • 测试开发系类之接口自动化测试
  • 走向全栈之MongoDB的使用
  • 数据可视化之下发图实践
  • ​sqlite3 --- SQLite 数据库 DB-API 2.0 接口模块​
  • ​批处理文件中的errorlevel用法
  • #HarmonyOS:Web组件的使用
  • #中国IT界的第一本漂流日记 传递IT正能量# 【分享得“IT漂友”勋章】
  • ( )的作用是将计算机中的信息传送给用户,计算机应用基础 吉大15春学期《计算机应用基础》在线作业二及答案...
  • (2022版)一套教程搞定k8s安装到实战 | RBAC
  • (delphi11最新学习资料) Object Pascal 学习笔记---第8章第2节(共同的基类)
  • (Java企业 / 公司项目)点赞业务系统设计-批量查询点赞状态(二)
  • (TOJ2804)Even? Odd?
  • (附源码)spring boot北京冬奥会志愿者报名系统 毕业设计 150947
  • (附源码)ssm教师工作量核算统计系统 毕业设计 162307
  • (蓝桥杯每日一题)love
  • (六)激光线扫描-三维重建
  • (论文阅读笔记)Network planning with deep reinforcement learning
  • (十)c52学习之旅-定时器实验
  • (四)c52学习之旅-流水LED灯
  • (原創) 如何優化ThinkPad X61開機速度? (NB) (ThinkPad) (X61) (OS) (Windows)
  • (转)mysql使用Navicat 导出和导入数据库
  • (转)setTimeout 和 setInterval 的区别
  • ***原理与防范
  • .bat批处理(九):替换带有等号=的字符串的子串