Gateway 网关
git地址:
http://10.16.202.103:8089/component/component-ser/gosci-tech-gateway-archetype
使用archetype骨架生成时,替换自己的gav:
mvn archetype:generate -DgroupId=com.gosci.tech -DartifactId=ocean-gateway -Dversion=0.0.1-SNAPSHOT -Dpackage=com.gosci.tech.gateway -DarchetypeGroupId=com.gosci.tech -DarchetypeArtifactId=gosci-tech-gateway-archetype -DarchetypeVersion=1.0-SNAPSHOT -DinteractiveMode=false
Gateway的作用
目前,Gateway模块具有五项功能:
- 路由转发
- 限流,基于令牌桶算法
- 集成权限,完成最基础的认证
- 用户信息透传
- 流量标记
1、路由转发
最核心的功能,将http请求和微服务的服务端分离,避免暴露真正的服务地址。常用静态路由、动态路由、自动路由三种配置方式,静态路由不支持动态添加、修改、删除路由,所以不推荐使用。
自动路由
Gateway网关通过微服务的名称,进行转发到对应的微服务:
spring:
cloud:
gateway:
discovery:
locator:
#开启根据服务名称自动转发
enabled: true
#微服务名称以小写形式呈现
lower-case-service-id: true
配置和使用起来非常方便,但是,个人没有采用这种方式,有两个原因:
- 无法给单个微服务添加拦截器
filter
- 有时候微服务的服务名可能过长,前端配接口路由的时候有些太长了
所以,推荐使用下面动态路由的方式。
动态路由
动态路由是指在运行时动态添加、修改和删除路由规则,可以根据不同的条件动态地调整路由规则。实现原理是使用RouteDefinitionWriter
来操作路由,通过它的 save()
和 delete()
方法可以动态的添加或删除路由规则。
具体实现可以看DynamicRouteConfig
这个类,我们在代码中添加了一个nacos文件的Listener
监听器,通过监听nacos上的配置文件,实现路由的动态刷新。动态路由以json的形式进行配置:
[
{
"id": "gosci-tech-basic-archetype",
"uri": "lb://gosci-tech-basic-archetype",
"order": 0,
"predicates": [{
"args": {
"pattern": "/basic/**"
},
"name": "Path"
}],
"filters": [{
"name": "StripPrefix",
"args": {
"_genkey_0": "1"
}
}]
},
{
"id": "gosci-tech-auth-archetype",
"uri": "lb://gosci-tech-auth-archetype",
"order": 0,
"predicates": [{
"args": {
"pattern": "/auth/**"
},
"name": "Path"
}],
"filters": [{
"name": "StripPrefix",
"args": {
"_genkey_0": "1"
}
}]
}
]
2、限流
SpringCloud Gateway 中实现了一个RedisRateLimiter
,底层算法基于令牌桶,使用Redis 的 lua脚本实现。配置:
spring:
cloud:
gateway:
default-filters:
- name: RequestRateLimiter
args:
#令牌桶每秒填充平均速率,即等价于允许用户每秒处理多少个请求平均数
redis-rate-limiter.replenishRate: 2
# 令牌桶容量,允许在一秒钟内完成的最大请求数
redis-rate-limiter.burstCapacity: 10
# 每次消费的Token数量
redis-rate-limiter.requestedTokens: 1
#SPEL表达式去的对应的bean
rate-limiter: "#{@defaultRedisRateLimiter}"
#SPEL表达式去的对应的bean
key-resolver: "#{@apiKeyResolver}"
对接口进行压测测试,访问20次接口:
可以看到后面的接口会被拦截,重新生成令牌后才能通过:
3、身份认证
认证
所有经过Gateway的接口,除配置在白名单外的,都需要进行基本的认证操作,认证过程由Gateway服务调用Auth服务的Feign接口完成,具体实现逻辑在AccessGatewayFilter
这个类中。
实际上,认证服务是由Auth服务完成的,Gateway只负责接口调用!
实现逻辑在自定义的AccessGatewayFilter
这个拦截器的filter
方法中,调用Auth服务中LoginApi
的checkToken
方法,通过传递token完成用户校验。
白名单
对于不需要认证的接口,需要在数据库中配置白名单,白名单配置在数据库的gateway_white_list
这张表中。当匹配到对应的路由规则时,会直接进行放行不走权限认证:
4、用户信息透传
在通过用户认证后,Auth服务会返回给网关用户的基础信息,通过在Http请求中添加一个header
的形式进行向下传递:
ServerHttpRequest newRequest = exchange.getRequest().mutate()
.header("authUser",
Base64Utils.encodeToString(JsonUtils.toString(authUser).getBytes(StandardCharsets.UTF_8)))
.header(HttpHeaders.AUTHORIZATION, " ")
.header(TAG_SOURCE, "portal")
.build();
整体流程:
这样,在微服务中可以把用户信息反序列化后存到ThreadLocal中,随时使用。
5、流量标记
目前系统暂时没有区分门户端和运营端,流量标记可能暂时没有什么作用,但是如果之后区分两端了,那么到微服务后端的接口就需要进行区分了。
暂时规划流量标记在header中添加一个X-TAG-SOURCE
的标记,值为portal
或admin
。
注意另一个使用到的流量标记是X-TAG-FEIGN
,用来表示请求是通过feign调用进行的。
gateway_white_list 建表语句
CREATE TABLE `gateway_white_list` (
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键',
`path` varchar(255) NOT NULL COMMENT '白名单地址',
`gateway_type` tinyint(2) NOT NULL COMMENT '网关类型,1:门户端网关,2:运营网关',
`service_name` varchar(255) DEFAULT NULL COMMENT '服务名,该项白名单属于哪个服务的',
`create_time` datetime DEFAULT NULL COMMENT '创建时间',
`update_by` varchar(32) DEFAULT NULL COMMENT '更新人',
`update_time` datetime DEFAULT NULL COMMENT '更新时间',
`del_flag` tinyint(2) NOT NULL DEFAULT '0' COMMENT '删除标志',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8mb4 COMMENT='网关白名单';