最近学了 spring gateway,之前都是使用 nginx 作为反向代理服务器,但 nginx 比较生疏,现在有了 spring gateway,也可以进行反向代理,作为 java 程序员,配置起来更顺手,所以自然而然地想要用 spring gateway 替换掉 nginx。
创建 spring gateway 的项目,简单地添加依赖 org.springframework.cloud:spring-cloud-starter-gateway:2.3.0.RELEASE
后启动运行。
如果执行单元测试需要添加依赖 testImplementation 'org.springframework.boot:spring-boot-starter-validation'
,否则会报错,具体原因有待进一步分析。
spring gateway 提供了非常方便的配置,可以实现动态资源的转发和重定向,以下简单地配置转发:
spring:
cloud:
gateway:
routes:
- id: peacetrue-microservice-resource-server
uri: lb://peacetrue-microservice-resource-server/
predicates:
- Path=/resource-server/**
- id: peacetrue-region
uri: lb://peacetrue-region/
predicates:
- Path=/regions/**
- id: peacetrue-user
uri: lb://peacetrue-user/
predicates:
- Path=/users/**
- id: peacetrue-attachment
uri: lb://peacetrue-attachment/
predicates:
- Path=/attachments/**
代理动态资源很容易,但对于静态资源目前只能在代码中声明:
@Bean
public RouterFunction<ServerResponse> staticResourceLocator() {
return RouterFunctions.resources("/static/**", new FileSystemResource("/Users/peacetrue/static/"));
}
上面的示例,路径以 /static/ 起始的静态资源,会从物理路径 /Users/peacetrue/static/ 下读取,例如:请求 https://peacetrue.cn/static/index.html 会匹配 /Users/peacetrue/static/index.html 文件。
只能在代码中声明比较繁琐,简单地实现从配置读取:
StaticResourceProperties
@Data
@ConfigurationProperties("peacetrue")
public class StaticResourceProperties {
/** 静态资源配置,key 表示路径规则,value 表示转发地址 */
private Map<String, String> staticResources = new LinkedHashMap<>();
}
StaticResourceAutoConfiguration
@Slf4j
@Configuration
@EnableConfigurationProperties(StaticResourceProperties.class)
public class StaticResourceAutoConfiguration {
private StaticResourceProperties properties;
public StaticResourceAutoConfiguration(StaticResourceProperties properties) {
this.properties = properties;
}
@Bean
@ConditionalOnMissingBean(name = "staticResources")
public Map<String, String> staticResources() {
return properties.getStaticResources();
}
@Bean
@ConditionalOnBean(name = "staticResources", value = Map.class)
public RouterFunction<ServerResponse> staticResourceLocator(ResourceLoader resourceLoader,
Map<String, String> staticResources) {
//空 `Map` 其实不需要启用配置,但没有 `@ConditionalOnNotEmptyBean` 这种注解, https://stackoverflow.com/questions/62734544/spring-conditionalonproperty-for-bean[此问题^] 待优化
if (staticResources.isEmpty()) return null;
RouterFunctions.Builder builder = RouterFunctions.route();
staticResources.forEach((key, value) -> {
log.debug("添加静态资源配置: [{}] -> [{}]", key, value);
builder.add(RouterFunctions.resources(key, resourceLoader.getResource(value)));
});
return builder.build();
}
}
配置示例
peacetrue:
static-resources:
#路径需要转译
'[/test/**]': file:/Users/docs/antora/modules/ROOT/pages/
#配置物理路径地址
'[/summarize/**]': file:/root/peacetrue/document-antora/public/
#配置类路径地址
'[/classpath/**]': classpath:public/
有一个 node 应用,前端监听在 3000 端口,后端监听在 8001 端口,想统一通过 https 端口 443 访问,并且前端直接挂在主域 peacetrue.cn 下。
之前使用 nginx 配置如下:
nginx 配置
server {
listen 443 ssl http2 default_server;
server_name peacetrue.cn;
#证书信息
ssl_certificate 1_peacetrue.cn_bundle.crt;
ssl_certificate_key 2_peacetrue.cn.key;
ssl_session_cache shared:SSL:1m;
ssl_session_timeout 10m;
ssl_ciphers HIGH:!aNULL:!MD5;
ssl_prefer_server_ciphers on;
# 所有请求默认转发到前端,因为前端直接挂在主域下
location / {
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_pass http://localhost:3000/;
}
# 以 games 起始的请求转发到后端
location /games/ {
include /etc/nginx/include/proxy.conf;
proxy_pass http://localhost:8001/games/;
}
# 以 socket.io 起始的请求转发到后端
location /socket.io/ {
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_pass http://localhost:8001/socket.io/;
}
# 备案信息
location /MP_verify_t4rKSxor2MowtjoC.txt {
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_pass http://localhost:9000/MP_verify_t4rKSxor2MowtjoC.txt;
}
}
现改为使用 spring gateway 配置如下:
spring gateway 初始配置
#https 配置
server:
port: 443
ssl:
key-store: file://${user.home}/peacetrue.cn.jks
key-alias: peacetrue.cn
key-store-password: ${MICROSERVICE_SSL_PASSWORD:password}
#配置动态代理
spring:
cloud:
gateway:
routes:
#前端转发到 3000
- id: biog_front
uri: http://localhost:3000/
predicates:
- Path=/**
#后端转发到 8001
- id: biog_back
uri: http://localhost:8001/
predicates:
- Path=/games/**,/socket.io/**
#配置静态代理
peacetrue:
static-resources:
#备案信息
'[/MP_verify_t4rKSxor2MowtjoC.txt]': file:/usr/share/nginx/html/MP_verify_t4rKSxor2MowtjoC.txt
路由会从上到下顺序匹配,所以代理所有请求的 biog_front 必须放在末尾,而且还要保证它的优先级低于静态代理,不然静态代理不会被执行。测试后发现,动态代理的优先级始终高于静态代理,那么就不能使用代理所有请求。调整配置如下:
spring gateway 子路径配置
#配置动态代理
spring:
cloud:
gateway:
routes:
#如果是主域 https://peacetrue.cn/ 直接重定向到 https://peacetrue.cn/game/
- id: biog_front
uri: https://peacetrue.cn/game/
predicates:
- Path=/
filters:
- RedirectTo=302, https://peacetrue.cn/game/
#如果是 /game 起始的,去掉 /game 后,进行转发
- id: biog_front_game
uri: http://localhost:3000/
predicates:
- Path=/game/**
filters:
- RewritePath=/game(?<segment>/?.*), $\{segment}
测试发现,访问 https://peacetrue.cn/ 会重定向到 https://peacetrue.cn/game/ ,界面可以正常打开,但静态资源全部失效:
静态资源仍然直接访问主域,应该是使用了绝对地址而非相对地址。这样只能找出前端所有的具体请求,然后分别配置代理:
spring gateway 静态资源配置
spring:
cloud:
gateway:
routes:
- id: biog_front
uri: http://localhost:3000/
predicates:
- Path=/,/*.js,/*.json,/static/**,/_next/**,/room/**
测试后发现一切正常,最终配置如下:
spring gateway 配置
spring:
cloud:
gateway:
routes:
- id: biog_front
uri: http://localhost:3000/
predicates:
- Path=/,/*.js,/*.json,/static/**,/_next/**,/room/**
- id: biog_back
uri: http://localhost:8001/
predicates:
- Path=/games/**,/socket.io/**
peacetrue:
static-resources:
#不能直接使用 /MP_verify_t4rKSxor2MowtjoC.txt,必须使用匹配模式,简单的将末尾字符改为?: MP_verify_t4rKSxor2Mowtjo?.txt
'[/MP_verify_t4rKSxor2Mowtjo?.txt]': file:/usr/share/nginx/html/MP_verify_t4rKSxor2MowtjoC.txt
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。