Funqy

Quarkus Funqy 是 Quarkus 的无服务器策略的一部分,旨在提供一个可移植的 Java API 来编写可部署到各种 FaaS 环境(如 AWS Lambda、Azure Functions、Google Cloud Functions、Knative 和 Knative Events(云事件))的函数。它还可以用作独立服务。

Quarkus Funqy is part of Quarkus’s serverless strategy and aims to provide a portable Java API to write functions deployable to various FaaS environments like AWS Lambda, Azure Functions, Google Cloud Functions, Knative, and Knative Events (Cloud Events). It is also usable as a standalone service.

由于 Funqy 是一个跨越多个不同云/功能提供商和协议的抽象,因此它必须是一个非常简单的 API,因此可能不具有在其他远程调用抽象中使用的所有功能。然而,一个好的副作用是 Funqy 进行了尽可能的优化,并且尽可能精简。这意味着由于 Funqy 在灵活性方面做出了小小的牺牲,因此可以获得开销很小或没有开销的框架。

Because Funqy is an abstraction that spans multiple different cloud/function providers and protocols it has to be a very simple API and thus, might not have all the features you are used to in other remoting abstractions. A nice side effect though is that Funqy is as optimized and as small as possible. This means that because Funqy sacrifices a little on flexibility, you’ll get a framework that has little to no overhead.

Funqy Basics

Funqy API 很简单。使用 `@Funq`为方法添加注解。此方法只能有一个可选输入参数,并且可以返回响应,也可以不返回响应。

The Funqy API is simple. Annotate a method with @Funq. This method may only have one optional input parameter and may or may not return a response.

import io.quarkus.funqy.Funq;

public class GreetingFunction {
    @Funq
    public String greet(String name) {
       return "Hello " + name;
    }
}

Java 类也可以用作输入和输出,并且必须遵循 Java bean 约定,并有一个默认构造函数。作为参数或返回类型声明的 Java 类型是指 Funqy 运行时所期望的类型。Funqy 在构建时执行类型内省以加快引导时间,因此派生类型在运行时将不会被 Funqy 编组层注意到。

Java classes can also be used as input and output and must follow the Java bean convention and have a default constructor. The Java type that is declared as the parameter or return type is the type that will be expected by the Funqy runtime. Funqy does type introspection at build time to speed up boot time, so any derived types will not be noticed by the Funqy marshalling layer at runtime.

以下展示如何将 POJO 用作输入和输出类型。

Here’s an example of using a POJO as input and output types.

public class GreetingFunction {
    public static class Friend {
        String name;

        public String getName() { return name; }
        public void setName(String name) { this.name = name; }
    }

    public static class Greeting {
        String msg;

        public Greeting() {}
        public Greeting(String msg) { this.msg = msg }

        public String getMessage() { return msg; }
        public void setMessage(String msg) { this.msg = msg; }
    }

    @Funq
    public Greeting greet(Friend friend) {
       return new Greeting("Hello " + friend.getName());
    }
}

Async Reactive Types

Funqy 支持 SmallRye Mutiny Uni 反应式类型作为返回类型。唯一要求是 Uni 必须填写泛型类型。

Funqy supports the SmallRye Mutiny Uni reactive type as a return type. The only requirement is that the Uni must fill out the generic type.

import io.quarkus.funqy.Funq;
import io.smallrye.mutiny.Uni;

public class GreetingFunction {

    @Funq
    public Uni<Greeting> reactiveGreeting(String name) {
       ...
    }
}

Function Names

函数名称默认为方法名,并区分大小写。如果您想通过不同的名称引用函数,请按如下方式对 @Funq 注解进行参数化:

The function name defaults to the method name and is case-sensitive. If you want your function referenced by a different name, parameterize the @Funq annotation as follows:

import io.quarkus.funqy.Funq;

public class GreetingFunction {

    @Funq("HelloWorld")
    public String greet(String name) {
       return "Hello " + name;
    }
}

Funqy DI

每个 Funqy Java 类都是一个 Quarkus Arc 组件,并通过 CDI 或 Spring DI 支持依赖项注入。Spring DI 要求在您的构建中包含 quarkus-spring-di 依赖项。

Each Funqy Java class is a Quarkus Arc component and supports dependency injection through CDI or Spring DI. Spring DI requires including the quarkus-spring-di dependency in your build.

Funqy 类的默认对象生命周期为 @Dependent

The default object lifecycle for a Funqy class is @Dependent.

import io.quarkus.funqy.Funq;

import jakarta.inject.Inject;
import jakarta.enterprise.context.ApplicationScoped;

@ApplicationScoped
public class GreetingFunction {

    @Inject
    GreetingService service;

    @Funq
    public Greeting greet(Friend friend) {
        Greeting greeting = new Greeting();
        greeting.setMessage(service.greet(friend.getName()));
        return greeting;
    }
}

Context injection

Funqy API 通常不允许您注入或使用特定于协议(例如 HTTP)或函数 API(例如 AWS Lambda)的抽象。不过规则也有例外,您也许能够注入与您所部署的环境相关的上下文信息。

The Funqy API will usually not allow you to inject or use abstractions that are specific to a protocol (i.e. HTTP) or function API (i.e. AWS Lambda). There are exceptions to the rule though, and you may be able to inject contextual information that is specific to the environment you are deploying in.

我们不建议注入特定于运行时的上下文信息。保持函数的可移植性。

We do not recommend injecting contextual information specific to a runtime. Keep your functions portable.

上下文信息通过 @Context 方式注入,可在函数参数或类字段上使用。一个很好的示例是我们的 FunqyKnative 云事件集成附带的 io.quarkus.funqy.knative.events.CloudEvent 接口:

Contextual information is injected via the @Context annotation which can be used on a function parameter or a class field. A good example is the io.quarkus.funqy.knative.events.CloudEvent interface that comes with our Funqy Knative Cloud Events integration:

import io.quarkus.funqy.Funq;
import io.quarkus.funqy.Context;
import io.quarkus.funqy.knative.events.CloudEvent;

public class GreetingFunction {

    @Funq
    public Greeting greet(Friend friend, @Context CloudEvent eventInfo) {
        System.out.println("Received greeting request from: " eventInfo.getSource());

        Greeting greeting = new Greeting();
        greeting.setMessage("Hello " + friend.getName()));
        return greeting;
    }
}

Should I Use Funqy?

在过去十年中,通过 HTTP 的 REST 已成为编写服务的一种非常常见的方法。虽然 Funqy 有一个 HTTP 绑定,但它并不能替代 REST。由于 Funqy 必须跨越各种协议和函数云平台,因此它非常极简且受限。例如,如果您使用 Funqy,您将失去将数据链接到函数输出的链接(认为是 URI)的能力。您还失去了利用类似 cache-control 和条件 GET 等酷炫 HTTP 特性的能力。许多开发者可以接受,因为许多人不会使用这些 REST/HTTP 特性或样式。您必须决定您属于哪种阵营。Quarkus 确实支持通过各种云/函数提供程序进行 REST 集成(通过 Jakarta REST、Spring MVC、Vert.x Web 和 Servlet),但使用这种方法也有一些缺点。例如,如果您想执行 HTTP with AWS Lambda,则要求您使用可能减缓部署和冷启动时间甚至增加成本的 AWS API Gateway。

REST over HTTP has become a very common way to write services over the past decade. While Funqy has an HTTP binding it is not a replacement for REST. Because Funqy has to work across a variety of protocols and function cloud platforms, it is very minimalistic and constrained. For example, if you use Funqy you lose the ability to link (think URIs) to the data your functions spit out. You also lose the ability to leverage cool HTTP features like cache-control and conditional GETs. Many developers will be ok with that as many won’t be using these REST/HTTP features or styles. You’ll have to make the decision on what camp you are in. Quarkus does support REST integration (through Jakarta REST, Spring MVC, Vert.x Web, and Servlet) with various cloud/function providers, but there are some disadvantages of using that approach as well. For example, if you want to do HTTP with AWS Lambda, this requires you to use the AWS API Gateway which may slow down deployment and cold start time or even cost you more.

Funqy 的目的是让您能够编写跨提供程序的函数,以便如果您当前的函数提供程序开始对他们的服务向您收取更多费用,则您可以将其取消。您可能不想使用 Funqy 的另一个原因是,如果您需要访问目标函数环境的特定 API。例如,开发人员通常希望访问 Lambda 上的 AWS Context。在这种情况下,我们会告诉他们可能最好改用 Quarkus AWS Lambda 集成。

The purpose of Funqy is to allow you to write cross-provider functions so that you can move off of your current function provider if, for instance, they start charging you a lot more for their service. Another reason you might not want to use Funqy is if you need access specific APIs of the target function environment. For example, developers often want access to the AWS Context on Lambda. In this case, we tell them they may be better off using the Quarkus AWS Lambda integration instead.