Skip to content
Permalink
Browse files

Virtual Host: File Descriptor Table (FDT).

The File Descriptor Table (FDT) aims to provide a mechanism to share
open file descriptors at Virtual Host level with the goal of to reduce the
number of open file descriptors and reduce the calls to open(2) and close(2).

The FDT is implemented in the following way:

 - For each worker thread, maintain a list matching the global Virtual Host
   list and for each entry create a Hash Table (HT) of 64 entries, and each
   HT entry maintain a sub-array of 8 chains.

 - When a request for a static file arrives, use the new Virtual Host open/close
   wrappers, from the given parsed and processed URI generate a hash value and
   try to see if it exists in the HT, if it don't exist, just open the file
   directly and register the data into the chain for a possible future reuse.

   If the hash exists in some HT/Chain, obtain the opened file descriptor and
   increment the readers counter.

 - When the Monkey core do not longer needs the file descriptor, instruct the
   Virtual Host 'close' wrapper to perform the cleanup. If the resource/hash
   exists in the HT/Chain, decrement the readers counter, if counter reach
   zero, just perform an explicit close(2) as nobody is using it, if readers
   is greater than zero just return as the file descriptor is in use.

   If the resource do not exists in the HT, do a direct close(2).

The good thing of this implementation is that exists at worker level, so there
is no race conditions and is lock-free. Also collisions are reduced to zero
using HashTable Chaining. The hashing algorithm in use MurmurHash 2.

This implementation reduce the overhead under high concurrency sharing the file
descriptor resources and making it more scalable.

Signed-off-by: Eduardo Silva <edsiper@gmail.com>
  • Loading branch information...
edsiper committed Jan 24, 2014
1 parent 6d666da commit 4efbc11bafeb56fbe2b4f0f6925671630ce84125
Showing with 325 additions and 6 deletions.
  1. +3 −0 src/include/mk_request.h
  2. +1 −1 src/include/mk_utils.h
  3. +28 −1 src/include/mk_vhost.h
  4. +5 −0 src/mk_cache.c
  5. +1 −1 src/mk_http.c
  6. +1 −1 src/mk_request.c
  7. +64 −1 src/mk_utils.c
  8. +222 −1 src/mk_vhost.c
@@ -236,6 +236,9 @@ struct session_request
struct file_info file_info;

/* Vhost */
int vhost_fdt_id;
unsigned int vhost_fdt_hash;

struct host *host_conf; /* root vhost config */
struct host_alias *host_alias; /* specific vhost matched */

@@ -86,12 +86,12 @@ void mk_print(int type, const char *format, ...) PRINTF_WARNINGS(2,3);
pthread_t mk_utils_worker_spawn(void (*func) (void *), void *arg);
int mk_utils_worker_rename(const char *title);
void mk_utils_stacktrace(void);
unsigned int mk_utils_gen_hash(const void *key, int len);

/* Thread key to hold a re-entrant buffer for strerror formatting */
#define MK_UTILS_ERROR_SIZE 128
pthread_key_t mk_utils_error_key;


/*
* Helpers to format and print out common errno errors, we use thread
* keys to hold a buffer per thread so strerror_r(2) can be used without
@@ -21,11 +21,11 @@

#include "mk_list.h"
#include "mk_config.h"
#include "mk_request.h"

#ifndef MK_VHOST_H
#define MK_VHOST_H


/* Custom error page */
struct error_page {
short int status;
@@ -62,9 +62,36 @@ struct host_alias
struct mk_list _head;
};


#define VHOST_FDT_HASHTABLE_SIZE 64
#define VHOST_FDT_HASHTABLE_CHAINS 8

struct vhost_fdt_hash_chain {
int fd;
int readers;
unsigned int hash;
};

struct vhost_fdt_hash_table {
int av_slots;
struct vhost_fdt_hash_chain chain[VHOST_FDT_HASHTABLE_CHAINS];
};

struct vhost_fdt_host {
struct host *host;
struct vhost_fdt_hash_table hash_table[VHOST_FDT_HASHTABLE_SIZE];
struct mk_list _head;
};

//pthread_key_t mk_vhost_fdt_key;
pthread_mutex_t mk_vhost_fdt_mutex;

struct host *mk_vhost_read(char *path);
int mk_vhost_get(mk_pointer host, struct host **vhost, struct host_alias **alias);
void mk_vhost_init(char *path);
int mk_vhost_fdt_worker_init();
int mk_vhost_open(struct session_request *sr);
int mk_vhost_close(struct session_request *sr);

#ifdef SAFE_FREE
void mk_vhost_free_all();
@@ -30,6 +30,7 @@
#include "mk_config.h"
#include "mk_macros.h"
#include "mk_utils.h"
#include "mk_vhost.h"

pthread_key_t mk_cache_iov_header;
pthread_key_t mk_cache_header_lm;
@@ -38,6 +39,7 @@ pthread_key_t mk_cache_header_ka;
pthread_key_t mk_cache_header_ka_max;
pthread_key_t mk_cache_utils_gmtime;
pthread_key_t mk_cache_utils_gmt_text;
pthread_key_t mk_utils_error_key;

/* This function is called when a thread is created */
void mk_cache_thread_init()
@@ -92,4 +94,7 @@ void mk_cache_thread_init()
/* Cache buffer for strerror_r(2) */
cache_error = mk_mem_malloc(MK_UTILS_ERROR_SIZE);
pthread_setspecific(mk_utils_error_key, (void *) cache_error);

/* Virtual hosts: initialize per thread-vhost data */
mk_vhost_fdt_worker_init();
}
@@ -559,7 +559,7 @@ int mk_http_init(struct client_session *cs, struct session_request *sr)

/* Open file */
if (mk_likely(sr->file_info.size > 0)) {
sr->fd_file = open(sr->real_path.data, sr->file_info.flags_read_only);
sr->fd_file = mk_vhost_open(sr);
if (sr->fd_file == -1) {
MK_TRACE("open() failed");
return mk_request_error(MK_CLIENT_FORBIDDEN, cs, sr);
@@ -102,7 +102,7 @@ static void mk_request_init(struct session_request *request)
static void mk_request_free(struct session_request *sr)
{
if (sr->fd_file > 0) {
close(sr->fd_file);
mk_vhost_close(sr);
}

if (sr->headers.location) {
@@ -362,7 +362,7 @@ void mk_utils_trace(const char *component, int color, const char *function,
color_function = ANSI_BOLD_MAGENTA;
color_fileline = ANSI_GREEN;
break;
case MK_TRACE_PLUGIN:
case MK_TRACE_PLUGIN:
color_component = ANSI_BOLD_GREEN;
color_function = ANSI_BLUE;
color_fileline = ANSI_GREEN;
@@ -662,3 +662,66 @@ void mk_utils_stacktrace(void)
}
}
#endif



/*
* This hash generation function is taken originally from Redis source code:
*
* https://github.com/antirez/redis/blob/unstable/src/dict.c#L109
*
* ----
* MurmurHash2, by Austin Appleby
* Note - This code makes a few assumptions about how your machine behaves -
* 1. We can read a 4-byte value from any address without crashing
* 2. sizeof(int) == 4
*
* And it has a few limitations -
*
* 1. It will not work incrementally.
* 2. It will not produce the same results on little-endian and big-endian
* machines.
*/
unsigned int mk_utils_gen_hash(const void *key, int len)
{
/* 'm' and 'r' are mixing constants generated offline.
They're not really 'magic', they just happen to work well. */
uint32_t seed = 5381;
const uint32_t m = 0x5bd1e995;
const int r = 24;

/* Initialize the hash to a 'random' value */
uint32_t h = seed ^ len;

/* Mix 4 bytes at a time into the hash */
const unsigned char *data = (const unsigned char *)key;

while(len >= 4) {
uint32_t k = *(uint32_t*) data;

k *= m;
k ^= k >> r;
k *= m;

h *= m;
h ^= k;

data += 4;
len -= 4;
}

/* Handle the last few bytes of the input array */
switch(len) {
case 3: h ^= data[2] << 16;
case 2: h ^= data[1] << 8;
case 1: h ^= data[0]; h *= m;
};

/* Do a few final mixes of the hash to ensure the last few
* bytes are well-incorporated. */
h ^= h >> 13;
h *= m;
h ^= h >> 15;

return (unsigned int) h;
}

0 comments on commit 4efbc11

Please sign in to comment.
You can’t perform that action at this time.