Getting Started with Varnish

Introduction to Varnish

Varnish is a high-performance, open-source reverse proxy server and HTTP accelerator. It adopts a new software architecture that closely integrates with modern hardware systems. Compared to traditional Squid, Varnish offers numerous advantages such as higher performance, faster speed, and easier management. Many large websites are beginning to try replacing Squid with Varnish, which has driven Varnish’s rapid development.

Norway’s largest online newspaper, Verdens Gang (vg.no), replaced 12 Squid servers with 3 Varnish servers and achieved better performance, making it Varnish’s most successful application case.

How Varnish File Caching Works

Varnish is similar to typical server software, consisting of a master process and a child process. The master process reads the storage configuration file, calls the appropriate storage type, and then creates or reads a cache file of the corresponding size. Next, the master initializes the structure that manages this storage space, then forks and monitors the child process. During the main thread initialization, the child process mmaps the entire previously opened storage file into memory. At this point, it creates and initializes free structures, attaching them to the storage management structure for future allocation. The child process allocates several threads for work, mainly including some management threads and many worker threads.

Then, the real work begins. One of Varnish’s threads responsible for accepting new HTTP connections starts waiting for users. If a new HTTP connection arrives, it is always responsible for accepting it, then wakes up a waiting thread and hands over the specific processing to it. The worker thread reads the URI of the HTTP request, looks up the existing object. If it’s a hit, it returns the object directly and replies to the user. If it’s a miss, it needs to fetch the requested content from the backend server, store it in the cache, and then reply.

The cache allocation process works like this: It creates a cache file of the corresponding size based on the size of the read object. For read/write convenience, the program adjusts the size of each object to the nearest multiple of the memory page size. Then, it searches the existing free storage structures, finds the most suitable free storage block, and allocates it. If the free block is not fully used, the remaining memory is formed into another free storage block and attached to the management structure. If the cache is full, the oldest object is released according to the LRU mechanism.

The cache release process works like this: A timeout thread checks the lifetime of all objects in the cache. If the initially set TTL (Time To Live) has expired without access, the object is deleted, and the corresponding structure and storage memory are released. Note that during release, it checks the free memory blocks before and after the released storage memory block. If the adjacent free memory blocks are contiguous with the released memory, they are merged into a larger block.

The entire file cache management does not consider the relationship between files and memory. Essentially, it treats all objects as if they are in memory. If the system runs low on memory, the system will automatically swap them to swap space, without requiring Varnish to control this.

Varnish Installation

Downloading the Varnish Installation Package

It is recommended to download the latest stable version (the latest Varnish version is now 3.0.2). Varnish provides source code installation packages and executable program installation packages. Choose whichever package suits your platform according to your habits.

Installing Varnish

Source Code Installation

First, install the pcre library. The pcre library is for regular expression compatibility. If not installed, when installing Varnish version 2.0 or above, you will be prompted that the pcre library cannot be found. The following is the pcre installation process, with code shown in Listing 1:

Listing 1. Pcre Library Installation Code

				     tar zxvf pcre.tar.gz      cd pcre/      ./configure --prefix=/usr/local/pcre/      Make && make install 

Install Varnish, with code shown in Listing 2:

Listing 2. Varnish Installation Code

				        tar xzvf varnish-3.0.2.tar.gz         cd varnish-3.0.2         export PKG_CONFIG_PATH =/usr/local/pcre/lib/pkgconfig         ./configure --prefix=/usr/local/varnish  	        make         make install 

Executable Package Installation

To install Varnish on a RedHat system environment, you need to install the following software: automake, autoconf, libtool, ncurses-devel, libxslt, groff, pcre-devel, pkgconfig. Then proceed with the Varnish installation. The installation code is shown in Listing 3:

Listing 3. Varnish Installation Code

				 rpm -i varnish-2.1.4-2.el5.x86_64.rpm 

Starting Varnish

Listing 4. Varnish Startup Code

				 varnishd -f /etc/varnish/default.vcl -s file,/var/varnish_cache,1G / -T 127.0.0.1:2000 -a 0.0.0.0:9082

The meanings of each parameter are as follows:

-f Specifies the location of the Varnish configuration file

-s Specifies the Varnish cache storage method. A common method is: “-s file,<dir_or_file>,<size>”.

-T address:port Sets the Varnish telnet management address and its port

-a address:port Indicates the Varnish HTTP listening address and its port

Varnish Configuration

Introduction to VCL

VCL (Varnish Configuration Language) is the Varnish configuration language used to define Varnish’s access policies. VCL syntax is relatively simple and somewhat similar to C and Perl. The main points are as follows:

  • Blocks are delimited by curly braces, statements end with a semicolon, and the ‘ # ’ symbol can be used to add comments.
  • VCL uses assignment operators like “=”, comparison operators like “==”, logical operators like “!,&&,!!” forms, and also supports regular expressions and ACL matching operations using “~”.
  • VCL does not have user-defined variables. You can set variable values on backend, request, or object using the set keyword. For example, set req.backend = director_employeeui;
  • For concatenating two strings, there are no operators between them. Code is shown in Listing 5:

Listing 5. String Concatenation Code

				 set req.http.X-hit = " hit" "it"; 

  • The /” character has no special meaning in VCL, which is slightly different from other languages.
  • VCL can use the set keyword to set any HTTP header, and can use the remove or unset keyword to remove HTTP headers.
  • VCL has if/else judgment statements, but no loop statements.

VCL Backend

Declare and initialize a backend object, code is shown in Listing 6

Listing 6. Backend Declaration Code

					 backend www { 	   .host = "www.example.com"; 	   .port = "9082"; 	 } 

Using backend objects, the code is shown in Listing 7.

Listing 7. Code for Using backend

					 if (req.http.host ~ "^(www.)?example.com$") { 	    set req.backend = www; 	 } 

VCL Backend Collections: director

VCL can aggregate multiple backends into a group called a director, which enhances performance and resilience. When one backend in the group fails, another healthy backend can be selected. VCL offers several types of directors, each using a different algorithm to choose a backend. The main ones are:

  • The random director

The random director selects a backend based on the assigned weight. The .retries parameter specifies the maximum number of attempts to find a backend, and the .weight parameter represents the weight.

  • The round-robin director

The round-robin director selects backends in a cyclical, sequential manner.

  • The client director

The client director selects a backend based on client.identity. You can set the value of client.identity to a session cookie to identify the backend.

backend probes

VCL can set up probes to check if a backend is healthy. The code for defining a backend probe is shown in Listing 8:

Listing 8. Code for Defining backend probes

					 backend www { 	  .host = "www.example.com"; 	  .port = "9082"; 	  .probe = { 	    .url = "/test.jpg";// Which URL Varnish should request	    .timeout = 1 s;// Timeout waiting period	    .interval = 5s// Interval between checks             .window = 5;// Maintain results from 5 sliding windows	    .threshold = 3;// Declare backend healthy if at least 3 windows are successful	  } 	 } 

ACL

ACL creates a client access control list. You can use ACL to control which clients can access and which are prohibited. The code for defining an ACL is shown in Listing 9:

Listing 9. ACL Definition Code

					 Acl local{ 	   "localhost"; 	   "192.0.2.0"/24; 	   !"192.0.2.23";// Exclude this IP 	 } 

VCL Built-in Functions

vcl_recv Function

Used to receive and process requests. It is called when a request arrives and is successfully received, determining how to handle the request by evaluating the request data—for example, how to respond, which response to use, or which backend server to use.

This function typically ends with one of the following keywords:

pass: Enters pass mode, handing control of the request to the vcl_pass function.

pipe: Enters pipe mode, handing control of the request to the vcl_pipe function.

lookup: Enters lookup mode, handing control to the lookup instruction, which searches for the requested object in the cache and then hands control to either the vcl_hit or vcl_miss function based on the result.

error code [reason]: Returns the “code” to the client and abandons processing the request. “code” is an error identifier, such as 200 or 405. “reason” is the error message.

vcl_pipe Function

This function is called when entering pipe mode, used to pass the request directly to the backend host. Unchanged content is returned to the client while the request and response remain unchanged, until the connection is closed.

This function typically ends with one of the following keywords:

error code [reason].

pipe.

vcl_pass Function

This function is called when entering pass mode, used to pass the request directly to the backend host. After the backend host responds with data, it sends the response data to the client without any caching, ensuring the latest content is returned for every request on the current connection.

This function typically ends with one of the following keywords:

error code [reason].

pass.

restart: Restarts the process, incrementing the restart count. If the restart count exceeds max_restarts, an error warning is issued.

vcl_hash

This function is called when you want to add data to the hash.

This function typically ends with the following keyword:

hash.

vcl_hit Function

After executing the lookup instruction, this function is automatically called if the requested content is found in the cache.

This function typically ends with one of the following keywords:

deliver: Sends the found content to the client and hands control to the vcl_deliver function.

error code [reason].

pass.

restart: Restarts the process, incrementing the restart count. If the restart count exceeds max_restarts, an error warning is issued.

vcl_miss Function

After executing the lookup instruction, this function is automatically called if the requested content is not found in the cache. This function can be used to determine whether content needs to be fetched from the backend server.

This function typically ends with one of the following keywords:

fetch: Fetches the requested content from the backend and hands control to the vcl_fetch function.

error code [reason].

pass.

vcl_fetch Function

This function is called after the backend host updates the cache and fetches the content. Subsequently, by evaluating the fetched content, it decides whether to put the content into the cache or return it directly to the client.

This function typically ends with one of the following keywords:

error code [reason].

pass.

deliver.

esi.

restart: Restarts the process, incrementing the restart count. If the restart count exceeds max_restarts, an error warning is issued.

vcl_deliver Function

This function is called before the content found in the cache is sent to the client.

This function typically ends with one of the following keywords:

error code [reason].

deliver.

restart: Restarts the process, incrementing the restart count. If the restart count exceeds max_restarts, an error warning is issued.

vcl_error

This function is called when an error occurs.

This function typically ends with one of the following keywords:

deliver.

restart.

VCL Processing Flow

The VCL processing flow is shown in Figure 1.

Figure 1. VCL Processing Flow
Figure 1. VCL Processing Flow

The process Varnish uses to handle an HTTP request is roughly divided into the following steps:

  • Receive State (vcl_recv). This is the entry state for request processing. Based on VCL rules, it determines whether the request should be passed (vcl_pass), piped (vcl_pipe), or enter lookup (local query).
  • Lookup State. After entering this state, data is searched for in the hash table. If found, it enters the hit (vcl_hit) state; otherwise, it enters the miss (vcl_miss) state.
  • Pass (vcl_pass) State. In this state, it proceeds directly to the backend request, i.e., enters the fetch (vcl_fetch) state.
  • Fetch (vcl_fetch) State. In the fetch state, it performs a backend fetch for the request, sends the request, obtains the data, and performs local storage according to settings.
  • Deliver (vcl_deliver) State. The obtained data is sent to the client, and then this request is completed.

VCL Built-in Public Variables

VCL’s built-in public variables can be used in different VCL functions. They are introduced below according to the different stages of use.

When a request arrives, the available public variables are shown in Table 1.

Table 1. Public Variables Available Upon Request Arrival

Public Variable Name Meaning
req.backend Specifies the corresponding backend host
server.ip Represents the server IP
client.ip Represents the client IP
req.quest Indicates the request type, e.g., GET, HEAD, etc.
req.url Specifies the request URL
req.proto Indicates the HTTP protocol version of the client request
req.http.header Represents the HTTP header information in the corresponding request
req.restarts Indicates the number of restarts; the default maximum is 4

When Varnish makes requests to the backend host, the available common variables are shown in Table 2.

Table 2. Common Variables Available When Requesting the Backend Host

Common Variable Name Meaning
beresp.request Specifies the request type, e.g., GET, HEAD, etc.
beresp.url Indicates the request URL
beresp.proto Indicates the HTTP protocol version of the client request
beresp.http.header Represents the HTTP header information in the corresponding request
beresp.ttl Indicates the cache lifetime, the cache retention time (in seconds)

After content is fetched from the cache or the backend host, the available common variables are shown in Table 3.

Table 3. Common Variables Available After Fetching Content from the Backend Host

Common Variable Name Meaning
obj.status The request status code of the returned content, e.g., 200, 302, 504, etc.
obj.cacheable Whether the returned content can be cached
obj.valid Whether it is a valid HTTP request
obj.response The request status message of the returned content
obj.proto The HTTP version of the returned content
obj.ttl The lifetime of the returned content, i.e., cache time, in seconds
obj.lastuse The time elapsed since the last request, in seconds

When responding to the client, the available common variables are shown in Table 4.

Table 4. Common Variables Available When Responding to the Client

Common Variable Name Meaning
resp.status The HTTP status code returned to the client
resp.proto The HTTP protocol version returned to the client
resp.http.header The HTTP header information returned to the client
resp.response The HTTP status header returned to the client

VCL Debugging

VCL is a configuration file language and cannot be single-step debugged like C/C++. When the VCL execution results differ from expectations, it is difficult to find where the logic error lies. In addition to code review, we can use inline C language and inspect the status in objects returned to the browser to find logic errors.

We can use inline C language to print corresponding information. For example, we can print information in relevant places to check whether the VCL flow is executing correctly. The inline C code for printing information is shown in Listing 10:

Listing 10. Code for Printing Information

				      C{ 	    #include<syslog.h>// First include the header file      }C       C{ 	    Syslog(LOG_INFO,"VCL run here in function xxx in line xxx");       }C 

After starting varnish, we can use the tail -f /var/log/messages command to view the corresponding print information in /var/log/messages. The viewed print information is shown in Figure 2:

Figure 2. Varnish Print Information
Figure 2. Varnish Print Information

We can also set the values of certain variables onto the object returned to the browser, and then view the variable value in the browser. The code for setting a variable value is shown in Listing 11:

Listing 11. Varnish Variable Setting Code

				 set beresp.http.X-Cookie-Debug-req-cookie = req.http.Cookie; 

View the value of beresp.http.X-Cookie-Debug-req-cookie in the Chrome browser, with the result shown in Figure 3:

Figure 3. Viewing Varnish Variable Values in the Browser
Figure 3. Viewing Varnish Variable Values in the Browser

Varnish Configuration File Code

See attachment

Summary

This article briefly introduced Varnish and provided a detailed explanation of its workflow, installation, and configuration. With this article, you can quickly master Varnish.

http://www.ibm.com/developerworks/cn/opensource/os-cn-varnish-intro/index.html

Leave a Comment

Your email address will not be published.