2015/12/01


この記事は Go Advent Calendar 2015 の記事ではありません。

ccat は cat やソースに色が付けられて非常に便利なので結構使っていた。

ccat

ふと「これ、ブログ書く時なんかに HTML として出力される機能があたら便利じゃね?」と思ったので ccat に html 出力機能を書いて pull-request を送った。

Htmlize by mattn - Pull Request #35 - jingweno/ccat - GitHub
https://github.com/jingweno/ccat/pull/35

マージされ、新しいバージョンがリリースされたので試したい人はダウンロードするか自分でビルドするといいと思います。ちなみにこんなソースが出力されます。

Posted at by



2015/11/27


ちょっとしたツールを作る際にシングルヘッダのライブラリは非常に役立ちます。json を STL ライクに扱える picojson 等が有名ですね。

今日は GitHub で mmx というシングルヘッダライブラリを見つけました。

vurtun/mmx - GitHub

single header libraries for C/C++

https://github.com/vurtun/mmx

同梱されているライブラリは、JSON パーサ、C言語っぽい字句解析器、マルチスレッドスケジューラ、行列演算、ライトウェイトなウェブサーバです。

今日はこれを使ってファイルをサーブする簡単なウェブサーバを書いてみました。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>

#define MMW_STATIC
#define MMW_IMPLEMENTATION
#define MMW_USE_FIXED_TYPES
#define MMW_USE_ASSERT
#include "mm_web.h"

#ifdef _WIN32
#include <winsock2.h>
#define SERVER_INIT { \
  WORD wsa_version = MAKEWORD(2,2); \
  WSADATA wsa_data; \
  if (WSAStartup(wsa_version, &wsa_data)) { \
    fprintf(stderr"WSAStartup failed\n"); \
    return 1; \
  } \
}
#define SERVER_FINISH \
  WSACleanup();
#else
#define SERVER_INIT
#define SERVER_FINISH
#endif

#ifdef __APPLE__
#include <unistd.h>
#endif

static void
logger(const char* text) {
  printf("[debug] %s\n", text);
}

static int
dispatch(struct mmw_con *conn, void *userdata) {
  FILE* fp;
  char buf[4096];
  struct stat fi;

  strcpy(buf, "./public");
  strcat(buf, conn->request.uri);
  if (buf[strlen(buf)-1] == '/') strcat(buf, "index.html");
  if (stat(buf, &fi)) return 1;
  fp = fopen(buf, "r");
  if (!fp) return 1;
  mmw_response_begin(conn, 200, (int) fi.st_size, NULL0);
  while (!feof(fp)) {
    int n = fread(buf, 1sizeof buf, fp);
    mmw_write(conn, buf, n);
  }
  mmw_response_end(conn);
  fclose(fp);
  return 0;
}

int
main(void) {
  void *memory = NULL;
  mmw_size needed_memory = 0;
  struct mmw_server server;

  struct mmw_config config;
  memset(&config, 0sizeof config);
  config.address = "127.0.0.1";
  config.port = 8888;
  config.connection_max = 4;
  config.request_buffer_size = 2048;
  config.io_buffer_size = 8192;
  config.log = logger;
  config.dispatch = dispatch;

  SERVER_INIT;

  mmw_server_init(&server, &config, &needed_memory);
  memory = calloc(needed_memory, 1);
  mmw_server_start(&server, memory);
  while (1) mmw_server_update(&server);
  mmw_server_stop(&server);
  free(memory);

  SERVER_FINISH;

  return 0;
}

お決まりの手順がならびますが、ライトウェイトとは言えどある程度カスタマイズが可能なウェブサーバが作れそうです。パフォーマンスを計測してみようと思ったのですが、どこかで刺さって完走できませんでした。ただ落ちるといった事はありませんし、ブラウザで閲覧する限りにはちゃんと動作している様です。

ちょろっとファイルサーバを書きたい、といったニーズには便利そうです。

ライセンスは mm_json.h、mm_lexer.h、mm_sched.h、mm_vec.h が zlib ライセンス、mm_web.h が BSD ライセンスになります。

Posted at by



2015/11/20


ミドルウェアとして Use するだけでパフォーマンスが向上するなんて夢の様な話はないと思っていたけど、HTTP Coala はそれをやってのけている様です。

goware/httpcoala · GitHub

Go http middleware handler for request coalescing

https://github.com/goware/httpcoala

ちょっと信じがたかったので、まずはベンチマークを取ってみた。

Coala 未使用

package main

import (
    "net/http"

    "github.com/zenazn/goji"
    "github.com/zenazn/goji/web"
)

func main() {
    goji.Get("/"func(c web.C, w http.ResponseWriter, r *http.Request) {
        w.Write([]byte("Hello World"))
    })
    goji.Serve()
}
$ ab -n 10 -n 10000 http://localhost:8000/ Server Software:
Server Hostname:        localhost
Server Port:            8000

Document Path:          /
Document Length:        11 bytes

Concurrency Level:      10
Time taken for tests:   4.185837 seconds
Complete requests:      10000
Failed requests:        0
Write errors:           0
Total transferred:      1280000 bytes
HTML transferred:       110000 bytes
Requests per second:    2389.01 [#/sec] (mean)
Time per request:       4.186 [ms] (mean)
Time per request:       0.419 [ms] (mean, across all concurrent requests)
Transfer rate:          298.63 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        0    0   0.1      0       7
Processing:     0    3   1.5      4      31
Waiting:        0    2   1.5      2      30
Total:          0    3   1.5      4      31

Percentage of the requests served within a certain time (ms)
  50%      4
  66%      4
  75%      4
  80%      4
  90%      5
  95%      6
  98%      7
  99%      8
 100%     31 (longest request)

Coala 使用

package main

import (
    "net/http"

    "github.com/goware/httpcoala"
    "github.com/zenazn/goji"
    "github.com/zenazn/goji/web"
)

func main() {
    goji.Get("/"func(c web.C, w http.ResponseWriter, r *http.Request) {
        w.Write([]byte("Hello World"))
    })
    goji.Use(httpcoala.Route("GET")) // ココ
    goji.Serve()
}
Server Software:
Server Hostname:        localhost
Server Port:            8000

Document Path:          /
Document Length:        11 bytes

Concurrency Level:      10
Time taken for tests:   3.860772 seconds
Complete requests:      10000
Failed requests:        0
Write errors:           0
Total transferred:      1280000 bytes
HTML transferred:       110000 bytes
Requests per second:    2590.16 [#/sec] (mean)
Time per request:       3.861 [ms] (mean)
Time per request:       0.386 [ms] (mean, across all concurrent requests)
Transfer rate:          323.77 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        0    0   0.0      0       1
Processing:     0    3   2.2      3      74
Waiting:        0    2   2.3      2      71
Total:          0    3   2.2      3      74

Percentage of the requests served within a certain time (ms)
  50%      3
  66%      3
  75%      4
  80%      4
  90%      4
  95%      5
  98%      5
  99%      6
 100%     74 (longest request)

確かに若干ですがパフォーマンスが良くなってる。README.md によるとスレッド数を上げると

Without httpcoala middleware, Requests/sec:   7081.09
   With httpcoala middleware, Requests/sec:  18373.87

2倍以上の差が出る場合もある様です。何がそうさせるのだろうとコードを読んでみた。

httpcoala/httpcoala.go at master · goware/httpcoala · GitHub
https://github.com/goware/httpcoala/blob/master/httpcoala.go

複数のリクエストを channel を使って結合し、同じレスポンスを返すという仕組みで毎回 ResponseWriter の生成や切断時処理を行うのを回避してる。ハンドラ内が遅くなればなるほど効果が出ます。

httpcoala/httpcoala.go at master · goware/httpcoala · GitHub
https://github.com/goware/httpcoala/blob/master/httpcoala.go#L71-L78

今回の様に同じ URL に対して同じメソッドのリクエストが頻繁に起こる場合には非常に有効な手段だと分かる。キャッシュではない為、リアルタイム性も担保される。なかなかいい所に目を付けてるなーと思った。

効果には個人差がある様です。

Posted at by