前言
网关是微服务架构的入口,外网请求通过网关转发到独立的微服务。项目一般会经过多个环境的测试,最终发布到生产。一个http请求,如:http://public_host/api/v1/some_service/some_path?a=b&c=d
会先经过公网域名,然后通过nginx匹配路径/api/v1/**
把请求转发到相应的网关,网关再转发给对应服务。
本地开发时,一般会采用idea的Tools->HTTP Client->Create Request in HTTP Client
工具来调试本地接口(http://localhost:8080/some_path?a=b&c=d
),发布测试环境后,用postman来调试测试环境接口(http://public_host/api/v1/some_service/some_path?a=b&c=d
);也就是说,本地开发时在idea中写的接口测试文档,等发布到测试环境后,又要再写一遍。如果本地有一个网关,提供基本的转发功能,就能打通本地和测试环境的隔阂:一份接口测试文档,多个环境使用。
本地搭一个网关,方式有很多:
- idea起一个spring-cloud-gateway项目:优点是简单易操作,可定制,缺点启动速度慢,占用内存高;
- 下载一个nginx服务器,本地起一个nginx服务,用于转发请求:也比较简单,内存也不高,缺点要阅读文档,不能修改代码来自定义;
- 用graalvm把spring-cloud-gateway编译成本地exe:springcloud项目,学习成本低,转发逻辑可以自定义,按需修改;本地镜像,启动快,占用内存低。
实践环节
准备工作
- 安装graalvm以及native-image
- 安装Visual Studio 2022 Developer Command Prompt v17.4.3
创建spring-cloud-gateway项目
添加下面几个依赖项:
- GraalVM Native Support
- Spring Reactive Web
- Gateway
- Spring Boot Actuator(监控端点,可选)
完整的pom文件如下:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.0.1</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>io.github.xxx</groupId>
<artifactId>cloudgateway</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>cloudgateway</name>
<description>cloudgateway</description>
<properties>
<java.version>17</java.version>
<spring-cloud.version>2022.0.0</spring-cloud.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.projectreactor</groupId>
<artifactId>reactor-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.graalvm.buildtools</groupId>
<artifactId>native-maven-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
配置文件配置了跨域和一个路由,调整了日志级别,代码如下:
server:
port: 9090
spring:
application:
name: cloudgateway
cloud:
gateway:
globalcors:
add-to-simple-url-handler-mapping: true
cors-configurations:
'[/**]':
maxAge: 3600
allowCredentials: false
allowedOrigins: "*"
allowedMethods: GET,HEAD,POST,PUT,PATCH,DELETE,OPTIONS,TRACE
allowedHeaders: "*"
exposedHeaders: Content-Length,content-disposition
routes:
- id: path_route
uri: http://127.0.0.1:8080
predicates:
- Path=/api/v1/**
filters:
- StripPrefix=3
logging:
level:
org.springframework.cloud.gateway.handler.predicate.PathRoutePredicateFactory: trace
https://start.spring.io
网站创建的项目模板有一个HELP.md
,提供了很多有用的信息。
开始编译
打开Visual Studio 2022 Developer Command Prompt v17.4.3,切换到项目目录,执行:
mvnw.cmd native:compile -Pnative -DskipTests
估计有10来分钟,先喝杯咖啡。
查看结果
项目target目录下,生成了一个cloudgateway.exe文件,双击打开,正常运行。
仿照上面流程又编译了一个普通的spring-boot项目(只包含一个接口和actutor端点),名叫demo.exe,双击打开,正常运行。
在浏览器输入一个请求发给网关转发,发现报错如下:
Caused by: java.lang.NullPointerException: null
at java.base@17.0.5/sun.net.dns.ResolverConfigurationImpl.stringToList(ResolverConfigurationImpl.java:69) ~[na:na]
at java.base@17.0.5/sun.net.dns.ResolverConfigurationImpl.loadConfig(ResolverConfigurationImpl.java:136) ~[na:na]
at java.base@17.0.5/sun.net.dns.ResolverConfigurationImpl.nameservers(ResolverConfigurationImpl.java:159) ~[na:na]
at jdk.naming.dns@17.0.5/com.sun.jndi.dns.DnsContextFactory.serversForUrls(DnsContextFactory.java:149) ~[cloudgateway2.exe:na]
at jdk.naming.dns@17.0.5/com.sun.jndi.dns.DnsContextFactory.getContext(DnsContextFactory.java:81) ~[cloudgateway2.exe:na]
at jdk.naming.dns@17.0.5/com.sun.jndi.dns.DnsContextFactory.urlToContext(DnsContextFactory.java:120) ~[cloudgateway2.exe:na]
at jdk.naming.dns@17.0.5/com.sun.jndi.dns.DnsContextFactory.getInitialContext(DnsContextFactory.java:64) ~[cloudgateway2.exe:na]
at java.naming@17.0.5/javax.naming.spi.NamingManager.getInitialContext(NamingManager.java:732) ~[cloudgateway2.exe:na]
at java.naming@17.0.5/javax.naming.InitialContext.getDefaultInitCtx(InitialContext.java:305) ~[cloudgateway2.exe:na]
at java.naming@17.0.5/javax.naming.InitialContext.init(InitialContext.java:236) ~[cloudgateway2.exe:na]
at java.naming@17.0.5/javax.naming.InitialContext.<init>(InitialContext.java:208) ~[cloudgateway2.exe:na]
at java.naming@17.0.5/javax.naming.directory.InitialDirContext.<init>(InitialDirContext.java:130) ~[cloudgateway2.exe:na]
at io.netty.resolver.dns.DirContextUtils.addNameServers(DirContextUtils.java:49) ~[na:na]
at io.netty.resolver.dns.DefaultDnsServerAddressStreamProvider.<clinit>(DefaultDnsServerAddressStreamProvider.java:53) ~[na:na]
... 145 common frames omitted
百度了半天,找到了报错原因:原来是HttpClient
没有配置默认的地址解析器。配置方法如下:
@Bean
public HttpClientCustomizer httpClientResolverCustomizer() {
return httpClient -> httpClient.resolver(DefaultAddressResolverGroup.INSTANCE);
}
再次编译结束,双击运行正常,转发请求也正常。
内存对比
exe运行内存如下:
idea运行内存如下:
至此graalvm编译spring-cloud-gateway就算完成了。