helidon使用
Oracle在2018年9月推出了新的开源框架Project Helidon 。最初名为J4C(Java for Cloud),Helidon是用于创建基于微服务的应用程序的Java库的集合。 在引入Helidon 1.0的六个月内,它于2019年2月发布。当前的稳定版本是Helidon 1.4.4,但是Oracle计划在2020年Spring末发布Helidon 2.0的过程中进展良好。
本教程将介绍Helidon SE和Helidon MP,探索Helidon SE的三个核心组件,如何入门,并介绍在Helidon MP之上构建的电影应用程序。 还将讨论GraalVM,以及即将发布的Helidon 2.0会带来什么。
Helidon设计简单,快速,是独一无二的,因为它附带两种编程模型: Helidon SE和Helidon MP 。 在下图中,您可以看到Helidon SE和Helidon MP与其他流行的微服务框架保持一致的地方。
Helidon SE是一个微框架,具有创建微服务所需的三个核心组件-Web服务器,配置和安全性-用于构建基于微服务的应用程序。 它是一种小型的,功能性的API,具有React性,简单和透明的特点,不需要应用程序服务器。
让我们看一下有关使用WebServer
界面启动Helidon Web服务器的非常简单的示例的Helidon SE的功能样式:
WebServer.create(
Routing.builder()
.get("/greet", (req, res)
-> res.send("Hello World!"))
.build())
.start();
以该示例为起点,我们将逐步构建一个正式的startServer()
方法(该服务器应用程序的一部分供您下载),以探索三个Helidon SE核心组件。
受NodeJS和其他Java框架的启发,Helidon的Web服务器组件是运行在Netty之上的异步和响应式API。 WebServer
界面通过配置,路由,错误处理以及构建指标和运行状况端点来增强基本的服务器生命周期和监视。
让我们从startServer()
方法的第一个版本开始,在随机可用端口上启动Helidon Web服务器:
private static void startServer() {
Routing routing = Routing.builder()
.any((request, response) -> response.send("Greetings from the web server!" + "\n"))
.build();
WebServer webServer = WebServer
.create(routing)
.start()
.toCompletableFuture()
.get(10, TimeUnit.SECONDS);
System.out.println("INFO: Server started at: http://localhost:" + webServer.port() + "\n");
}
首先,我们需要构建一个Routing
接口的实例,该实例用作带有路由规则的HTTP请求-响应处理程序。 在此示例中,我们使用any()
方法将请求路由到定义的服务器响应“来自Web服务器的问候!”,该响应将显示在浏览器中或通过curl
命令显示。
在构建Web服务器时,我们调用旨在接受各种服务器配置的重载create()
方法。 如上所示,最简单的方法接受我们刚刚创建的实例变量routing
,以提供默认服务器配置。
Helidon Web服务器被设计为可响应的,这意味着start()
方法将返回,并且CompletionStage
接口的实例将启动该Web服务器。 这使我们可以调用toCompletableFuture()
方法。 由于未定义特定的服务器端口,因此服务器将在启动时找到随机可用的端口。
让我们使用Maven构建并运行我们的服务器应用程序:
$ mvn clean package
$ java -jar target/helidon-server.jar
服务器启动时,您应该在终端窗口中看到以下内容:
Apr 15, 2020 1:14:46 PM io.helidon.webserver.NettyWebServer
INFO: Version: 1.4.4
Apr 15, 2020 1:14:46 PM io.helidon.webserver.NettyWebServer lambda$start$8
INFO: Channel '@default' started: [id: 0xcba440a6, L:/0:0:0:0:0:0:0:0:52535]
INFO: Server started at: http://localhost:52535
如最后一行所示,Helidon Web服务器选择了端口52535。服务器运行时,请在浏览器中输入此URL,或在单独的终端窗口中使用以下curl
命令执行该URL:
$ curl -X GET http://localhost:52535
您应该看到“ 来自Web服务器的问候! ”
要关闭Web服务器,只需添加以下代码行:
webServer.shutdown()
.thenRun(() -> System.out.println("INFO: Server is shutting down...Good bye!"))
.toCompletableFuture();
配置组件加载并处理配置属性。 Helidon的Config
界面将从定义的属性文件中读取配置属性,通常但不限于以YAML格式。
让我们创建一个application.yaml
文件,它将为应用程序,服务器和安全性提供配置。
app:
greeting: "Greetings from the web server!"
server:
port: 8080
host: 0.0.0.0
security:
config:
require-encryption: false
providers:
- http-basic-auth:
realm: "helidon"
users:
- login: "ben"
password: "${CLEAR=password}"
roles: ["user", "admin"]
- login: "mike"
password: "${CLEAR=password}"
roles: ["user"]
- http-digest-auth:
此application.yaml
文件app
, server
和security
有三个主要部分或节点。 前两个节点很简单。 greeting
子节点定义了我们在上一个示例中进行硬编码的服务器响应。 port
子节点定义了Web服务器在启动时使用的端口8080。 但是,您应该已经注意到,利用YAML的映射序列来定义多个条目, security
节点要复杂一些。 用' -
'字符分隔,定义了两个安全提供程序http-basic-auth
和http-digest-auth
,以及两个用户ben
和mike
。 我们将在本教程的“ 安全性组件”部分中对此进行更详细的讨论。
还要注意,此配置允许使用明文密码,因为config.require-encryption
子节设置为false
。 您显然会在生产环境中将此值设置为true
,以便任何尝试传递明文密码的尝试都将引发异常。
现在我们有了一个可行的配置文件,让我们更新startServer()
方法以利用我们刚刚定义的配置。
private static void startServer() {
Config config = Config.create();
ServerConfiguration serverConfig = ServerConfiguration.create(config.get("server"));
Routing routing = Routing.builder()
.any((request, response) -> response.send(config.get("app.greeting").asString().get() + "\n"))
.build();
WebServer webServer = WebServer
.create(serverConfig, routing)
.start()
.toCompletableFuture()
.get(10, TimeUnit.SECONDS);
System.out.println("INFO: Server started at: http://localhost:" + webServer.port() + "\n");
}
首先,我们需要通过调用Config
接口的create()
方法来构建Config
接口的实例,以读取我们的配置文件。 Config
提供的get(String key)
方法从key
指定的配置文件中返回一个节点或特定的子节点。 例如, config.get("server")
将返回server
节点下的内容,而config.get("app.greeting")
将返回“ 来自Web服务器的问候! ”。
接下来,我们通过传入语句config.get("server")
来调用ServerConfiguration
的create()
方法,从而创建ServerConfiguration
实例,以提供不可变的Web服务器信息。
除了我们通过调用config.get("app.greeting").asString().get()
消除了对服务器响应的硬编码之外,实例变量routing
的构建与上一个示例相同。
除了我们使用不同版本的create()
方法来接受两个实例变量serverConfig
和routing
之外,该Web服务器的创建与前面的示例相同。
现在,我们可以使用相同的Maven和Java命令来构建和运行此版本的Web服务器应用程序。 执行相同的curl
命令:
$ curl -X GET http://localhost:8080
您应该看到“ 来自Web服务器的问候! ”
Helidon的安全组件提供身份验证,授权,审核和出站安全。 有许多已实现的安全提供程序支持在Helidon应用程序中使用:
您可以使用以下三种方法之一在Helidon应用程序中实现安全性:
我们将使用混合方法在应用程序中实现安全性,但是我们需要先做一些内部管理。
让我们回顾一下如何引用在配置文件的安全性节点下定义的用户。 考虑以下字符串:
security.providers.0.http-basic-auth.users.0.login
当解析器遇到字符串中的数字时,表明配置文件中存在一个或多个子节点。 在此示例中, providers
之后的0
将指示解析器移动到第一个提供者子节点http-basic-auth
。 在0
之后users
直接将解析器移动到包含第一子节点的用户login
, password
和roles
。 因此,当传递给config.get()
方法时,上述字符串将返回用户ben
的登录名,密码和角色信息。 同样,将使用以下字符串返回用户mike
的登录名,密码和角色信息:
security.providers.0.http-basic-auth.users.1.login
接下来,让我们为Web服务器应用程序AppUser
创建一个新类, AppUser
实现SecureUserStore.User
接口:
public class AppUser implements SecureUserStore.User {
private String login;
private char[] password;
private Collection roles;
public AppUser(String login, char[] password, Collection roles) {
this.login = login;
this.password = password;
this.roles = roles;
}
@Override
public String login() {
return login;
}
@Override
public boolean isPasswordValid(char[] chars) {
return false;
}
@Override
public Collection roles() {
return roles;
}
@Override
public Optional digestHa1(String realm, HttpDigest.Algorithm algorithm) {
return Optional.empty();
}
}
我们将使用此类来构建用户角色映射,即:
Map
为此,我们向Web服务器应用程序添加了一个新方法getUsers()
,该方法使用来自配置文件的http-basic-auth
子节中的配置来填充地图。
private static Map getUsers(Config config) {
Map users = new HashMap<>();
ConfigValue ben = config.get("security.providers.0.http-basic-auth.users.0.login").asString();
ConfigValue benPassword = config.get("security.providers.0.http-basic-auth.users.0.password").asString();
ConfigValue> benRoles = config.get("security.providers.0.http-basic-auth.users.0.roles").asNodeList();
ConfigValue mike = config.get("security.providers.0.http-basic-auth.users.1.login").asString();
ConfigValue mikePassword = config.get("security.providers.0.http-basic-auth.users.1.password").asString();
ConfigValue> mikeRoles = config.get("security.providers.0.http-basic-auth.users.1.roles").asNodeList();
users.put("admin", new AppUser(ben.get(), benPassword.get().toCharArray(), Arrays.asList("user", "admin")));
users.put("user", new AppUser(mike.get(), mikePassword.get().toCharArray(), Arrays.asList("user")));
return users;
}
现在,我们已经在Web服务器应用程序中内置了此新功能,让我们更新startServer()
方法,以通过Helidon的HTTP基本身份验证实现增加安全性:
private static void startServer() {
Config config = Config.create();
ServerConfiguration serverConfig = ServerConfiguration.create(config.get("server"));
Map users = getUsers(config);
displayAuthorizedUsers(users);
SecureUserStore store = user -> Optional.ofNullable(users.get(user));
HttpBasicAuthProvider provider = HttpBasicAuthProvider.builder()
.realm(config.get("security.providers.0.http-basic-auth.realm").asString().get())
.subjectType(SubjectType.USER)
.userStore(store)
.build();
Security security = Security.builder()
.config(config.get("security"))
.addAuthenticationProvider(provider)
.build();
WebSecurity webSecurity = WebSecurity.create(security)
.securityDefaults(WebSecurity.authenticate());
Routing routing = Routing.builder()
.register(webSecurity)
.get("/", (request, response) -> response.send(config.get("app.greeting").asString().get() + "\n"))
.get("/admin", (request, response) -> response.send("Greetings from the admin, " + users.get("admin").login() + "!\n"))
.get("/user", (request, response) -> response.send("Greetings from the user, " + users.get("user").login() + "!\n"))
.build();
WebServer webServer = WebServer
.create(serverConfig, routing)
.start()
.toCompletableFuture()
.get(10, TimeUnit.SECONDS);
System.out.println("INFO: Server started at: http://localhost:" + webServer.port() + "\n");
}
与前面的示例一样,我们将构建实例变量config
和serverConfig
。 然后,我们使用getUsers()
方法将角色映射到用户,用户。
使用Optional
表示null类型安全性时,可通过SecureUserStore
接口构建store
实例变量,如lambda表达式所示。 安全的用户存储用于HTTP基本身份验证和HTTP摘要身份验证。 请记住,即使不需要与SSL一起使用,HTTP基本身份验证也可能是不安全的,因为不需要密码。
现在,我们准备构建HTTPBasicAuthProvider ,
的实例,该实例是SecurityProvider
接口的实现类之一。 realm()
方法定义未经身份验证时发送到浏览器(或任何其他客户端)的安全领域名称。 由于我们在配置文件中定义了一个领域,因此将其传递到方法中。 subjectType()
方法定义安全提供程序将提取或传播的主体类型。 它接受两个SubjectType
枚举之一,即USER
或SERVICE
。 userStore()
方法接受我们为验证应用程序中的用户而构建的store
实例变量。
使用我们的provider
实例变量,我们现在可以构建Security
类的实例,该实例用于引导安全性并将其与其他框架集成。 我们使用config()
和addAuthenticationProvider()
方法来完成此任务。 请注意,可以通过将其他addAuthenticationProvider()
方法链接在一起来注册多个安全提供程序。 例如,假设我们定义了实例变量basicProvider
和digestProvider
,分别代表HttpBasicAuthProvider
和HttpDigestAuthProvider
类。 我们的security
实例变量可以如下构建:
Security security = Security.builder()
.config(config.get("security"))
.addAuthenticationProvider(basicProvider)
.addAuthenticationProvider(digestProvider)
.build();
WebSecurity
类实现了Service
接口,该接口封装了一组路由规则和相关逻辑。 实例变量webSecurity
是使用create()
方法构建的,方法是传入security
实例变量,然后将WebSecurity.authentic()
方法传入securityDefaults()
方法,以确保请求将通过身份验证过程。
我们在前面两个示例中构建的熟悉的实例变量routing
看起来现在大不相同了。 它注册了webSecurity
实例变量,并通过将get()
方法链接在一起get()
定义端点' /
',' /admin
'和' /user
'。 请注意, /admin
和/user
端点分别绑定到用户ben
和mike
。
终于,我们的网络服务器可以启动了! 在我们刚刚实施了所有机制之后,构建Web服务器看起来与前面的示例完全一样。
现在,我们可以使用相同的Maven和Java命令构建并运行此版本的Web服务器应用程序,并执行以下curl
命令:
$ curl -X GET http://localhost:8080/
将返回“ 来自Web服务器的问候! ”
$ curl -X GET http://localhost:8080/admin
将返回“ 来自管理员的问候,本! ”
$ curl -X GET http://localhost:8080/user
将返回“ 来自用户的问候,迈克! ”
您可以找到一个全面的服务器应用程序 ,该应用程序演示了与我们刚刚探讨的三个核心Helidon SE组件相关的startServer()
方法的所有三个版本。 您还可以找到更详尽的Helidon 安全示例 ,这些示例将向您展示如何实现其他一些安全提供程序。
Helidon MP建立在Helidon SE之上,是一个小型的声明式API,它是MicroProfile规范的实现,该规范针对微服务架构优化了企业Java,以构建基于微服务的应用程序。 MicroProfile计划于2016年由IBM,Red Hat,Payara和Tomitribe组成的合作组织成立,它指定了三个原始API-CDI( JSR 365 ),JSON-P( JSR 374 )和JAX-RS( JSR-370 )-用于创建微服务应用程序的API数量最少。 从那时起,MicroProfile已发展到12个核心API以及四个独立API,以支持响应流和GraphQL。 最新版本是2020年2月发布的MicroProfile 3.3。
Helidon MP当前支持MicroProfile 3.2。 对于Java EE / Jakarta EE开发人员来说,Helidon MP是一个绝佳的选择,因为它熟悉了使用注释的声明方法。 没有部署模型,也不需要其他Java EE打包。
让我们看一下有关启动Helidon Web服务器的非常简单的示例的Helidon MP的声明式样式,以及它如何与Helidon SE的功能样式进行比较。
public class GreetService {
@GET
@Path("/greet")
public String getMsg() {
return "Hello World!";
}
}
请注意,此样式与Helidon SE功能样式相比有所不同。
既然已经向您介绍了Helidon SE和Helidon MP,那么让我们看看它们如何结合在一起。 下图显示了Helidon的体系结构。 Helidon MP建立在Helidon SE之上,CDI扩展(将在下一部分中进行说明)扩展了Helidon MP的云原生功能。
Helidon附带了可移植的上下文和依赖项注入(CDI) 扩展 ,这些扩展支持各种数据源,事务和客户端的集成,以扩展Helidon MP应用程序的云原生功能。 提供了以下扩展:
Helidon提供了有关Helidon SE和Helidon MP的快速入门指南。 只需访问这些页面并按照说明进行操作即可。 例如,您只需在终端窗口中执行以下Maven命令即可快速构建Helidon SE应用程序:
$ mvn archetype:generate -DinteractiveMode=false \
-DarchetypeGroupId=io.helidon.archetypes \
-DarchetypeArtifactId=helidon-quickstart-se \
-DarchetypeVersion=1.4.4 \
-DgroupId=io.helidon.examples \
-DartifactId=helidon-quickstart-se \
-Dpackage=io.helidon.examples.quickstart.se
这将在文件夹helidon-quickstart-se
生成一个小的但正在运行的应用程序,其中包括该应用程序的测试和配置文件(application.yaml
),日志记录( logging.properties
),并使用GraalVM构建本机映像( native-image.properties
),使用Docker( Dockerfile
和Dockerfile.native
)对应用程序进行容器化,并使用Kubernetes( app.yaml
)进行编排。
同样,您可以快速构建Helidon MP应用程序:
$ mvn archetype:generate -DinteractiveMode=false \
-DarchetypeGroupId=io.helidon.archetypes \
-DarchetypeArtifactId=helidon-quickstart-mp \
-DarchetypeVersion=1.4.4 \
-DgroupId=io.helidon.examples \
-DartifactId=helidon-quickstart-mp \
-Dpackage=io.helidon.examples.quickstart.mp
这是构建更复杂的Helidon应用程序的一个很好的起点,我们将在下一部分中进行讨论。
使用生成的Helidon MP快速入门应用程序,添加了其他类-POJO,资源,存储库,自定义异常以及ExceptionMapper
的实现-以构建维护完整的Quentin Tarantino电影列表的电影应用程序 。 如下所示的HelidonApplication
类注册所需的类。
@ApplicationScoped
@ApplicationPath("/")
public class HelidonApplication extends Application {
@Override
public Set> getClasses() {
Set> set = new HashSet<>();
set.add(MovieResource.class);
set.add(MovieNotFoundExceptionMapper.class);
return Collections.unmodifiableSet(set);
}
}
您可以克隆GitHub 存储库以了解有关该应用程序的更多信息。
Helidon支持GraalVM (一个多语言虚拟机和平台),可将应用程序转换为本地可执行代码。 由Oracle Labs创建的GraalVM包括用Java编写的即时编译器Graal ,允许提前将Java应用程序编译成可执行映像的框架SubstrateVM和开源工具箱Truffle 。和用于构建语言解释器的API。 最新版本是20.1.0。
您可以使用GraalVM的native-image
实用程序将Helidon SE应用程序转换为本地可执行代码,该实用程序是使用GraalVM的gu
实用程序进行的单独安装:
$ gu install native-image
$ export
GRAALVM_HOME=/usr/local/bin/graalvm-ce-java11-20.1.0/Contents/Home
安装后,您可以返回到helidon-quickstart-se
目录并执行以下命令:
$ mvn package -Pnative-image
此操作将花费几分钟,但是一旦完成,您的应用程序将转换为本机代码。 可执行文件位于/target
目录中。
Helidon 2.0.0计划于2020年Spring末发布,此时开发人员可以使用Helidon 2.0.0.RC1 。 重要的新功能包括在Helidon MP应用程序上支持GraalVM,新的Web客户端和数据库客户端组件,新的CLI工具以及独立的MicroProfile React消息和React流操作程序 API的实现。
直到最近,由于在核心MicroProfile API CDI 2.0( JSR 365 )中使用了反射,只有Helidon SE应用程序才能够利用GraalVM。 但是,由于客户需求,Helidon 2.0.0将支持将Helidon MP应用程序转换为本地映像。 Oracle已为Java社区创建了该演示应用程序 ,以预览此新功能。
为了补充最初的三个核心Helidon SE API(Web服务器,配置和安全性),新的Web客户端 API完善了Helidon SE的功能。 通过构建WebClient
接口的实例,您可以处理与指定端点有关的HTTP请求和响应。 就像Web服务器API一样,Web客户端也可以通过配置文件进行配置。
您可以在即将发布的Helidon 2.0.0的GA版本中了解有关开发人员期望的更多详细信息 。
翻译自: https://www.infoq.com/articles/helidon-tutorial/?topicPageSponsorship=c1246725-b0a7-43a6-9ef9-68102c8d48e1
helidon使用