前言
随着公司业务的发展,目前公司的服务数已达到了21个,旧业务一直都是一个业务一个域名,再加上我们有n套测试环境,因此运维要维护的域名为21n个(这个数只多不少),大大增加了运维的维护成本,包括新起一套测试环境域名问题也十分困扰人。
同时微服务之前的鉴权原本也是通过nginx塞请求头的方式来处理,每次新起服务都需要重新配置。
跨域也是在各个服务里自己配置,有时候开发会忘记在各自服务里配置跨域,导致时间成本的增加。
综上所述在服务数日益增加的需求下,接入网关刻不容缓。
系统配置及版本
1 | # 线上服务器配置 |
依赖 | 版本 |
---|---|
SpringCloud | Hoxton.SR10 |
SpringGateway | 2.2.7.RELEASE |
SpringBoot | 2.3.8.RELEASE |
SA-TOKEN | 1.25.0 |
*** |
全局过滤器
全局过滤器不需要配置在每个路由规则下就可以生效可以用来配置网关日志、白名单、鉴权等功能,通过实现Odered接口来实现过滤器优先级,优先级如下
1 | /** |
1 | 4j |
统一接口权限
统一接口权限在项目中采用的是SA-TOKEN,因此按照SA-TOKEN接口权限文档编码即可。可以集成nacos配置中心,变成实时可控的权限。
这边需要注意先注册SA-TOKEN全局过滤器
1 |
|
1 | // 全局鉴权开关 |
1 | 4j |
统一APIGateway knife4j接入
网关侧配置
首先引入knife4j的依赖
1 | <dependency> |
1 | 4j |
1 |
|
如此之后,访问http://${网关域名}/doc.html
,能看到统一swagger文档,网关会根据服务名去业务服务去自动收集swagger的json展示在页面上,因此子服务也需要集成swagger框架,即访问${业务服务}/v2/api-docs
能拿到业务服务的接口json文档即可。
业务服务侧配置
1 | <dependency> |
1 | /** |
业务服务集成swaager文档即可,这边可以继续进行优化,将swagger封装成一个starter供给另外的服务引用
动态路由
由于网关是一个比较基础的服务,他的重启几乎会影响所有后端服务的访问,因此不能经常重启,此时我们就非常需要能实时改换路由规则的一个功能。本文将结合nacos实现网关动态路由
1 | /** |
动态路由实现类
1 | /** |
1 | /** |
由此在nacos中新增配置文件gateway-router即可实现动态路由,可以根据自己需要新增过滤器或者更改路由规则
1 | nacos: |
1 | [{ |
一般情况下,我们为了方便可能在网关转发的时候直接使用gateway自带的根据服务名进行路由转发,但这不影响动态路由的配置,两者可以共存。
1 | gateway: |
跨域及重复跨域问题解决
1 | Access to XMLHttpRequest at 'http://192.168.0.144:8762/ctginms_main2019/api/traceRoute/getMapData' from origin 'http://localhost:8080' has been blocked by CORS policy: The 'Access-Control-Allow-Origin' header contains multiple values '*, *', but only one is allowed. |
由于之前的老项目每个服务对应一个域名,且存在很多h5页面直接根据老域名进行调用,前端修改域名起来十分困难,因此后端需要兼容这些老域名。
之前的技术架构中,每个业务服务都有自己的跨域配置,且都开启了(为了兼容老h5页面访问),现在需要接入统一网关,新服务的跨域配置理当在网关中统一配置,不然后续服务多起来一个配置要重复n次,不太合理,由此造成了当经过网关访问到某个旧服务(自己开启跨域配置)的时候会出现以上错误。意思就是重复跨域了。
什么是跨域
何谓同源:URL由协议、域名、端口和路径组成,如果两个URL的协议、域名和端口相同,则表示它们同源。浏览器的同源策略,从一个域上加载的脚本不允许访问另外一个域的文档属性 ,是浏览器上为安全性考虑实施的非常重要的安全策略。违反了这个同源策略我们就认为是跨域访问了,浏览器会拒绝这项请求。
以上看起来十分拗口,想要理解上面的内容也有点困难,其实可以简单的理解,后端返回的resonse中跨域的配置比如配置了允许的请求头,当前请求是否带有其他的非法请求头访问,如果是非法的即不能访问。
1 | public static final String ACCESS_CONTROL_ALLOW_CREDENTIALS = "Access-Control-Allow-Credentials"; |
重复跨域解决
之前说过网关和业务服务都开启了跨域配置因此我们可以理解成一个请求链经过网关和业务服务都给他的ResonseHeader
加上了以Access-Control-Allow-Origin
为Key,*
为Value的键值对,这就是重复跨域。
我们可以看下HttpHeaders
的数据结构,他实现了名为MultiValueMap
的数据结构,而这个底层的数据结构是Map<K, List<V>>
因此我们可以知道这个数据结构一个key可以存放多个值,由此之前的*,*
也可以理解了
1 | public class HttpHeaders implements MultiValueMap<String, String>, Serializable { |
我们只需要在网关侧拦截到请求链的response,对ResonseHeader
进行处理即可,如果有重复的value则给他去重在放行给浏览器这样问题也就迎刃而解了。
1 | /** |
最后我们只需要在对应的路由规则上配置该路由器即可
1 | gateway: |
Exceeded limit on max bytes to buffer : 262144
接入完统一网关之后,上线的第一天晚上,监听到网关侧有大量Exceeded limit on max bytes to buffer : 262144
该类型错误。经过排查发现是因为业务服务返回的json串大于了256k导致的。
配置文件配置缓冲区大小
经过参考网上的解决方案之后,按照如下配置即可放大缓冲区的大小,但是在SpringBoot版本2.3.8.RELEASE
中配置并没有起作用。
1 | spring: |
配置类配置缓冲区大小
于是采用了通过配置来进行配置缓冲区大小,但是在当前SpringBoot版本中这种方法也没有起到作用。
1 |
|
重写Spring原文件
我们可以看到在SpringBoot2.3.8.RELEASE
中这个缓存区大小被硬编码了,因此我们如何更改都无效,这边采用了一个不怎么优雅的办法,就是在自己项目文件中建一个和Spring源码相同的包名类名的文件并将新建的AbstractDataBufferDecoder<T>
这个类的缓存区调大即可,最后采用了这种方案解决了问题。
1 | package org.springframework.core.codec; |
升级SpringBoot版本
按照网上大量资料的说法在SpringBoot高版本中这个问题得到了解决,但是在一个稳定运行的线上项目,想要升级SpringBoot版本风险实在太大,因为有很多未知的坑。因此这种方法一开始就被摒弃了。有兴趣的可以自己尝试下高版本中是否开启这项配置有效。