| --- |
| id: functions-quickstart |
| title: Get started with Pulsar Functions |
| sidebar_label: "Get started" |
| original_id: functions-quickstart |
| --- |
| |
| This tutorial walks you through running a [standalone](reference-terminology.md#standalone) Pulsar [cluster](reference-terminology.md#cluster) on your machine, and then running your first Pulsar Function using that cluster. The first Pulsar Function runs in local run mode (outside your Pulsar [cluster](reference-terminology.md#cluster)), while the second runs in cluster mode (inside your cluster). |
| |
| > In local run mode, Pulsar Functions communicate with Pulsar cluster, but run outside of the cluster. |
| |
| ## Prerequisites |
| |
| Install [Maven](https://maven.apache.org/download.cgi) on your machine. |
| |
| ## Run a standalone Pulsar cluster |
| |
| In order to run Pulsar Functions, you need to run a Pulsar cluster locally first. The easiest way is to run Pulsar in [standalone](reference-terminology.md#standalone) mode. Follow these steps to start up a standalone cluster. |
| |
| ```bash |
| |
| $ wget @pulsar:binary_release_url@ |
| $ tar xvfz apache-pulsar-@pulsar:version@-bin.tar.gz |
| $ cd apache-pulsar-@pulsar:version@ |
| $ bin/pulsar standalone \ |
| --advertised-address 127.0.0.1 |
| |
| ``` |
| |
| When running Pulsar in standalone mode, the `public` tenant and the `default` namespace are created automatically. The tenant and namespace are used throughout this tutorial. |
| |
| ## Run a Pulsar Function in local run mode |
| |
| You can start with a simple function that takes a string as input from a Pulsar topic, adds an exclamation point to the end of the string, and then publishes the new string to another Pulsar topic. The following is the code for the function. |
| |
| ```java |
| |
| package org.apache.pulsar.functions.api.examples; |
| |
| import java.util.function.Function; |
| |
| public class ExclamationFunction implements Function<String, String> { |
| @Override |
| public String apply(String input) { |
| return String.format("%s!", input); |
| } |
| } |
| |
| ``` |
| |
| A JAR file containing this function and several other functions (written in Java) is included with the binary distribution you have downloaded (in the `examples` folder). Run the function in local mode on your laptop but outside your Pulsar cluster with the following commands. |
| |
| ```bash |
| |
| $ bin/pulsar-admin functions localrun \ |
| --jar examples/api-examples.jar \ |
| --classname org.apache.pulsar.functions.api.examples.ExclamationFunction \ |
| --inputs persistent://public/default/exclamation-input \ |
| --output persistent://public/default/exclamation-output \ |
| --name exclamation |
| |
| ``` |
| |
| > #### Multiple input topics |
| > |
| > In the example above, a single topic is specified using the `--inputs` flag. You can also specify multiple input topics with a comma-separated list using the same flag. |
| > |
| |
| > ```bash |
| > |
| > --inputs topic1,topic2 |
| > |
| > |
| > ``` |
| |
| |
| You can open up another shell and use the [`pulsar-client`](reference-cli-tools.md#pulsar-client) tool to listen for messages on the output topic. |
| |
| ```bash |
| |
| $ bin/pulsar-client consume persistent://public/default/exclamation-output \ |
| --subscription-name my-subscription \ |
| --num-messages 0 |
| |
| ``` |
| |
| > Setting the `--num-messages` flag to `0` means that consumers listen on the topic indefinitely, rather than only accepting a certain number of messages. |
| |
| With a listener up and running, you can open up another shell and produce a message on the input topic that you specify. |
| |
| ```bash |
| |
| $ bin/pulsar-client produce persistent://public/default/exclamation-input \ |
| --num-produce 1 \ |
| --messages "Hello world" |
| |
| ``` |
| |
| When the message has been successfully processed by the exclamation function, you will see the following output. To shut down the function, press **Ctrl+C**. |
| |
| ``` |
| |
| ----- got message ----- |
| Hello world! |
| |
| ``` |
| |
| ### Process explanation |
| |
| * The `Hello world` message you publish to the input topic (`persistent://public/default/exclamation-input`) is passed to the exclamation function. |
| * The exclamation function processes the message (providing a result of `Hello world!`) and publishes the result to the output topic (`persistent://public/default/exclamation-output`). |
| * If the exclamation function *does not* run, Pulsar will durably store the message data published to the input topic in [Apache BookKeeper](https://bookkeeper.apache.org) until a consumer consumes and acknowledges the message. |
| |
| ## Run a Pulsar Function in cluster mode |
| |
| [Local run mode](#run-a-pulsar-function-in-local-run-mode) is useful for development and test. However, when you use Pulsar for real deployment, you run it in **cluster mode**. In cluster mode, Pulsar Functions run *inside* of your Pulsar cluster and are managed using the same [`pulsar-admin functions`](reference-pulsar-admin.md#functions) interface. |
| |
| The following command deploys the same exclamation function you run locally in your Pulsar cluster, rather than outside of it. |
| |
| ```bash |
| |
| $ bin/pulsar-admin functions create \ |
| --jar examples/api-examples.jar \ |
| --classname org.apache.pulsar.functions.api.examples.ExclamationFunction \ |
| --inputs persistent://public/default/exclamation-input \ |
| --output persistent://public/default/exclamation-output \ |
| --name exclamation |
| |
| ``` |
| |
| You will see `Created successfully` in the output. Check the list of functions running in your cluster. |
| |
| ```bash |
| |
| $ bin/pulsar-admin functions list \ |
| --tenant public \ |
| --namespace default |
| |
| ``` |
| |
| You will see the `exclamation` function. Check the status of your deployed function using the `getstatus` command. |
| |
| ```bash |
| |
| $ bin/pulsar-admin functions getstatus \ |
| --tenant public \ |
| --namespace default \ |
| --name exclamation |
| |
| ``` |
| |
| You will see the following JSON output. |
| |
| ```json |
| |
| { |
| "functionStatusList": [ |
| { |
| "running": true, |
| "instanceId": "0" |
| } |
| ] |
| } |
| |
| ``` |
| |
| As you can see, the instance is currently running, and an instance with the ID of `0` is running. With the `get` command, you can get other information about the function, for example, topics, tenant, namespace, and so on. |
| |
| ```bash |
| |
| $ bin/pulsar-admin functions get \ |
| --tenant public \ |
| --namespace default \ |
| --name exclamation |
| |
| ``` |
| |
| You will see the following JSON output. |
| |
| ```json |
| |
| { |
| "tenant": "public", |
| "namespace": "default", |
| "name": "exclamation", |
| "className": "org.apache.pulsar.functions.api.examples.ExclamationFunction", |
| "output": "persistent://public/default/exclamation-output", |
| "autoAck": true, |
| "inputs": [ |
| "persistent://public/default/exclamation-input" |
| ], |
| "parallelism": 1 |
| } |
| |
| ``` |
| |
| As you can see, only one instance of the function is running in your cluster. Update the parallel functions to `3` using the `update` command. |
| |
| ```bash |
| |
| $ bin/pulsar-admin functions update \ |
| --jar examples/api-examples.jar \ |
| --classname org.apache.pulsar.functions.api.examples.ExclamationFunction \ |
| --inputs persistent://public/default/exclamation-input \ |
| --output persistent://public/default/exclamation-output \ |
| --tenant public \ |
| --namespace default \ |
| --name exclamation \ |
| --parallelism 3 |
| |
| ``` |
| |
| You will see `Updated successfully` in the output. If you enter the `get` command, you see that the parallel functions are increased to `3`, meaning that three instances of the function are running in your cluster. |
| |
| ```json |
| |
| { |
| "tenant": "public", |
| "namespace": "default", |
| "name": "exclamation", |
| "className": "org.apache.pulsar.functions.api.examples.ExclamationFunction", |
| "output": "persistent://public/default/exclamation-output", |
| "autoAck": true, |
| "inputs": [ |
| "persistent://public/default/exclamation-input" |
| ], |
| "parallelism": 3 |
| } |
| |
| ``` |
| |
| Shut down the running function with the `delete` command. |
| |
| ```bash |
| |
| $ bin/pulsar-admin functions delete \ |
| --tenant public \ |
| --namespace default \ |
| --name exclamation |
| |
| ``` |
| |
| When you see `Deleted successfully` in the output, you've successfully run, updated, and shut down functions running in cluster mode. |
| |
| ## Write and run a new function |
| |
| In order to write and run [Python](functions-api.md#functions-for-python) functions, you need to install some dependencies. |
| |
| ```bash |
| |
| $ pip install pulsar-client |
| |
| ``` |
| |
| In the examples above, you run and manage pre-written Pulsar Functions and learn how they work. You can also write your own functions with Python API. In the following example, the function takes a string as input, reverses the string, and publishes the reversed string to the specified topic. |
| |
| First, create a new Python file. |
| |
| ```bash |
| |
| $ touch reverse.py |
| |
| ``` |
| |
| Add the following information in the Python file. |
| |
| ```python |
| |
| def process(input): |
| return input[::-1] |
| |
| ``` |
| |
| The `process` method defines the processing logic of Pulsar Functions. It uses Python slice magic to reverse each incoming string. You can deploy the function using the `create` command. |
| |
| ```bash |
| |
| $ bin/pulsar-admin functions create \ |
| --py reverse.py \ |
| --classname reverse \ |
| --inputs persistent://public/default/backwards \ |
| --output persistent://public/default/forwards \ |
| --tenant public \ |
| --namespace default \ |
| --name reverse |
| |
| ``` |
| |
| If you see `Created successfully`, the function is ready to accept incoming messages. Because the function is running in cluster mode, you can **trigger** the function using the [`trigger`](reference-pulsar-admin.md#trigger) command. This command sends a message that you specify to the function and returns the function output. The following is an example. |
| |
| ```bash |
| |
| $ bin/pulsar-admin functions trigger \ |
| --name reverse \ |
| --tenant public \ |
| --namespace default \ |
| --trigger-value "sdrawrof won si tub sdrawkcab saw gnirts sihT" |
| |
| ``` |
| |
| You will get the following output. |
| |
| ``` |
| |
| This string was backwards but is now forwards |
| |
| ``` |
| |
| You have created a new Pulsar Function, deployed it in your Pulsar standalone cluster in [cluster mode](#run-a-pulsar-function-in-cluster-mode), and triggered the Function. |
| |
| ## Write and run a Go function |
| Go function depends on `pulsar-client-go`. Make sure that you have built `pulsar-client-go` before using Go function. |
| |
| To write and run a Go function, complete the following steps. |
| |
| 1. Create a new Go file. |
| |
| ``` |
| |
| touch helloFunc.go |
| |
| ``` |
| |
| 2. Append a byte for messages from the input topic. |
| The following is a `helloFunc.go` example. Each message from the input topic is appended with a `110` byte, and then delivered to the output topic. |
| |
| ``` |
| |
| package main |
| |
| import ( |
| "context" |
| |
| "github.com/apache/pulsar/pulsar-function-go/pf" |
| ) |
| |
| func HandleResponse(ctx context.Context, in []byte) ([]byte, error) { |
| res := append(in, 110) |
| return res, nil |
| } |
| |
| func main() { |
| pf.Start(HandleResponse) |
| } |
| |
| ``` |
| |
| 3. Compile code. |
| |
| ``` |
| |
| go build -o examplepulsar helloFunc.go |
| |
| ``` |
| |
| 4. Run Go function. |
| |
| ``` |
| |
| $ bin/pulsar-admin functions create \ |
| --go examplepulsar \ |
| --inputs persistent://public/default/backwards \ |
| --output persistent://public/default/forwards \ |
| --tenant public \ |
| --namespace default \ |
| --name gofunc |
| |
| ``` |
| |
| If you see `Created successfully`, the function is ready to accept incoming messages. Start a producer and produce messages to the `backwards` input topic. Start a consumer and consume messages from the `forwards` output topic, you will see `110` is appended to all messages. |
| |
| The `--classname` parameter is not specified when running Go function, because there is no `Class` concept in Go, which is different from Java and Python. |
| |
| :::note |
| |
| When you use the `--go` command to specify an executable file, make sure you have executable permissions. |
| |
| ::: |
| |
| ## Package Python dependencies |
| |
| When you deploy Python functions in a cluster offline, you need to package the required dependencies in a ZIP file before deployment. |
| |
| ### Client requirements |
| |
| The following programs are required to be installed on the client machine. |
| |
| ``` |
| |
| pip \\ required for getting python dependencies |
| zip \\ for building zip archives |
| |
| ``` |
| |
| ### Python dependencies |
| |
| A file named **requirements.txt** is needed with required dependencies for the Python function. |
| |
| ``` |
| |
| sh==1.12.14 |
| |
| ``` |
| |
| Prepare the Pulsar Function in the **src** folder. |
| |
| Run the following command to gather Python dependencies in the **deps** folder. |
| |
| ``` |
| |
| pip download \ |
| --only-binary :all: \ |
| --platform manylinux1_x86_64 \ |
| --python-version 27 \ |
| --implementation cp \ |
| --abi cp27m -r requirements.txt -d deps |
| |
| ``` |
| |
| Sample output |
| |
| ``` |
| |
| Collecting sh==1.12.14 (from -r requirements.txt (line 1)) |
| Using cached https://files.pythonhosted.org/packages/4a/22/17b22ef5b049f12080f5815c41bf94de3c229217609e469001a8f80c1b3d/sh-1.12.14-py2.py3-none-any.whl |
| Saved ./deps/sh-1.12.14-py2.py3-none-any.whl |
| Successfully downloaded sh |
| |
| ``` |
| |
| :::note |
| |
| `pulsar-client` is not needed as a dependency as it has already installed in the worker node. |
| |
| ::: |
| |
| #### Package |
| Create a destination folder with the desired package name, for example, **exclamation**. Copy the **src** and **deps** folders into it, and compress the folder into a ZIP archive. |
| |
| Sample sequence |
| |
| ``` |
| |
| cp -R deps exclamation/ |
| cp -R src exclamation/ |
| |
| ls -la exclamation/ |
| total 7 |
| drwxr-xr-x 5 a.ahmed staff 160 Nov 6 17:51 . |
| drwxr-xr-x 12 a.ahmed staff 384 Nov 6 17:52 .. |
| drwxr-xr-x 3 a.ahmed staff 96 Nov 6 17:51 deps |
| drwxr-xr-x 3 a.ahmed staff 96 Nov 6 17:51 src |
| |
| zip -r exclamation.zip exclamation |
| |
| ``` |
| |
| After package all the required dependencies into the **exclamation.zip** file, you can deploy functions in a Pulsar worker. The Pulsar worker does not need internet connectivity to download packages, because they are all included in the ZIP file. |