graalvm+spring-cloud-gateway打造又快又小的类nginx本地网关

前言

网关是微服务架构的入口,外网请求通过网关转发到独立的微服务。项目一般会经过多个环境的测试,最终发布到生产。一个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中写的接口测试文档,等发布到测试环境后,又要再写一遍。如果本地有一个网关,提供基本的转发功能,就能打通本地和测试环境的隔阂:一份接口测试文档,多个环境使用。

本地搭一个网关,方式有很多:

  1. idea起一个spring-cloud-gateway项目:优点是简单易操作,可定制,缺点启动速度慢,占用内存高;
  2. 下载一个nginx服务器,本地起一个nginx服务,用于转发请求:也比较简单,内存也不高,缺点要阅读文档,不能修改代码来自定义;
  3. 用graalvm把spring-cloud-gateway编译成本地exe:springcloud项目,学习成本低,转发逻辑可以自定义,按需修改;本地镜像,启动快,占用内存低。

实践环节

准备工作

  1. 安装graalvm以及native-image
  2. 安装Visual Studio 2022 Developer Command Prompt v17.4.3

创建spring-cloud-gateway项目

添加下面几个依赖项:

  1. GraalVM Native Support
  2. Spring Reactive Web
  3. Gateway
  4. 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项目(只包含一个接口和actuator端点),名叫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运行内存如下:
idea占用内存

至此graalvm编译spring-cloud-gateway就算完成了。

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 6
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值