Your First OpenResty Program: Hello World

API7.ai

September 9, 2022

OpenResty (NGINX + Lua)

When we start learning a new development language, tutorials provide a simple hello world case. So let's skip the installation first and see how to write this case in OpenResty:

$ resty -e "ngx.say('hello world')"
hello world

This should be the most straightforward hello world code you've ever seen, similar to Python:

$ python -c 'print("hello world")'
hello world

Behind this is a manifestation of OpenResty's philosophy. The code should be concise enough to let you get rid of the idea of "from entry to give up." This post will focus on the hello world case.

As we said in the What is the difference between OpenResty and NGINX post, OpenResty is based on NGINX. So you may have a question: Why can't we see the shadow of NGINX here? Don't worry, let's add a line of code to see what is running behind resty:

$ resty -e "ngx.say('hello world'); ngx.sleep(10)" &

We added a ngx.sleep method so that the program will not exit after printing out the string hello world. In this way, we have the opportunity to find out:

$ ps -ef | grep nginx
root     15944  6380  0 13:59 pts/6    00:00:00 grep --color=auto nginx

Finally the NGINX process appears! It seems that the resty command essentially starts an NGINX service, so what is the resty?

You may not have OpenResty installed on your machine, so next, let's go back to the installation steps we skipped at the beginning, and install OpenResty before continuing.

Installation

Like other open source software, we can install OpenResty in various ways, such as using the operating system's package manager, compiling from source code, or docker image. However, I recommend that you first use a package manager such as yum, apt-get, and brew to install OpenResty. In this post, I will use the macOS as an example:

$ brew tap openresty/brewbrew install openresty

Using other operating systems is similar. First, add the OpenResty repository's URL to the package manager, and then use the package manager to install OpenResty. For more detailed steps, you can refer to the official documentation.

However, there are two problems behind this seemingly simple installation:

  1. Why do I not recommend using source code to install it?
  2. Why can't it be installed directly from the official repository of the operating system but need to set another repository first?

Please think about those two questions first.

Here I would like to add one more thing. I will put many "why" behind the appearance in this course. I hope you can think while learning new things. It doesn't matter whether the result is correct or not. Unfortunately, independent thinking is also scarce in the technical field. Due to the difference in each person's technical field and depth, teachers will inevitably have personal opinions and mistakes in knowledge in any course. We can gradually form our own technical system by asking a few more whys in the learning process and understanding it.

Many engineers enjoy building from source codes, and so did I many years ago. However, when using an open source project, I always hope that I can manually configure and make from the source code and modify some compilation parameters. I feel this is the best way to suit the environment of this machine and maximize its performance.

But this is not the case in reality. Every time I compile the source code, I encounter weird environmental problems, and I can only install it after stumbling. Now I understand that our original purpose is to use open source projects to solve business needs. We should not waste time and environment fighting, not to mention package managers and container technology. It is precisely to help us solve these problems.

Let's go back to the topic. Using OpenResty source code to install, not only are the steps cumbersome, you need to solve external dependencies such as PCRE, OpenSSL, etc., but you also need to patch the corresponding version of OpenSSL manually. Otherwise, there will be a lack of functionality when dealing with SSL sessions. For example, Lua APIs such as ngx.sleep that cause yield cannot be used. If you want to learn more about this part, you can refer to the official documentation for more detailed information.

OpenResty is maintaining those patches in the OpenSSL package script by itself. When OpenResty upgrades the OpenSSL version, it must regenerate the corresponding patch and perform a complete regression testing.

Source0: https://www.openssl.org/source/openssl-%{version}.tar.gz

Patch0: https://raw.githubusercontent.com/openresty/openresty/master/patches/openssl-1.1.0d-sess_set_get_cb_yield.patch
Patch1: https://raw.githubusercontent.com/openresty/openresty/master/patches/openssl-1.1.0j-parallel_build_fix.patch

At the same time, we can take a look at the package script of OpenResty in CentOS to see if there are other hidden points:

BuildRequires: perl-File-Temp
BuildRequires: gcc, make, perl, systemtap-sdt-devel
BuildRequires: openresty-zlib-devel >= 1.2.11-3
BuildRequires: openresty-openssl-devel >= 1.1.0h-1
BuildRequires: openresty-pcre-devel >= 8.42-1
Requires: openresty-zlib >= 1.2.11-3
Requires: openresty-openssl >= 1.1.0h-1
Requires: openresty-pcre >= 8.42-1

As you can see here, OpenResty maintains its version of OpenSSL and its own version of zlib and PCRE. However, the latter two adjusted the compilation parameters and did not preserve their patches.

So, considering these factors, I do not recommend compiling OpenResty from the source codes unless you already know the details.

It should be clear to you why the source installation is not recommended. When we answered the first question, we also answered the second question: why can't we install directly from the official package repositories of the operating system but need to set another repository first?

This is because the official repositories are unwilling to accept OpenSSL, PCRE, and zlib packages maintained by third parties to prevent them from leading to confusion for other users who do not know which one to choose. On the other hand, OpenResty needs specified versions of OpenSSL and PCRE libraries to run normally, and the default versions of the system are relatively old.

OpenResty CLI

After installing OpenResty, the OpenResty CLI resty is already installed by default. It is a Perl script, and as we mentioned before, the OpenResty ecosystem tools are all written in Perl, which is determined by the technical preference of the OpenResty author.

$ which resty
/usr/local/bin/resty

$ head -n 1 /usr/local/bin/resty
#!/usr/bin/env perl

The resty CLI is very powerful, and we can use resty -h or read official documentation for a complete functionality list. Next, I'll introduce two exciting features.

$ resty --shdict='dogs 1m' -e 'local dict = ngx.shared.dogs dict:set("Tom", 56) print(dict:get("Tom"))'

56

The example above shows an Nginx configuration with the Lua code, which accomplishes a shared memory dictionary setup and querying. The dogs 1m is an Nginx configuration that declares a shared memory space named dogs with a size of 1m. The shared memory is used as a dictionary in the Lua code.

Also, the --http-include and --main-include parameters are used to set up the NGINX configuration file, so we could rewrite the above example as:

$ resty --http-conf 'lua_shared_dict dogs 1m;' -e 'local dict = ngx.shared.dogs dict:set("Tom", 56) print(dict:get("Tom"))'

The standard debugging tools in the OpenResty world, such as gdb, valgrind, sysetmtap, and Mozilla rr can also be used with resty to facilitate normal development and testing. They have corresponded to different commands of resty, so the internal implementation is straightforward, just an extra layer of command line calls. Let's take valgrind as an example.

$ resty --valgrind -e "ngx.say('hello world'); "

ERROR: failed to run command "valgrind /usr/local/openresty/nginx/sbin/nginx -p /tmp/resty_rJeOWaYGIY/ -c conf/nginx.conf": No such file or directory

They are not only applicable to the OpenResty world but are also general tools for the server side, so let's learn them step by step.

More formal "hello world"

The first OpenResty program we wrote, in the beginning, used resty command without the master process and without listening on specific ports. Next, let's implement another hello world program.

We need at least three steps to complete it.

  1. Create a working directory.
  2. Modify the NGINX configuration file to embed Lua code in it.
  3. Start the OpenResty service.

Let's start by creating a working directory.

$ mkdir openresty-sample
$ cd openresty-sample
$ mkdir logs/ conf/

The following is a minimalist nginx.conf with the OpenResty's content_by_lua directive in the root directory, which embeds the ngx.say method.

events {
    worker_connections 1024;
}

http {
    server {
        listen 8080;
        location / {
            content_by_lua '
                ngx.say("hello, world")
            ';
        }
    }
}

Please make sure that openresty has been added to the PATH environment first; then, start the OpenResty service:

$ openresty -p `pwd` -c conf/nginx.conf

If no errors occurred, the OpenResty service has been successfully started. We can access it by the cURL command to see the returned results.

$ curl -i 127.0.0.1:8080

HTTP/1.1 200 OK
Server: openresty/1.13.6.2
Content-Type: text/plain
Transfer-Encoding: chunked
Connection: keep-alive
hello, world

Congratulations! The formal Hello World in OpenResty is complete!

Summary

Let's review what we learned today. First, we start the OpenResty installation and CLI with a simple line of "hello, world" code, and finally, we start the OpenResty process and run an actual backend program.

Among them, resty is a command-line tool that we will frequently use in the future. Therefore, the demo code in the course is run with it, rather than starting the OpenResty service in the background.

More importantly, many cultural and technical details are hidden behind OpenResty, and it is like an iceberg floating on the sea. Through this course, I hope to show you a more comprehensive OpenResty, not just its exposed API.