如何用Helidon创建一个Java REST API

302 阅读4分钟

在本教程中,我将向你展示如何用Helidon创建一个安全的REST API和本地图像。你将看到如何运行一个安全的、受OAuth 2.0保护的、允许JWT认证的Java REST API。然后,我将比较它与Micronaut、Quarkus和Spring Boot的性能。

本教程也可作为截屏提供。

先决条件。

一些步骤后面的括号表示我在视频中使用的IntelliJ Live模板。你可以在mraible/idea-live-templates找到模板的定义。

目录

用GraalVM安装JDK

使用SDKMAN用GraalVM安装Java 17

sdk install java 22.1.0.r17-grl

生成一个OAuth 2.0访问令牌

  1. 安装Okta CLI并运行okta register ,以注册一个新账户。如果你已经有一个账户,运行okta login

  2. 运行okta apps create spa 。设置oidcdebugger 作为应用程序名称,然后按回车键

  3. 在重定向URI中使用https://oidcdebugger.com/debug ,并将注销重定向URI设置为https://oidcdebugger.com

  4. 导航到OpenID Connect Debugger网站

    1. 填入你的客户ID

    2. 在授权URI中使用/oauth2/default/v1/authorize

    3. 为响应类型选择代码使用PKCE

    4. 点击发送请求,继续

  5. 在终端窗口中将访问令牌设置为TOKEN 环境变量。

    TOKEN=eyJraWQiOiJYa2pXdjMzTDRBYU1ZSzNGM...
    

用Helidon建立一个Java REST API

  1. 创建一个支持OAuth 2.0的Helidon应用。

    mvn -U archetype:generate -DinteractiveMode=false \
       -DarchetypeGroupId=io.helidon.archetypes \
       -DarchetypeArtifactId=helidon-quickstart-mp \
       -DarchetypeVersion=2.5.0 \
       -DgroupId=com.okta.rest \
       -DartifactId=helidon \
       -Dpackage=com.okta.rest
    
    你也可以安装Helidon的CLI并运行helidon init
  2. pom.xml 中添加MicroProfile JWT支持。

    <dependency>
        <groupId>io.helidon.microprofile.jwt</groupId>
        <artifactId>helidon-microprofile-jwt-auth</artifactId>
    </dependency>
    
  3. 添加一个HelloResource 类,返回用户的信息。[h-hello]

    package com.okta.rest.controller;
    
    import io.helidon.security.Principal;
    import io.helidon.security.SecurityContext;
    
    import javax.ws.rs.GET;
    import javax.ws.rs.Path;
    import javax.ws.rs.Produces;
    import javax.ws.rs.core.Context;
    import java.util.Optional;
    
    import static javax.ws.rs.core.MediaType.TEXT_PLAIN;
    
    @Path("/hello")
    public class HelloResource {
    
        @GET
        @Produces(TEXT_PLAIN)
        public String hello(@Context SecurityContext context) {
            Optional<Principal> userPrincipal = context.userPrincipal();
            return "Hello, " + userPrincipal.get().getName() + "!";
        }
    }
    
  4. src/main/java/com/okta/rest 中添加一个HelloApplication 类来注册你的资源并配置JWT认证。[h-app]

    package com.okta.rest;
    
    import com.okta.rest.controller.HelloResource;
    import org.eclipse.microprofile.auth.LoginConfig;
    
    import javax.enterprise.context.ApplicationScoped;
    import javax.ws.rs.core.Application;
    import java.util.Set;
    
    @LoginConfig(authMethod = "MP-JWT")
    @ApplicationScoped
    public class HelloApplication extends Application {
    
        @Override
        public Set<Class<?>> getClasses() {
            return Set.of(HelloResource.class);
        }
    }
    
  5. 添加你的Okta端点到src/main/resources/META-INF/microprofile-config.properties

    mp.jwt.verify.publickey.location=/oauth2/default/v1/keys
    mp.jwt.verify.issuer=/oauth2/default
    

使用HTTPie运行和测试你的Helidon REST API

  1. 从你的IDE或使用终端启动你的应用程序。

    mvn package && java -jar ./target/helidon.jar
    
  2. 用访问令牌测试你的API。

    http :8080/hello Authorization:"Bearer $TOKEN"
    

用GraalVM构建一个本地Helidon应用程序

  1. 使用native-image profile将你的Helidon应用编译成一个本地可执行文件。

    mvn package -Pnative-image
    
  2. 启动你的Helidon应用。

    ./target/helidon
    
  3. 使用访问令牌测试你的API。

    http :8080/hello Authorization:"Bearer $TOKEN"
    

启动时间比较

在记录数字之前,我通过运行每个图像三次来比较各框架的启动时间。然后,我又将每个应用程序运行了五次,并对结果进行了平均。我在一台2019年的MacBook Pro上收集了这些数字,它带有固态硬盘、2.4 GHz的8核英特尔酷睿i9处理器和64GB的内存。

google.charts.load('current', {packages: ['corechart', 'bar']}); google.charts.setOnLoadCallback(drawChart); function drawChart() { var data = google.visualization.arrayToDataTable([ [ 'Framework', 'Milliseconds to start', { role: 'style' }], [ 'Quarkus', 19.2, 'red'], ['Micronaut', 27.8, 'blue'], ['Helidon', 42.4, 'orange'], ['Spring Boot', 58.6, 'green'] ]; var options = { title: '本地Java框架的启动时间', chartArea:{width: '50%'}, hAxis: { title: 'Milliseconds', minValue: 0 }, vAxis: { title: 'Java Framework' } }; var chart = new google.visualization.BarChart(document.getElementById('startup-times') ); chart.draw(data, options); }

使用的版本。Quarkus 2.9.0,Micronaut 3.4.3,Helidon 2.5.0,以及Spring Boot 2.6.7与Spring Native 0.11.5。

内存使用情况比较

我使用下面的命令测试了每个应用程序的内存使用情况(以兆字节为单位)。我在启动应用程序后立即运行了它,在一个认证请求后,以及在五个认证请求后。

ps -o pid,rss,command | grep --color <executable> | awk '{$2=int($2/1024)"M";}{ print;}'

下面的图表显示了五个请求后的内存使用情况。

google.charts.load('current', {packages: ['corechart', 'bar']}); google.charts.setOnLoadCallback(drawChart); function drawChart() { var data = google.visualization.arrayToDataTable([ ['Framework', 'Memory usage (MB)', { role: 'style' }], ['Quarkus', 36, 'red'], ['Micronaut', 56, 'blue'], ['Spring Boot', 62, 'green'], ['Helidon', 62, 'orange'], ] ) ; var options = { title: '本地Java框架的内存使用', chartArea:{width: '50%'}, hAxis: { title: 'Megabytes', minValue: 0 }, vAxis: { title: 'Java Framework' } }; var chart = new google.visualization.BarChart(document.getElementById('memory-usage') ) ; chart.draw(data, options); }

MacBook Pro M1 Max的情况如何?

我的MacBook Pro(16英寸,2021年)采用苹果M1 Max,构建速度快得多,应用程序启动速度快2倍,但它们使用的内存更多。

google.charts.load('current', {packages: ['corechart', 'bar']}); google.charts.setOnLoadCallback(drawChart); function drawChart() { var data = google.visualization.arrayToDataTable([ ['Framework', 'Milliseconds to start', { role: 'style' }], ['Quarkus', 12, 'red'], ['Micronaut', 17, 'blue'], ['Helidon', 23, 'orange'], ['Spring Boot', 36, 'green'] ] ) ; var options = { title: '苹果硅上Java REST框架的启动时间' , chartArea:{width: '50%'}, hAxis: { title: 'Milliseconds', minValue: 0 }, vAxis: { title: 'Java Framework' } }; var chart = new google.visualization.BarChart(document.getElementById('startup-times-m1')); chart.raw(data, options); }

google.charts.load('current', {packages: ['corechart', 'bar']}); google.charts.setOnLoadCallback(drawChart); function drawChart() { var data = google.visualization.arrayToDataTable([ ['Framework', 'Memory usage (MB)', { role: 'style' }], ['Quarkus', 47, 'red'], ['Micronaut', 68, 'blue'], ['Spring Boot', 75, 'green'], ['Helidon', 84, 'orange'], ] ; var options = { title: '苹果硅上Java REST框架的内存使用', chartArea:{width: '50%'}, hAxis: { title: 'Megabytes', minValue: 0 }, vAxis: { title: 'Java Framework' } }; var chart = new google.visualization.BarChart(document.getElementById('memory-use-m1')); chart.raw(data, options); }