写点什么

Helidon 项目教程:如何使用 Oracle 轻量级 Java 框架构建微服务

  • 2020-07-21
  • 本文字数:10025 字

    阅读完需:约 33 分钟

Helidon项目教程:如何使用Oracle轻量级Java框架构建微服务

本文要点:


  • Helidon 是由 Oracle 在 2018 年 9 月份推出的轻量级微服务框架。

  • Helidon 是一个创建微服务应用的 Java 库的集合。

  • 按照设计,Helidon 非常简单和快捷,它提供了两个版本:Helidon SE 和 Helidon MP。

  • Helidon 支持 GraalVM,能够将 Helidon SE 应用转换为原生可执行的代码。

  • 在本教程中,我们将会向你介绍 Helidon,探索 Helidon SE 和 Helidon MP,并且会下载本教程相关的 GitHub 仓库内容。

  • Helidon 1.4.4 是当前的稳定版本,不过 Helidon 2.0 计划在今年发布。


2018 年 9 月份,Oracle 推出了新的开源框架Helidon项目。Helidon 最初的名字叫做 J4C(Java for Cloud),它是一个创建基于微服务应用的 Java 库的集合。在推出六个月之后,Helidon 1.0 于 2019 年 2 月份发布。目前的稳定发布版本是 Helidon 1.4.4,但是 Oracle 正在计划发布 Helidon 2.0(2.0 版本已经在 6 月 25 日发布,参见发布声明变更记录——译者注)。


本教程将会介绍 Helidon SE 和 Helidon MP,探索 Helidon SE 的三个核心组件、如何起步并且还会介绍一个基于 Helidon MP 构建的电影应用。另外,我们还有关于 GraalVM 的讨论以及在即将发布的 Helidon 2.0 中都有哪些值得期待的功能。

Helidon 概览

按照设计,Helidon 非常简单和快捷,它很独特的一点在于它提供了两个编程模型 Helidon SE Helidon MP。在下图中,展示与其他流行的微服务框架对比,Helidon SE 和 Helidon MP 分别位于什么地方。


Helidon SE

Helidon SE 是一个微框架,它提供了创建微服务的三个核心组件来构建基于微服务的应用,即 Web 服务器、配置以及安全性。它是一个很小的函数式 API,具有反应式(reactive)、简单和透明的特点,不需要应用服务器。


我们通过一个简单的例子看一下函数式风格的 Helidon SE,这里使用WebServer接口启动了一个 Helidon Web 服务器:


WebServer.create(    Routing.builder()        .get("/greet", (req, res)             -> res.send("Hello World!"))        .build())    .start();

复制代码


以该例子作为起点,我们将会增量式地构建一个正式的startServer()方法,以此探索 Helidon SE 的三个核心组件,它是你所下载的服务器应用的一部分。

Web 服务器组件

受到 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()方法将请求路由至定义好的服务器响应”Greetings from the web server!“,这个响应信息会通过浏览器或者curl命令展示出来。


在构建 web 服务器的时候,我们调用了重载的create()方法。按照设计,该方法用来接受各种服务器配置。如上所示,最简单的方式就是接受我们刚刚创建的用来提供默认服务器配置的实例变量routing


按照设计,Helidon Web 服务器是反应式的,这意味着start()方法会返回一个CompletionStage<WebServer>接口的实例来启动 Web 服务器。它允许我们调用toCompletableFuture()方法。因为这里没有指定服务器端口,服务器在启动的时候会选择任意一个可用的端口。


接下来,我们使用 Maven 构建并运行我们的服务器应用:


$ mvn clean package$ java -jar target/helidon-server.jar
复制代码


服务器启动的时候,我们会在终端窗口看到如下所示的输出:


Apr 15, 2020 1:14:46 PM io.helidon.webserver.NettyWebServer &lt;init&gt;INFO: Version: 1.4.4Apr 15, 2020 1:14:46 PM io.helidon.webserver.NettyWebServer lambda$start$8INFO: 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命令:


$ curl -X GET http://localhost:52535


我们将会看到“Greetings from the web server!


要关闭 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文件中有三个主要的组成部分或者说是节点,即appserversecurity。前两个节点非常简单直接。greeting子节点定义了我们在上述样例中硬编码的服务器响应。port子节点定义了 web 服务器在启动的时候所使用的端点是 8080。但是,你应该也注意到了,security节点要复杂一些,它使用 YAML 的映射序列定义了多个条目。通过使用“-”字符分割,我们定义了两个安全 provider(即http-basic-authhttp-digest-auth)和两个用户(即benmike)。在本教程的安全组件章节,我们将会对其进行详细讨论。


另外,这个配置允许我们通过将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"); }
复制代码


首先,我们需要通过调用Configcreate()方法构建该接口的一个实例。Config提供的get(String key)方法能够返回配置文件中给定key所声明的节点或子节点。例如,config.get("server")将会返回server节点下的内容,config.get("app.greeting")将会返回“Greetings from the web server!”。


接下来,我们创建了ServerConfiguration实例并为其提供不可变的 web 服务器信息,这是通过调用其create()方法并传入 config.get("server")语句实现的。


实例变量routing的构造方式和之前的样例很相似,只不过我们消除了硬编码的服务器响应,将其替换为调用 config.get("app.greeting").asString().get()。


Web 服务器的创建过程和之前的样例类似,只不过我们使用了一个不同版本的create()方法,它接受两个实例变量serverConfigrouting


我们可以使用相同的 Maven 和 Java 命令来构建和运行这个版本的 Web 服务器应用。执行相同的curl命令:


$ curl -X GET http://localhost:8080


你应该会看到“Greetings from the web server!

安全组件

Helidon 的安全组件提供了认证、授权、审计和出站安全性功能。在 Helidon 应用中,支持使用大量的安全供应商实现:


  • HTTP Basic 认证

  • HTTP Digest 认证

  • HTTP 签名

  • 基于属性的访问控制(Attribute Based Access Control,ABAC)授权

  • JWT Provider

  • Header 断言

  • Google 登录认证

  • OpenID Connect

  • IDCS 角色映射(IDCS Role Mapping)


在 Helidon 应用中,我们可以采用如下三种方式的一种来实现安全性:


  • 手动提供配置的构建者模式

  • 提供配置文件的配置模式

  • 组合使用构建者模式和配置模式的混合模式


在样例应用中,我们将会采用混合方式,但是我们首先要做一些准备工作。


我们看一下如何引用在配置文件中 security 节点下所定义的用户。考虑如下的字符串:


security.providers.0.http-basic-auth.users.0.login


当解析器遇到字符串中的数字时,就意味着配置文件中有一个或多个子节点。在本例中,providers后面的0将会指导解析器转移至第一个 provider 子节点,即http-basic-authusers后面的0将会指导解析器转移至包含loginpasswordroles的第一个 user 子节点。因此,当传递到config.get()方法时,上述的字符串将会返回ben用户的 login、password 和 role 信息。与之类似,mike用户的 login、password 和 role 信息可以通过如下的字符串获取到:


security.providers.0.http-basic-auth.users.1.login


接下来,我们为 Web 服务器应用创建一个新的类AppUser,它实现了SecureUserStore.User接口:


public class AppUser implements SecureUserStore.User {
private String login; private char[] password; private Collection<String> roles;
public AppUser(String login, char[] password, Collection<String> 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<String> roles() { return roles; }
@Override public Optional<String> digestHa1(String realm, HttpDigest.Algorithm algorithm) { return Optional.empty(); } }
复制代码


我们将会使用这个类来构建角色和用户的 map,如下所示:


Map<String, AppUser> users = new HashMap<>();
复制代码


为了实现这一点,我们为 Web 服务器应用添加了一个新的方法getUsers(),它会使用配置文件中http-basic-auth子元素的配置来填充这个 map。


private static Map<String, AppUser> getUsers(Config config) {    Map<String, AppUser> users = new HashMap<>();
ConfigValue<String> ben = config.get("security.providers.0.http-basic-auth.users.0.login").asString(); ConfigValue<String> benPassword = config.get("security.providers.0.http-basic-auth.users.0.password").asString(); ConfigValue<List<Config>> benRoles = config.get("security.providers.0.http-basic-auth.users.0.roles").asNodeList();
ConfigValue<String> mike = config.get("security.providers.0.http-basic-auth.users.1.login").asString(); ConfigValue<String> mikePassword = config.get("security.providers.0.http-basic-auth.users.1.password").asString(); ConfigValue<List<Config>> 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 Basic 认证实现来为其添加安全性:


private static void startServer() {    Config config = Config.create();    ServerConfiguration serverConfig = ServerConfiguration.create(config.get("server"));
Map<String, AppUser> 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"); }
复制代码


和前面样例做法一样,我们构建了变量实例configserverConfig。随后,我们通过上述的getUsers()方法构建了角色和用户的 map。


这里利用了Optional的空类型安全性,store实例变量是通过SecureUserStore接口构建的,如 lambda 表达式所示。SecureUserStore 同时用于 HTTP Basic 认证和 HTTP Digest 认证。需要注意,HTTP Basic 可能是非安全的,即便使用 SSL 也是如此,因为密码并不是必需的。


我们现在已经准备好构建HTTPBasicAuthProvider实例了,它是SecurityProvider接口的一个实现类。realm()方法定义了在未认证的时候发送至浏览器(或其他客户端)的安全 realm 名。因为我们在配置文件中定义了一个 realm,所以我们将它传递到了该方法中。subjectType()方法定义了安全 provider 抽取或传播的 principal 类型。它会接受SubjectType枚举的两个值中的一个,也就是USERSERVICEuserStore()方法接受我们刚刚构建的store实例变量,用来在我们的应用中校验用户。


借助provider实例变量,我们现在就可以构建Security类的实例了,用来启动安全功能并将它与其他框架进行集成。我们使用config()addAuthenticationProvider()来完成这一点。需要注意的是,我们可以注册多个安全 provider,只需要通过addAuthenticationProvider()方法将它们链接在一起即可。例如,假设我们定义了实例变量basicProviderdigestProvider,它们分别代表HttpBasicAuthProviderHttpDigestAuthProvider类,那么我们的security实例变量可以按照如下的方式进行构建:


Security security = Security.builder()        .config(config.get("security"))        .addAuthenticationProvider(basicProvider)        .addAuthenticationProvider(digestProvider)        .build();
复制代码


WebSecurity类实现了Service接口,它封装了一组路由规则和相关逻辑。实例变量webSecurity是通过create()方法和securityDefaults()方法构建的,前者将security 实例变量传递了进去而后者则将WebSecurity.authentic()传递了进去,从而确保请求将会经过认证过程。


我们熟悉的实例变量routing并没有太大的差异,在前面两个样例中我们已经构建过它。它注册了webSecurity实例变量并定义了端点“/”、“/admin”和“/user”,这是通过get()方法将它们链接起来的。注意,/admin/user端点分别关联了benmike


最后,我们的 web 服务器就可以启动了!在实现了所有的零部件之后,构建 web 服务器就和之前的样例完全一样了。


现在,我们可以使用相同的 Maven 和 Java 命令构建和运行 web 服务器应用了,执行如下的curl命令:


$ curl -X GET [http://localhost:8080/](http://localhost:8080/)将会返回“Greetings from the web server!


$ curl -X GET [http://localhost:8080/admin](http://localhost:8080/admin)将会返回“Greetings from the admin, ben!


$ curl -X GET [http://localhost:8080/user](http://localhost:8080/user)将会返回“Greetings from the user, mike!


你可以看到阐述所有三个版本startServer()方法的综合服务器应用,它们关联了我们刚刚探讨的三个 Helidon SE 核心组件。同时,你也可以参考更广泛的安全样例,它们会为你展示如何实现其他的安全 provider。

Helidon MP

Helidon MP 构建在 Helidon SE 之上,是一个小型的、声明式风格的 API,它是MicroProfile规范的实现,MicroProfile是一个平台,致力于将企业级 Java 优化为微服务架构,适用于构建基于微服务的应用。MicroProfile 最初是由 IBM、Red Hat、Payara 和 Tomitribe 在 2006 年联合成立的,当时它定义了最初的三个 API,即 CDI (JSR 365)、JSON-P (JSR 374)和 JAX-RS (JSR-370),它们被认为是构建微服务应用所需的最小数量的 API。从那时开始,MicroProfile 已经发展到 12 个核心 API 以及支持反应式流和 GraphQL 的 4 个独立 API。MicroProfile 3.3 于 2020 年 2 月发布,是最新的版本。


Helidon MP 目前支持 MicroProfile 3.2。对于 Java EE/Jakarta EE 开发人员来说,Helidon MP 是一个非常好的可选方案,因为它使用注解实现了熟悉的声明式方式。它不需要特殊的部署模型,也不需要额外的 Java EE 打包。


我们看一下 Helidon MP 的声明式风格,这是一个启动 Helidon web 的简单样例,可以将其与 Helidon SE 的函数式风格进行一下对比。


public class GreetService {  @GET  @Path("/greet")  public String getMsg() {    return "Hello World!";    }  }
复制代码


请注意这种风格与 Helidon SE 函数式风格的差异。

Helidon 架构

既然已经介绍了 Helidon SE 和 Helidon MP,那么我们看一下它们是如何组合在一起的。Helidon 的架构如下图所示。Helidon MP 是构建在 Helidon SE 和 CDI 扩展之上的,如下文所述,CDI 扩展丰富了 Helidon MP 的云原生能力。


CDI Extensions

Helidon 提供了可迁移的上下文与依赖注入(Context and Dependency Injection,CDI)扩展,它支持与各种数据源、事务和客户端进行集成,扩展 Helidon MP 应用的云原生功能。目前它提供了如下的扩展:


Helidon 快速上手指南

Helidon 为Helidon SEHelidon 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 容器化应用(DockerfileDockerfile.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
复制代码


对于构建复杂的应用来讲,这是一个很好的起点,我们会在下一节讨论一个复杂的应用。

Movie 应用

基于所生成的 Helidon MP 快速上手应用,我们添加了一些额外的类完成了movie应用,新增加的类包括 POJO、资源、repository、自定义异常以及ExceptionMapper的实现,该应用会维护 Quentin Tarantino 电影的一个列表。HelidonApplication类如下所示,它会注册所需的类。


@ApplicationScoped@ApplicationPath("/")public class HelidonApplication extends Application {
@Override public Set<Class<?>> getClasses() { Set<Class<?>> set = new HashSet<>(); set.add(MovieResource.class); set.add(MovieNotFoundExceptionMapper.class); return Collections.unmodifiableSet(set); } }
复制代码


你可以 colne GitHub 仓库以了解关于该应用的详细信息。

GraalVM

Helidon 支持GraalVM,它是一个多语言的虚拟机和平台,能够将应用转换成原生可执行代码。GraalVM 是由Oracle Labs创建的,由GraalSubstrateVMTruffle组成,其中 Graal 是一个使用 Java 编写的即时编译器,SubstrateVM 是一个允许提前将 Java 应用编译为可执行镜像的框架,Truffle 则是一个用于构建语言解释器(interpreter)的开源工具集和 API。它最新的版本是 20.1.0。


我们可以通过 GraalVM 的native-image工具将 Helidon SE 应用转换成原生可执行代码,native-image需要通过 GraalVM 的gu工具单独进行安装:


$ gu install native-image$ exportGRAALVM_HOME=/usr/local/bin/graalvm-ce-java11-20.1.0/Contents/Home
复制代码


安装完成之后,我们就可以回到helidon-quickstart-se目录并执行如下的命令:


$ mvn package -Pnative-image


这个操作将会耗时几分钟,完成之后,我们的应用就转换成了原生代码。可执行文件位于/target目录下。

Helidon 2.0 的路线图

Helidon 2.0.0 计划在 2020 年发布(目前,该版本已经发布,读者可以参考该地址——译者注)。该版本重要的新特性包括为 Helidon MP 应用添加对 GraalVM 的支持、新的 Web Client 和DB Client组件、新的CLI工具以及独立 MicroProfile Reactive MessagingReactive Streams Operators API 的实现。


直到最近,由于用到了 CDI 2.0(JSR 365)的反射(这是 MicroProfile API 的一个核心 API),所以只有 Helidon SE 应用能够利用 GraalVM。但是,根据客户的需要,Helidon 2.0.0 将支持 Helidon MP 应用转换成原生镜像。Oracle 创建了一个示例应用为 Java 社区预览这个新特性。


为了补充原有的三个核心 Helidon SE API,即 Web 服务器、配置和安全性,新的Web Client API 完备了 Helidon SE 的特性集。通过构建WebClient接口的实例,我们能够处理对特定端点的 HTTP 请求和响应。和 Web Server API 一样,Web Client 也可以通过配置文件进行配置。


我们可以详细了解开发人员可以期待 Helidon 2.0.0 会带来哪些新功能。


作者简介:


Michael Redlich 是新泽西州克林顿市ExxonMobil研究与工程部门的高级研究技术人员(观点仅代表个人),在过去的 30 年里,他具有开发定制科学实验室和 web 应用的经验。他还曾经在 Ai-Logix, Inc.(现在是AudioCodes了)担任过技术支持工程师,为客户提供技术支持和开发电话应用程序。他的技术专长包括面向对象设计和分析、关系型数据库设计和开发、计算机安全、C/ C++、Java、Python 和其他编程/脚本语言。他最近的关注点包括MicroProfileJakarta EEHelidonMicronautMongoDB


原文链接:


Project Helidon Tutorial: Building Microservices with Oracle’s Lightweight Java Framework


2020-07-21 19:083002

评论

发布
暂无评论
发现更多内容

Hadoop HDFS 3.3.1分布式存储搭建

KunlunBase昆仑数据库

国产数据库

TGIP-CN 036 报名|预见 2022 · Apache Pulsar 技术进展与社区动态

Apache Pulsar

开源 架构 云原生 Apache Pulsar 社区

推荐 5 个 yyds 的开源 Python Web 框架

AlwaysBeta

Python django flask tornado Web

架构实战营第 4 期 -- 毕业总结

烈火干柴烛灭田边残月

架构实战营

2022年2月云主机性能评测报告

博睿数据

阿里巴巴在开源压测工具 JMeter 上的实践和优化

阿里巴巴云原生

Apache 阿里云 云原生 Jmeter

投稿开奖丨轻量应用服务器征文活动(2月)奖励公布

阿里云弹性计算

轻量征文

架构实战营 4 期第九模块作业

jialuooooo

架构实战营

架构实战营学习总结

李晓笛

架构实战营

Gartner发布服务器虚拟化市场指南 灵雀云作为唯一本土容器厂商入选

York

容器 云原生 数据中心

Flutter 设置应用主题色和字体

岛上码农

flutter ios 安卓 移动端 3月月更

毕业设计

cqyanbo

社区知识库|常见问答 FAQ 集合第 5 期:Broker、Topic、Pulsar 客户端及消息限制等相关问题

Apache Pulsar

开源 架构 云原生 Apache Pulsar 社区

架构实战营模块九作业

zhongwy

模块九作业

李晓笛

架构训练营

干货|性能提升密钥,由代码细节带来的极致体验

SphereEx

Apache 数据库 开源 ShardingSphere SphereEx

建设领先的AI原生云,百度智能云落地新一代高性能AI计算集群

百度开发者中心

VuePress 博客优化之增加 Vssue 评论功能

冴羽

JavaScript Vue 前端 vuepress vssues

视频质量评价VMAF,为何让人又喜又忧?

微帧Visionular

视频编码

Linux这5大磁盘分区工具,绝了!

Ethereal

如何限制Linux终端中tree命令递归文件列表的深度?

Ethereal

区块链发展趋势与思考

CECBC

架构实战营第 4 期 -- 模块九作业

烈火干柴烛灭田边残月

架构实战营

业内首家!百度智能云智慧金融业务通过ISO37301合规管理体系认证

百度开发者中心

开发者们看过来~填问卷参与抽奖,双重周边奖励仅限本周!

InfoQ写作社区官方

OpenHarmony 热门活动

如何基于制品元数据提升交付效率 | 阿里巴巴DevOps实践指南

阿里云云效

云计算 阿里云 云原生 软件开发 持续交付

什么是单臂路由器?如何配置单臂路由?

Ethereal

什么是广域网WAN?学WAN,看这篇文章就够了!

Ethereal

干货分享 | 推荐两款好用的企业文档管理软件

小炮

一文了解路由平台的 Cisco IOS 和 IOS XE 命名约定,看这篇就够了!

Ethereal

黄金VS比特币:谁更有吸引力?

CECBC

Helidon项目教程:如何使用Oracle轻量级Java框架构建微服务_软件工程_Michael Redlich_InfoQ精选文章