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

使用网关过滤器,根据业务规则实现微服务动态路由

文章目录

    • 业务场景
    • 拦截器实现
    • Spring Cloud Gateway介绍

业务场景

  • 我们服务使用Spring Cloud微服务架构,使用Spring Cloud Gateway 作为网关,使用 Spring Cloud OpenFeign 作为服务间通信方式
  • 作为网关,主要作用是鉴权与路由转发。大多数应用场景,网关主要是针对前端的请求,前端调用接口,网关鉴权和转发。对于微服务间的调用,一般都不经过网关,直接根据注册服务列表路由到对应服务
  • 对于一般的微服务路由转发,一般按照默认设置,根据服务名转发到对应服务即可
  • 对于一些有转发规则的请求,也可以根据规则(如url前缀、某个参数的值、请求header里某个属性的)匹配转发到对应服务
  • 简单的直接在配置文件里编写,例如某个值到某个服务是已知的,这些值是确定的和少量的,可以根据不通值到对应的服务,也可以自定义过滤器实现
  • 现在有一个需求,需要根据参数的值路由到对应的服务,但是参数的值不是枚举值,数量不确定,可能有很多,值也不确定
  • 但是这个值对应哪个服务(ip 端口)是已知的,需要实现根据这个业务规则动态路由到对应服务

拦截器实现

  • Spring Cloud Gateway的默认配置写法,将服务名转化为小写,根据名字匹配
spring:cloud:gateway:discovery:locator:enabled: truelower-case-service-id: truepredicates:- name: Pathargs:pattern: "'/services/'+serviceId.toLowerCase()+'/**'"filters:- name: RewritePathargs:regexp: "'/services/' + serviceId.toLowerCase() + '/(?<remaining>.*)'"replacement: "'/${remaining}'"
  • 默认写法,只根据服务名字匹配进行路由转发,同服务名的多个微服务,轮询转发
  • 一开始试着重写RouteToRequestUrlFilter,发现没有作用,根本走不到这个类
  • 后面在@Component注解里指定了名称value = "org.springframework.cloud.gateway.filter.RouteToRequestUrlFilter",完全覆盖,生效了
  • 具体代码如下,核心是路由转发重新指定exchange.getAttributes().put(ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR, mergedUrl)
package com.newatc.com.authorization;import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.newatc.com.SpringContextHolderUtil;
import com.newatc.com.authorization.util.HttpClientUtil;
import com.newatc.com.authorization.vo.SignalVO;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.cloud.gateway.route.Route;
import org.springframework.cloud.gateway.support.ServerWebExchangeUtils;
import org.springframework.context.annotation.Lazy;
import org.springframework.core.Ordered;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.util.UriComponentsBuilder;
import reactor.core.publisher.Mono;import java.net.URI;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.regex.Pattern;/*** 重写Spring的RouteToRequestUrlFilter,自定义unit服务路由** @author yanyulin* @date 2023-12-6 19:44:52*/
@Component(value = "org.springframework.cloud.gateway.filter.RouteToRequestUrlFilter")
@Lazy
public class RouteToRequestUrlFilter implements GlobalFilter, Ordered {public static final int ROUTE_TO_URL_FILTER_ORDER = 10000;private static final Log log = LogFactory.getLog(RouteToRequestUrlFilter.class);private static final String SCHEME_REGEX = "[a-zA-Z]([a-zA-Z]|\\d|\\+|\\.|-)*:.*";static final Pattern schemePattern = Pattern.compile(SCHEME_REGEX);private static final String UNIT_API_PREFIX = "unit";private static final String UNIT_ADAPTER = "UNIT_ADAPTER";public static final String GET_UNIT_URL = "/services/core/api/signalcontrol/getSignalList";public static final String GET_UNIT_HOSTS = "/services/core/api/signalcontrol/getUnitHostList";private static final RedisTemplate<String, String> redisTemplate = SpringContextHolderUtil.getBean(StringRedisTemplate.class);@Value("${server.port}")private Integer SERVER_PORT;public RouteToRequestUrlFilter() {}static boolean hasAnotherScheme(URI uri) {return schemePattern.matcher(uri.getSchemeSpecificPart()).matches() && uri.getHost() == null && uri.getRawPath() == null;}public int getOrder() {return ROUTE_TO_URL_FILTER_ORDER;}public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {Route route = exchange.getAttribute(ServerWebExchangeUtils.GATEWAY_ROUTE_ATTR);if (route == null) {return chain.filter(exchange);} else {log.trace("RouteToRequestUrlFilter start");URI uri = exchange.getRequest().getURI();boolean encoded = ServerWebExchangeUtils.containsEncodedParts(uri);URI routeUri = route.getUri();if (hasAnotherScheme(routeUri)) {exchange.getAttributes().put(ServerWebExchangeUtils.GATEWAY_SCHEME_PREFIX_ATTR, routeUri.getScheme());routeUri = URI.create(routeUri.getSchemeSpecificPart());}log.debug("routeUri: " + JSON.toJSONString(routeUri));if ("lb".equalsIgnoreCase(routeUri.getScheme()) && routeUri.getHost() == null) {throw new IllegalStateException("Invalid host: " + routeUri);} else {// 原先的UriURI mergedUrl = UriComponentsBuilder.fromUri(uri).scheme(routeUri.getScheme()).host(routeUri.getHost()).port(routeUri.getPort()).build(encoded).toUri();// 如果是需要自定义指定路由的服务,根据业务重写if (UNIT_API_PREFIX.equalsIgnoreCase(routeUri.getHost())) {try {log.info("mergedUrl前: " + JSON.toJSONString(mergedUrl));String[] pathArr = uri.getPath().split("/");String unitId = pathArr[pathArr.length - 1];String host = "";if (StringUtils.hasText(unitId)) {host = getUnitHost(unitId);}if (StringUtils.hasText(host)) {String newurl = host + mergedUrl.getPath();if (StringUtils.hasText(exchange.getRequest().getURI().getQuery())) {newurl = newurl + "?" + exchange.getRequest().getURI().getQuery();}URI newURI = new URI(newurl);mergedUrl =UriComponentsBuilder.fromUri(uri).scheme(newURI.getScheme()).host(newURI.getHost()).port(newURI.getPort()).build(encoded).toUri();log.debug("mergedUrl后: " + JSON.toJSONString(mergedUrl));}} catch (Exception e) {log.error("uri error", e);}}exchange.getAttributes().put(ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR, mergedUrl);return chain.filter(exchange);}}}/*** 根据信号机编号获取对应的服务适配器** @param unitId* @return*/private String getUnitHost(String unitId) {Map<String, String> unitIdHostMap = new HashMap<>();List<SignalVO> signalList;if (Boolean.TRUE.equals(redisTemplate.hasKey(UNIT_ADAPTER))) {signalList = JSONArray.parseArray(redisTemplate.opsForValue().get(UNIT_ADAPTER), SignalVO.class);} else {String url = "http://localhost:" + SERVER_PORT + GET_UNIT_URL;String info = HttpClientUtil.doGet(url, null);signalList = JSONArray.parseArray(info, SignalVO.class);if (null != signalList && !signalList.isEmpty()) {redisTemplate.opsForValue().set(UNIT_ADAPTER, JSON.toJSONString(signalList), 1, TimeUnit.MINUTES);}}if (null != signalList && !signalList.isEmpty()) {signalList.forEach(e -> unitIdHostMap.put(e.getUnitId(), "http://" + e.getServerIp() + ":" + e.getServerPort()));}return unitIdHostMap.get(unitId);}
}
  • 核心就是重新指定uri,再设置进请求里
String newurl = host + mergedUrl.getPath();
if (StringUtils.hasText(exchange.getRequest().getURI().getQuery())) {newurl = newurl + "?" + exchange.getRequest().getURI().getQuery();
}
URI newURI = new URI(newurl);
mergedUrl =UriComponentsBuilder.fromUri(uri).scheme(newURI.getScheme()).host(newURI.getHost()).port(newURI.getPort()).build(encoded).toUri();
exchange.getAttributes().put(ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR, mergedUrl);
  • 这个是前期的做法,后面考虑到服务间调用,不需要走网关Gateway服务,又废弃掉了,改为在服务调用的地方,使用拦截器过滤,详情参考:拦截器配置,FeignClient根据业务规则实现微服务动态路由

Spring Cloud Gateway介绍

Spring Cloud Gateway是Spring Cloud生态系统中的一个组件,用于构建基于Spring Boot的API网关服务。Spring Cloud Gateway基于Reactive编程模型,使用WebFlux框架实现,可以快速、可靠地构建和部署高性能的微服务应用程序。

Spring Cloud Gateway具有以下特点:

  1. 基于WebFlux:Spring Cloud Gateway基于Reactive编程模型,利用WebFlux框架实现非阻塞、事件驱动的异步处理,可以提供更好的性能和并发处理能力。

  2. 灵活的路由规则:Spring Cloud Gateway支持基于URI、请求方法、请求头等多种维度的路由规则配置,使得对请求进行灵活的路由转发变得简单易用。

  3. 过滤器:Spring Cloud Gateway的过滤器功能可以实现对请求和响应的预处理和后处理,包括请求日志记录、鉴权、路由转发、重定向等功能,可以满足各种需求的定制化处理。

  4. 集成性:Spring Cloud Gateway可以与其他Spring Cloud组件和微服务框架无缝集成,例如Eureka、Consul、Ribbon等,能够灵活地进行微服务的注册、发现和负载均衡。

总之,Spring Cloud Gateway是一个灵活、高性能的API网关服务,能够帮助开发者快速构建和部署微服务应用,实现请求路由、负载均衡、安全验证等功能。

相关文章:

使用网关过滤器,根据业务规则实现微服务动态路由

文章目录 业务场景拦截器实现Spring Cloud Gateway介绍 业务场景 我们服务使用Spring Cloud微服务架构&#xff0c;使用Spring Cloud Gateway 作为网关&#xff0c;使用 Spring Cloud OpenFeign 作为服务间通信方式作为网关&#xff0c;主要作用是鉴权与路由转发。大多数应用场…...

PKI - 03 密钥管理(如何进行安全的公钥交换)

文章目录 Pre密钥管理面临的挑战安全密钥管理的几种方式手动密钥交换与确认受信任的介绍 Pre PKI - 02 对称与非对称密钥算法 密钥管理面临的挑战 密钥管理面临的挑战主要包括以下几点&#xff1a; 安全的公钥交换&#xff1a;在使用基于非对称密钥算法的服务之前&#xff0c…...

Bee+SpringBoot稳定的Sharding、Mongodb ORM功能(同步 Maven)

Hibernate/MyBatis plus Sharding JDBC Jpa Spring data GraphQL App ORM (Android, 鸿蒙) Bee 小巧玲珑&#xff01;仅 860K, 还不到 1M, 但却是功能强大&#xff01; V2.2 (2024春节・LTS 版) 1.Javabean 实体支持继承 (配置 bee.osql.openEntityCanExtendtrue) 2. 增强批…...

HarmonyOS SDK 助力新浪新闻打造精致易用的新闻应用

原生智能是HarmonyOS NEXT的核心亮点之一&#xff0c;依托HarmonyOS SDK丰富全面的开放能力&#xff0c;开发者只需通过几行代码&#xff0c;即可快速实现AI功能。新浪新闻作为鸿蒙原生应用开发的先行者之一&#xff0c;从有声资讯入手&#xff0c;基于Speech Kit朗读控件上线听…...

IT行业有哪些证书含金量高呢?

目录 引言&#xff1a; 一、 计算机网络类证书 二、 数据库管理类证书 三、 安全与信息技术管理类证书 四、 编程与开发类证书 五、 数据科学与人工智能类证书 六、结论&#xff1a; 悟已往之不谏&#xff0c;知来者犹可追 …...

zlib交叉编译(rv1126)

目录 1.下载 2.解压 3.配置 4.编译 1.下载 1&#xff09;下载地址 zlib Home Site 2&#xff09;下载tar.gz版本 下载该版本。 2.解压 1&#xff09;解压到某个文件夹&#xff0c;新建 install-rv1126文件夹 2&#xff09;进入源码目录 3.配置 1&#xff09;导出交叉编…...

数字孪生与智慧园区的融合:打造未来产业生态的新篇章

随着科技的飞速发展&#xff0c;数字孪生和智慧园区已经成为当今社会发展的重要趋势。数字孪生技术为物理世界的对象提供了数字化的复制体&#xff0c;而智慧园区则通过各种信息技术手段实现园区的智能化管理。二者的融合&#xff0c;将为未来产业生态的发展开辟新的篇章。 一…...

nodejs将console.log保存到log.txt文档中(electron工具)

txtConsole.js const { app } require(electron); const fs require(fs); const moment require(moment); const mainData require(./mainData);//electron 软件根目录 const rootPath path.dirname(app.getPath(exe));const txtConsole {log(p1 , p2 , p3 , p4 , p…...

微服务的幂等性

微服务架构设计的中心思想是将服务进行拆分&#xff0c;但是在这个过程中&#xff0c;如果被依赖的服务发生奔溃&#xff0c;就会引起一系列问题。为了解决这个问题&#xff0c;就会引入重试的机制&#xff0c;重试又会引入幂等性的问题&#xff0c;下面我们就分析这个过程&…...

Redis之基础篇

Redis简介 Redis是一种基于键值对&#xff08;Key-Value&#xff09;的NoSQL数据库&#xff0c;它支持string&#xff08;字符串&#xff09;、hash&#xff08;哈希&#xff09;、list&#xff08;列表&#xff09;、set&#xff08;集合&#xff09;、zset&#xff08;有序集…...

靶机实战bwapp亲测xxe漏洞攻击及自动化XXE注射工具分析利用

靶机实战bwapp亲测xxe漏洞攻击及自动化XXE注射工具分析利用。 1|0介绍 xxe漏洞主要针对webservice危险的引用的外部实体并且未对外部实体进行敏感字符的过滤,从而可以造成命令执行,目录遍历等.首先存在漏洞的web服务一定是存在xml传输数据的,可以在http头的content-type中查…...

openGauss学习笔记-216 openGauss性能调优-确定性能调优范围-硬件瓶颈点分析-CPU

文章目录 openGauss学习笔记-216 openGauss性能调优-确定性能调优范围-硬件瓶颈点分析-CPU216.1 CPU216.2 查看CPU状况216.3 性能参数分析 openGauss学习笔记-216 openGauss性能调优-确定性能调优范围-硬件瓶颈点分析-CPU 获取openGauss节点的CPU、内存、I/O和网络资源使用情况…...

【教程】Linux使用git自动备份和使用支持文件恢复的rm命令

转载请注明出处&#xff1a;小锋学长生活大爆炸[xfxuezhang.cn] 背景介绍 首先非常不幸地告诉你&#xff1a;Linux 系统的标准 rm 命令不支持文件恢复功能。一旦使用 rm 删除了文件或目录&#xff0c;它们就会从文件系统中永久删除&#xff0c;除非你使用专门的文件恢复工具尝试…...

记录使用M1 Mac开发LVGL嵌入式项目

技术流 使用Gui Guider进行UI设计&#xff0c;生成lvgl code将lvgl code移植到esp32s3开发板 Gui Guider的安装 安装下面流程一步一步进行 LVGL的移植 硬件&#xff1a;esp32-8048s043开发板 开发环境&#xff1a;PlatformIO M1芯片安装ESP32驱动 从https://www.wch.cn…...

【SpringBoot】JWT令牌

&#x1f4dd;个人主页&#xff1a;五敷有你 &#x1f525;系列专栏&#xff1a;SpringBoot ⛺️稳重求进&#xff0c;晒太阳 什么是JWT JWT简称JSON Web Token&#xff0c;也就是通过JSON形式作为Web应用的令牌&#xff0c;用于各方面之间安全的将信息作为JSON对象传输…...

Python爬虫 Beautiful Soup库详解#4

爬虫专栏&#xff1a;http://t.csdnimg.cn/WfCSx 使用 Beautiful Soup 前面介绍了正则表达式的相关用法&#xff0c;但是一旦正则表达式写的有问题&#xff0c;得到的可能就不是我们想要的结果了。而且对于一个网页来说&#xff0c;都有一定的特殊结构和层级关系&#xff0c;…...

Tkinter教程21:Listbox列表框+OptionMenu选项菜单+Combobox下拉列表框控件的使用+绑定事件

------------★Tkinter系列教程★------------ Tkinter教程21&#xff1a;Listbox列表框OptionMenu选项菜单Combobox下拉列表框控件的使用绑定事件 Tkinter教程20&#xff1a;treeview树视图组件&#xff0c;表格数据的插入与表头排序 Python教程57&#xff1a;tkinter中如何…...

Django中的SQL注入攻击防御策略

Django中的SQL注入攻击防御策略 SQL注入是一种常见的网络安全威胁&#xff0c;可以导致数据库被非法访问和数据泄露。本文将介绍在Django框架中防止SQL注入攻击的关键方法&#xff0c;包括使用参数化查询、使用ORM、进行输入验证和使用安全的编码实践。 SQL注入是一种利用应用程…...

ORM模型类

模型 创建两个表 创建模型类 from django.db import models# Create your models here. class BookInfo(models.Model):name models.CharField(max_length10, uniqueTrue) # 书名pub_date models.DateField(nullTrue) # 发布时间read_count models.IntegerField(default…...

Java强训day14(选择题编程题)

选择题 编程题 题目1 import java.util.Scanner;public class Main {public static void main(String[] args) {//读入年月日&#xff08;字符串形式读入&#xff09;Scanner sc new Scanner(System.in);String s sc.nextLine();String[] ss s.split(" ");i…...

PHP和Node.js哪个更爽?

先说结论&#xff0c;rust完胜。 php&#xff1a;laravel&#xff0c;swoole&#xff0c;webman&#xff0c;最开始在苏宁的时候写了几年php&#xff0c;当时觉得php真的是世界上最好的语言&#xff0c;因为当初活在舒适圈里&#xff0c;不愿意跳出来&#xff0c;就好比当初活在…...

理解 MCP 工作流:使用 Ollama 和 LangChain 构建本地 MCP 客户端

&#x1f31f; 什么是 MCP&#xff1f; 模型控制协议 (MCP) 是一种创新的协议&#xff0c;旨在无缝连接 AI 模型与应用程序。 MCP 是一个开源协议&#xff0c;它标准化了我们的 LLM 应用程序连接所需工具和数据源并与之协作的方式。 可以把它想象成你的 AI 模型 和想要使用它…...

三体问题详解

从物理学角度&#xff0c;三体问题之所以不稳定&#xff0c;是因为三个天体在万有引力作用下相互作用&#xff0c;形成一个非线性耦合系统。我们可以从牛顿经典力学出发&#xff0c;列出具体的运动方程&#xff0c;并说明为何这个系统本质上是混沌的&#xff0c;无法得到一般解…...

多种风格导航菜单 HTML 实现(附源码)

下面我将为您展示 6 种不同风格的导航菜单实现&#xff0c;每种都包含完整 HTML、CSS 和 JavaScript 代码。 1. 简约水平导航栏 <!DOCTYPE html> <html lang"zh-CN"> <head><meta charset"UTF-8"><meta name"viewport&qu…...

重启Eureka集群中的节点,对已经注册的服务有什么影响

先看答案&#xff0c;如果正确地操作&#xff0c;重启Eureka集群中的节点&#xff0c;对已经注册的服务影响非常小&#xff0c;甚至可以做到无感知。 但如果操作不当&#xff0c;可能会引发短暂的服务发现问题。 下面我们从Eureka的核心工作原理来详细分析这个问题。 Eureka的…...

html css js网页制作成品——HTML+CSS榴莲商城网页设计(4页)附源码

目录 一、&#x1f468;‍&#x1f393;网站题目 二、✍️网站描述 三、&#x1f4da;网站介绍 四、&#x1f310;网站效果 五、&#x1fa93; 代码实现 &#x1f9f1;HTML 六、&#x1f947; 如何让学习不再盲目 七、&#x1f381;更多干货 一、&#x1f468;‍&#x1f…...

【C++进阶篇】智能指针

C内存管理终极指南&#xff1a;智能指针从入门到源码剖析 一. 智能指针1.1 auto_ptr1.2 unique_ptr1.3 shared_ptr1.4 make_shared 二. 原理三. shared_ptr循环引用问题三. 线程安全问题四. 内存泄漏4.1 什么是内存泄漏4.2 危害4.3 避免内存泄漏 五. 最后 一. 智能指针 智能指…...

PostgreSQL——环境搭建

一、Linux # 安装 PostgreSQL 15 仓库 sudo dnf install -y https://download.postgresql.org/pub/repos/yum/reporpms/EL-$(rpm -E %{rhel})-x86_64/pgdg-redhat-repo-latest.noarch.rpm# 安装之前先确认是否已经存在PostgreSQL rpm -qa | grep postgres# 如果存在&#xff0…...

Chrome 浏览器前端与客户端双向通信实战

Chrome 前端&#xff08;即页面 JS / Web UI&#xff09;与客户端&#xff08;C 后端&#xff09;的交互机制&#xff0c;是 Chromium 架构中非常核心的一环。下面我将按常见场景&#xff0c;从通道、流程、技术栈几个角度做一套完整的分析&#xff0c;特别适合你这种在分析和改…...

comfyui 工作流中 图生视频 如何增加视频的长度到5秒

comfyUI 工作流怎么可以生成更长的视频。除了硬件显存要求之外还有别的方法吗&#xff1f; 在ComfyUI中实现图生视频并延长到5秒&#xff0c;需要结合多个扩展和技巧。以下是完整解决方案&#xff1a; 核心工作流配置&#xff08;24fps下5秒120帧&#xff09; #mermaid-svg-yP…...