02 How to Write Your Hello World

02 How to Write Your Hello World #

Hello, I am Wen Ming. Starting today, we will begin our formal learning journey.

Whenever we start learning a new development language or platform, we always begin with the simplest hello world. OpenResty is no exception. Let’s skip the installation process for now and take a look at how the simplest OpenResty program is written and run:

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

This is probably the simplest form of a hello world code you have ever seen, similar to Python:

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

Behind this lies the philosophy of OpenResty, which is to write concise code that helps you dispel the thought of “from entry to abandonment.” Today, we will specifically focus on this line of code and discuss it.

In the previous section, we mentioned that OpenResty is based on NGINX. So, you might be wondering why we don’t see any trace of NGINX here. Don’t worry, let’s add one line of code to see what resty is actually running behind the scenes:

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

We added a line of code that sleeps for 10 seconds after the resty program prints the string and does not exit. This way, we have the opportunity to find out:

$ ps -ef | grep nginx
501 25468 25462   0  7:24 PM ttys000    0:00.01 /usr/local/Cellar/openresty/''1.13.6.2/nginx/sbin/nginx -p /tmp/resty_AfNwigQVOB/ -c conf/nginx.conf

Finally, we see the familiar NGINX process. It turns out that resty essentially starts an NGINX service. So, what kind of program is resty? I won’t reveal it just yet. We’ll talk about it later.

OpenResty may not be installed on your machine yet, so let’s go back to the skipped installation steps first and then continue.

Installation of OpenResty #

Like other open-source software, OpenResty can be installed in multiple ways, such as using the operating system’s package manager, compiling from source code, or using a Docker image. I recommend using package management systems like yum, apt-get, or brew to install OpenResty. Here’s an example for Mac:

brew tap openresty/brew
brew install openresty

Similar steps can be followed on other operating systems. You first need to add the OpenResty repository address to the package manager and then use the package management tool to install it. For specific steps, you can refer to the official documentation.

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

  • Why do I not recommend installing from source code?
  • Why can’t you install directly from the official repository of the operating system and instead need to set up another repository address?

You can think about these two questions yourself.

Here, let me add one thing. In this course, I will be asking a lot of “why” questions behind the surface. I hope you can think while learning, and it doesn’t matter whether the conclusions are correct. Independent thinking is also scarce in the field of technology. Due to different technical domains and depths among individuals, teachers in any course inevitably have personal opinions and gaps in knowledge. Only by asking more “why” questions during the learning process, integrating and comprehending, can you gradually form your own technical system.

Many engineers have a penchant for source code, and I was the same many years ago. When using an open-source project, I always wanted to manually configure and make from source code, and modify some compilation parameters, feeling that this would make it most suitable for the environment on this machine and maximize its performance.

But reality is not like that. Every time I compiled from source code, I would encounter various strange environment issues, stumbling through to finally install it. Now I understand that our initial goal is actually to solve business requirements using open-source projects. We should not waste time and fight with the environment. Moreover, package managers and container technologies are precisely designed to help us solve these problems.

To get back on track, let me tell you my opinion. Installing OpenResty from source code is not only cumbersome, requiring you to handle external dependencies like PCRE and OpenSSL by yourself, but you also need to manually apply patches to OpenSSL for the corresponding version. Otherwise, there will be missing functionality when handling SSL sessions, such as the inability to use Lua APIs that cause yield, like ngx.sleep. If you want to understand this part in depth, you can refer to the official documentation for more detailed information.

From the OpenSSL packaging script maintained by OpenResty itself, you can see these patches. And when OpenResty upgrades the OpenSSL version, it needs to regenerate the corresponding patches and perform 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, let’s take a look at the packaging script for OpenResty in CentOS to see if there are any 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

From here, we can see that OpenResty not only maintains its own version of OpenSSL but also maintains its own versions of zlib and PCRE. However, the latter two simply make adjustments to the compilation parameters and do not maintain their own patches.

Therefore, considering all these factors, I do not recommend that you compile OpenResty from source code unless you are already very clear about these details.

By now, you should already be clear about why I do not recommend installing from source code. In fact, when answering the first question, I also indirectly answered the second question: why can’t you install directly from the official repository of the operating system and instead need to set up another repository address?

This is because the official repositories are not willing to accept third-party-maintained packages of OpenSSL, PCRE, and zlib. This would confuse other users who do not know which version to choose. On the other hand, OpenResty needs specific versions of OpenSSL and PCRE libraries to function properly, while the versions that come with the system by default are relatively old.

OpenResty CLI #

After installing OpenResty, the OpenResty CLI called resty is already installed by default. resty is a Perl script with over 1000 lines. As we mentioned before, the peripheral tools of OpenResty are all written in Perl, which is determined by the technical preferences of the author of OpenResty.

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

resty is powerful. If you want to know the complete list of functions, you can check resty -h or the official documentation. Next, I will introduce two interesting functions.

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

First, let’s look at the first example. This example combines NGINX configuration and Lua code to set and query a shared memory dictionary. dogs 1m is a NGINX configuration that declares a shared memory area named “dogs” with a size of 1m. In the Lua code, the shared memory is used as a dictionary. There are also --http-include and --main-include to set the NGINX configuration file. Therefore, the above example can also be written as:

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

Common debugging tools in the OpenResty world, such as gdb, valgrind, systemtap, and Mozilla rr, can also be used with resty to facilitate your development and testing. They correspond to different instructions in resty, and their internal implementations are actually quite simple. They simply invoke command line tools. Let’s take valgrind as an example:

$ resty --valgrind  -e "ngx.say('hello world'); "
ERROR: failed to run command "valgrind /usr/local/Cellar/openresty/1.13.6.2/nginx/sbin/nginx -p /tmp/resty_hTFRsFBhVl/ -c conf/nginx.conf": No such file or directory

In the chapters on debugging, testing, and performance analysis, we will cover the use of these tools. They are not only applicable to the OpenResty world but are also commonly used tools for server-side development. Let’s learn step by step.

More formal hello world #

The first OpenResty program we wrote using resty did not have a master process and did not listen on a port. Now, let’s write a more formal hello world program.

Writing such an OpenResty program is not simple. You need at least three steps to complete it:

  • Create a working directory;
  • Modify the NGINX configuration file to embed the Lua code;
  • Start the OpenResty service.

Let’s start by creating the working directory.

mkdir geektime
cd geektime
mkdir logs/ conf/

Next is a simplified nginx.conf file. Add the OpenResty content_by_lua directive at the root directory, and embed the code for ngx.say inside it.

events {
    worker_connections 1024;
}

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

Please confirm whether you have added openresty to the PATH environment variable. Then, start the OpenResty service.

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

If there are no errors, the OpenResty service has been successfully started. You can open a browser or use the curl command to view the result.

$ 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! You have completed a real OpenResty program.

Summary #

Let’s review what we have discussed today. We started with a simple hello, world code and expanded to the installation and CLI of OpenResty. Finally, we started the OpenResty process and ran a real backend program.

In particular, resty is a command-line tool that we will frequently use later on. The demonstration code in the course is run using resty rather than starting the background OpenResty service.

More importantly, OpenResty hides a lot of cultural and technical details behind it, like an iceberg floating on the sea. I hope that through this course, I can show you a more comprehensive and three-dimensional view of OpenResty, not just the API it exposes externally.

Reflection #

Lastly, I’ll leave you with a homework question. Currently, we write Lua code in the NGINX configuration file. However, if the code becomes more extensive, its readability and maintainability will become compromised.

Do you have any methods to solve this problem? Feel free to leave a comment and share with me. You can also forward this article to your colleagues and friends.