以下博客最近追加了更新,欢迎回顾 口语化讲解MySQL - 掘金 (juejin.cn)
前言
完蛋,我被缅北大汉包围了!开幕雷击,博主接近一个月没更新了,原因不为其他,只为博主进了缅北,9116一个多月后超级加倍迎来了每晚2-4点下班的福报,我的腰子正在经历前所未有的挑战。以下文章由我的腰子代笔,且征询大佬同意后发出,已抹除隐私信息,看在我腰子写了大半个月的份上给个赞吧,拜托啦,这对我真的很重要!
自研背景
网关和注册中心在部门服务治理体系中占据着绝对关键的地位,不仅作为所有微服务对外的唯一入口,还充当多种服务治理功能的载体。主要提供以下能力:服务注册与发现、API转发、动态路由、负载均衡、鉴权授权、限流限频、日志与监控支持、熔断降级。部门对于网关的定位是企业级网关,相比常规的产品级网关提供更全面和更灵活的功能及配置来适配企业的需求。部门网关相对于常规的来说,最主要的区别是业务系统之间无法发现,从而无法直接进行远程过程调用。(这里想问下大家,各位做企业级网关的时候,是使用成熟产品比如ShenYu、Kong。还是说用开源产品做二开,比如在SpringGateway上去改)
应用场景
- 前后端调用-前端指移动端及PC端,面向公司直接用户,需要对用户进行身份鉴权、安全控制、日志监控、链路追踪等
- 内部跨服务-与前后端的唯一区别在于调用方是另一个企业内部服务
- 外采系统集成-公司外采部署在内部的独立系统或者Saas系统(提供基于云的软件服务,用户通过网络访问应用程序,无需在本地安装软件)
- 外部业务及开源封装-比如天眼查、银企直连、短信飞书消息通知、消息通知等,是对外部服务或者开源中间件的统一封装,方便内部调用,还会增加流控、配额、安全等功能
- 中台业务-比如开发平台、网关和其他部门自研系统等底层通用服务的封装
- 开放接口-企业内部产品对外暴露的API,需要增加流控、配额、安全等功能
代码分析
RJNATRequestController
继承了抽象网关类AbstractGatewayFilterFactory用于简化自定义Spring GateWay过滤器的生成,用于在请求进入网关时和响应离开网关时对它们进行修改或扩展。RJNATRequestController是一个自定义的过滤器工厂,内部提供方法有
- checkNatEnv--从自定义的请求头(何时注入的)中获取请求环境env和请求路由环境natEnv,校验两者是否相同,确保路由地址指向正确环境
- checkWhileIp--从自定义Config类中获取白名单IP,看是否包含当前请求的真实IP地址
内部类只有一个Config,内部是一个白名单IP集合
RJAppRequestController继承RJNATRequestController
还是一个抽象类(应该和上面那个类合并的,这个没有扩展意义),初衷可能是想要区分HTTP和RPC请求
- RJServiceProvider--接口类,内部提供方法获取服务和根据服务ID获取服务
- MongoTemplate--引入MongoDB的操作类,其中存储了当前登录用户的ticket
- getSysIdFromHttp--从HTTP请求头中获取sysid
- checkAuthorization4SysId--判断getSysIdFromHttp获取的sysid在Config类变量sysidList中存在
- checkSign--校验登录人信息,从请求头获取令牌signserverauth,该令牌由服务ID(sysid)+服务令牌secretkey+时间戳组成,校验前两者相同,再根据时间戳进行有效期的判断
- check--通过sysid获取Mono配置信息
RJRequestControllerGatewayFilterFactory继承RJAppRequestController
并非工厂类,哈哈,别被名字误导了。最基本的请求过滤器,重写org.springframework.cloud.gateway.filter.factory.GatewayFilterFactory#apply(C),只校验checkNatEnv
RJServiceRequestControllerGatewayFilterFactory继承RJAppRequestController
跨后端服务请求过滤器(对应上面的应用场景2),处理逻辑是checkNatEnv,通过后checkWhileIp如果白名单通过,进入下一个过滤器。如果不在白名单,checkAuthorization4SysId,最后check
reactor.core.publisher.Mono这个类在SpringGateway中有什么用
在 Spring WebFlux 中,特别是在 Spring Cloud Gateway 这样的项目中,reactor.core.publisher.Mono
被广泛用于处理异步操作和构建响应式流式处理。
在 Spring Cloud Gateway 中,Mono
主要用于异步处理和链式操作。作为响应式编程的一部分,Gateway 中的路由、过滤器和各种操作可以通过 Mono
来表示异步结果或者进行异步操作。举例来说:
- 路由操作 :当定义路由规则时,可以使用
Mono
来处理请求,并对请求进行转发、修改或过滤。 - 过滤器 :Gateway 中的过滤器可以通过
Mono
进行异步操作,例如对请求进行验证、转换、重定向或者添加响应头等。 - 异步处理 :Spring Cloud Gateway 支持异步非阻塞的操作,
Mono
可以用来处理异步任务或异步操作,并将结果进行链式处理。
示例代码可能如下所示:
kotlin
@Component
public class CustomFilter implements GlobalFilter {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
// 异步操作示例:在请求头中添加一个自定义信息
return Mono.fromRunnable(() -> {
exchange.getResponse().getHeaders().add("Custom-Header", "Custom-Value");
}).then(chain.filter(exchange)); // 执行下一个过滤器
}
}
在这个示例中,CustomFilter
是一个自定义的过滤器,它使用 Mono
实现了异步操作,在请求头中添加了一个自定义的信息,然后继续执行下一个过滤器。
总的来说,在 Spring Cloud Gateway 中,Mono
被用于异步的、非阻塞的处理流程中,可以作为异步操作的结果的表示,也可以进行链式操作以处理请求和响应的处理管道。
RJSessionRequestControllerGatewayFilterFactory继承RJAppRequestController
处理逻辑checkNatEnv-checkWhileIp-checkAuthorization4SysId-checkSign-checkAuthorization4session
RJWSRequestControllerGatewayFilterFactory继承RJAppRequestController
处理逻辑checkNatEnv-checkWhileIp-checkAuthorization4SysId,用于处理websocket获取请求头参数sysid时有所区别,是从请求参数集合中获取数据
RJWeb2RequestControllerGatewayFilterFactory继承RJAppRequestController
RJWebRequestControllerGatewayFilterFactory继承RJAppRequestController
处理逻辑checkNatEnv-checkWhileIp-checkAuthorization4SysId,用于处理web端请求,请求头参数略有不同
RJRequestRateLimiterGatewayFilterFactory继承AbstractGatewayFilterFactory
封装了SpringGateway中的RateLimiter和KeyResolver,提供了扩展---没太搞明白逻辑,到时候问一下
自定义两种KeyResolver,一种是请求IP,一种是服务注册ID
RJLogGlobalFilter实现GlobalFilter和Ordered
往ServerWebExchange填入请求开始时间,GatewayFilterChain.then,.doOnError,.doOnCancel处理各种情况,填入相应日志
RJCircuitBreakerGlobalFilter实现GlobalFilter和Ordered
通过io.github.resilience4j.circuitbreaker.CircuitBreakerRegistry对异常及故障进行容错处理,这里是打印了异常日志并向上继续抛出错误,熔断机制保障,大部分在配置文件中配置。
RJGlobalErrorExceptionHandler继承DefaultErrorWebExceptionHandler
DefaultErrorWebExceptionHandler
是 Spring WebFlux 中用于处理异常的默认异常处理器。
在 Spring WebFlux 应用中,当发生异常时,它会尝试捕获异常并将其转换为 HTTP 响应。DefaultErrorWebExceptionHandler
的主要功能包括:
- 异常处理:捕获应用程序中的异常,并尝试将其转换为适当的 HTTP 响应。它可以处理来自控制器、过滤器等的异常,将其映射到标准的 HTTP 状态码和响应体。
- 自定义处理 :可以通过继承
DefaultErrorWebExceptionHandler
并覆盖其中的方法来自定义异常处理的行为。这使得开发人员可以根据自己的需求来定义异常的处理方式。 - 错误信息的格式化:它可以将异常信息转换为标准的错误响应,通常包括异常的类型、消息以及可能的堆栈信息。
- 默认的异常处理策略 :
DefaultErrorWebExceptionHandler
提供了默认的异常处理策略,如果开发者没有显式地定义异常处理方式,它会使用默认的处理方式来处理异常。
在 Spring WebFlux 应用中,DefaultErrorWebExceptionHandler
通常会被自动配置为默认的异常处理器。如果你想要自定义全局异常处理的行为,可以通过扩展和配置 DefaultErrorWebExceptionHandler
或编写自己的异常处理器来实现。
DefaultErrorWebExceptionHandler
和 @RestControllerAdvice
都是在 Spring 中处理异常的方式,但是它们有一些区别:
- 作用范围:
-
DefaultErrorWebExceptionHandler
是用于处理全局的、WebFlux 应用中发生的异常,它处理的是整个应用中的异常情况,包括未被处理的异常、错误的状态码和响应等。@RestControllerAdvice
则是一个注解,用于定义全局的异常处理器,通常结合@ExceptionHandler
注解使用,针对特定的控制器或者控制器中的特定异常进行处理。
- 适用场景:
-
DefaultErrorWebExceptionHandler
更适合于对 WebFlux 应用中全局的异常进行统一处理,例如定制错误页面、格式化错误信息等。@RestControllerAdvice
更适合于针对特定的控制器或控制器中的特定异常进行自定义处理,可以根据需要定义多个@ExceptionHandler
方法来处理不同类型的异常。
- 自定义性:
-
DefaultErrorWebExceptionHandler
可以自定义,但需要继承并覆盖其方法,相对来说更加底层、全局化。@RestControllerAdvice
则更直接,通过注解方式来定义全局异常处理,更加灵活且对定制性要求不高。
一般来说,在 Spring WebFlux 应用中,可以同时使用这两种方式。DefaultErrorWebExceptionHandler
更适合处理全局的异常情况和错误状态码,而 @RestControllerAdvice
更适合处理特定控制器或特定异常的情况。
该类处理全局异常,并修改响应数据格式为公司定制风格
RJConfigProviderImpl
getService--通过WebClient,定义和发送异步的 HTTP 请求,并且可以利用响应式编程的特性处理响应流,从开发配置中心获取指定服务信息
getRouteDefinitions--通过系统环境参数env调用开发配置中心获取路由数据
RJServiceProviderImpl
服务信息的增删改查。网关服务启动时、10s定时、消息队列、后门接口四种方式保证数据同步
RJRouteDefinitionRepository实现RouteDefinitionRepository继承RouteDefinitionLocator和RouteDefinitionWriter
动态地向应用中添加、删除、修改路由定义,实现动态路由的管理。ApplicationContext.publishEvent(new RefreshRoutesEvent(this))推送刷新路由事件,大量反应式编程方式代码。
MessageListener
消息队列消费者,从rabbitmq中获取服务增删改数据然后调用RJServiceProvider的接口
RJLoadBalancer实现ReactorServiceInstanceLoadBalancer
区域负载均衡策略类,重写了org.springframework.cloud.loadbalancer.core.ReactorLoadBalancer的choose方法,服务根据区域进行轮询负载。轮询是自增数取余解决,区域是通过网关服务启动时填入环境参数得到。
RJGatewayControllerEndpoint
提供一些常用信息查询和后门操作接口,比如获取全部路由,后门刷新增删路由
服务功能分析
服务注册与发现
服务注册是通过引入SDK完成每个微服务的注册,服务启动时申报自己的注册信息,跟网关服务本身无关。服务发现部分,修改了nacos的源码,禁止跨组进行服务发现。因为部门正式环境共用一套Nacos,为了避免越权调用,因此禁用了直接调用,而是限制只能通过HTTP调用网关进行请求转发。
API转发与负载均衡
对于一个web端的请求来讲,流程是通过Http请求指定的网关域名+接口路径,首先DNS会解析域名发送请求到对应的IP地址,然后Nginx转发给后端网关服务的某个节点。网关节点会匹配当前服务的地址标签,该标签在自研的开发服务平台上注册服务时必填,优先转发到网关集群中同一地址标签的节点。网关从全部路由信息中筛选出被调用服务信息,进行请求转发。
动态路由
网关服务启动时、10s定时、消息队列、后门接口四种方式保证路由数据同步
鉴权授权
通过一系列网关过滤器保证
限流限频
自定义实现了请求IP和服务ID的键选择器,实现使用了SpringGateway默认支持的限流限频策略,但是操作上提供了自定义配置。在开发服务平台上可以通过JSON进行限流限频参数的配置,然后推送到网关服务,网关服务解析推送信息,刷新路由信息,并增加该限流限频配置。
写在最后
大致是我梳理了一遍,顺了顺流程,除此之外,还有自研的开发服务平台(DevOps)和日志收集平台以及开发框架可以聊聊。后两者其实看我之前写过的也行,大体逻辑都类似,相对比较简单,开发服务平台不出意外的话,会是下一篇,我个人很感兴趣。部门这方面技术跨越比较快,四年多经历了Jenkins、猪齿鱼、自研DevOps服务,很遗憾没有参与开发,不过哈哈我有权限可以看,混入架构组看看源码,带来一些粗糙的理解和分享。
本篇讲网关的并不成体系,算是试水篇吧,后续会追加更新的。最近各种意义上的累,累还焦虑,哈哈,麻了,心态要好,有时候也挺佩服自己的,成天傻乐,缺少危机感。厉害的人真的很多呀,大家都在努力地活着,希望我自己能在拼搏和安逸中掌握好平衡。说点闲话,哈哈,我对象是老S了,因此博主的生活真的是多姿多彩,精彩纷呈(真字面意思)。