今日の記事ではサーバサイドの話を書きたいと思います。
皆さんが、普段使ってるミドルウェアは、大体こんな感じですよね?
Nginx, MySQL 弊社Adwaysなら Perl, Ruby
そして、
Nginx + リバースプロキシ + 複数WEBサーバ
この構成は、ほぼWEB業界の定番になってます。
まぁ確かにNginxは早いのですが、動的配信ではどうしてもDB側がボトルネックになってしまい、rpsがなかなか上がりません。サービスの運用が続くとデータ量も増え、さらに悪化してしまいます。
そこでのよくある対処方法は、Slave DBを増やしたり、WEBサーバを増やしたり、FusionIOを買ったり、などなど... お金かかる方法ばっかりですよね 。
実はアプリ側をちょっと頑張れば、お金をかけないでチューニングする方法があるんです。
そんな、ボトルネックを全て排除した、純粋な動的サイトを作る方法を紹介したいと思います。
全体図はこんな感じです。
ざっくり説明すると、Nginxと同じHTTPサーバの機能を持ったWEBアプリを直接作っちゃいます。
データ配信は内部のメモリを直接アクセスするので超高速です。
ボトルネックを無くすために、Nginxを使用せず、DBも使いません。ただアプリとメモリとlibeventだけで頑張る方法です。
実はアプリ側をちょっと頑張れば、お金をかけないでチューニングする方法があるんです。
そんな、ボトルネックを全て排除した、純粋な動的サイトを作る方法を紹介したいと思います。
全体図はこんな感じです。
ざっくり説明すると、Nginxと同じHTTPサーバの機能を持ったWEBアプリを直接作っちゃいます。
データ配信は内部のメモリを直接アクセスするので超高速です。
ボトルネックを無くすために、Nginxを使用せず、DBも使いません。ただアプリとメモリとlibeventだけで頑張る方法です。
#include <stdio.h>#include <stdlib.h>#include <string.h>#include <event.h>#include <evhttp.h>#include <pthread.h>#define HOST "0.0.0.0"#define PORT 20000#define CACHE_SLEEP 3600pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;void *pull_cache(void *arg){while(1){※ここでキャッシュ更新sleep(CACHE_SLEEP);}return 0;}void cb(struct evhttp_request* req, void *arg){struct evbuffer *buf;buf = evbuffer_new();if(buf == NULL)return;evhttp_add_header(req->output_headers,"Content-Type","text/plain; charset=utf-8");evhttp_add_header(req->output_headers,"Pragma","no-cache");evhttp_add_header(req->output_headers,"Cache-Control","no-cache");evhttp_add_header(req->output_headers,"Expires","Thu, 01 Dec, 1994 16:00:00 GMT");evhttp_add_header(req->output_headers,"Connection","close");struct evkeyvalq keys;evhttp_parse_query(req->uri,&keys);const char *cmd = evhttp_find_header(&keys,"cmd");const char *page_size = evhttp_find_header(&keys,"p");pthread_mutex_lock(&mutex);if(cmd == NULL){evbuffer_add_printf(buf,"ng");}else if(strcmp(cmd,"ping")==0){evbuffer_add_printf(buf,"pong");}else if(strcmp(cmd,"hoge")==0){evbuffer_add_printf(buf,"fuga");}※ここでcmdのパターンを追加して処理するpthread_mutex_unlock(&mutex);evhttp_send_reply(req,HTTP_OK,"",buf);evhttp_clear_headers(&keys);evbuffer_free(buf);}void create_srv(){event_init();struct event_base *evbase = NULL;struct evhttp *evh = evhttp_new(evbase);if(evhttp_bind_socket(evh,HOST,PORT)!=0){printf("Error! Can't listen to " HOST ":%d, exit\n",PORT);exit(1);}else{evhttp_set_gencb(evh,cb,NULL);event_dispatch();}}int main(int argc, char * argv[]){pthread_t t0;pthread_create(&t0,NULL,pull_cache,NULL);create_srv();return 0;}