サーバーサイドエンジニアの技術メモあれこれ

調べたことのメモがき。その他雑記もちょろちょろと。ゆるくやってます。

Rustメモ

What is Rust

www.rust-lang.org

メモ

変数宣言パターン

OKな例
// 不変の変数
let _str = String::from("hello");
// 可変の変数(mutを指定してミュータブルにする)
let mut _str = String::from("hello");
// 不変の変数に対する更新NG参照を不変の変数に格納
let _str = String::from("hello");
let _refer = &str;
// 可変の変数に対する更新OK参照を不変の変数に格納
let mut _str = String::from("hello");
let _ref = &mut str;
// 不変の変数に対する更新NG参照を可変の変数に格納
let _str = String::from("hello");
let mut _refer = &str;
// 可変の変数に対する更新NG参照を不変の変数に格納
let mut _str = String::from("hello");
let _refer = &str;
NGな例
// 不変の変数に対する更新OK参照
let _str = String::from("hello");
let _refer = &mut str;

標準入力から文字列を受け取るやり方

use std::io; は前提として

let mut input = String::new();
io::stdin().read_line(&mut input).unwrap();

let vec: Vec<u32> = input
                        .split_whitespace()
                        .map(|n| u32::from_str_radix(n, 10).unwrap())
                        .collect();

C言語メモ

What is C

www.c-lang.org

メモ

#includeの挙動

#includeの行がヘッダファイルの内容へ置き換えられる

#ifndef ~ #endif

当該ヘッダファイルが複数回includeされてしまうのを防ぐために使われる。 以下が例。

#ifndef _HOGE_H_
#define _HOGE_H_
(インクルードしたい内容)
#endif

#define で_HOGE_H_が宣言されていなかったら#ifndef ~ #endif間が実行される。 _HOGE_H_をinclude実行のフラグとして使っている感じ。

stdint

固定サイズの整数型を提供するライブラリ。

int32_t
int64_t

みたいな長さ決め打ちの整数型を利用できる。 最近はあまり意識しなくてもよいはずだが、intは環境によって16ビットだったり32ビットだったり64ビットだったりする場合があるため明示的に指定できるようにこのライブラリが用意されている。

_tってそういえば何

typedefで作った型の名前に慣習的につけるもの

0をポインタ型にキャストすると

null ポインタになる

ポインタ型をifの条件節に渡すと

  • null ポインタの場合false
  • それ以外はtrue として判断される

getopt

en.wikipedia.org

optionをgetするということなので、コマンドに追加で引っ付けられるオプションを解析するもの。

実際使うときはこんな感じ(上記wikiより引用)

#include <getopt.h>

int getopt_long(int argc, char * const argv[],
                const char *optstring,
                const struct option *longopts, int *longindex);

getoptはoptstringで指定したコロンの数によってオプションの性質を決められる。

オプション文字の直後に

  • コロンが無い→引数無し
  • コロンが1つある→引数アリだがなくても良い。(つまりデフォルト値が存在する)
  • コロンが2つある→引数必須

という感じ。

例えば

abc:d::

となっている場合、 a,b,c,dの4つのオプションがあり、a,bは引数無し、cは引数ありだがなくても良い、dは引数必須、ということが分かる。https://en.wikipedia.org/wiki/Getopt

sigset_tの初期化パターン

sigset_tは内部的にはunsigned long int の配列でシグナルを保持できる。 sigset_tの初期化パターンは2通り覚えておく

sigfillset後に絞り込みパターン

全てのシグナルをsigset_tに突っ込んで捕捉し、その後デフォルト動作をさせたいものだけ適宜除外してから利用する方式。 割り込み絶対許さないマンになりたいときなどはこっちを使えば楽。

sigemptyset後に追加パターン

取り敢えずシグナルは無しの状態で初期化し、シグナルを適宜後から追加して補足する 特定のシグナルを受け取った時だけ動作を変えたいパターンの時はこっちが有効。

C言語でハッシュテーブル

<search.h>を使えば実現できる

linuxjm.osdn.jp

hserach_data型の構造体変数を用意し、hcreateやhcreate_rで初期化すればhashを扱う準備が整う。 _rがついている方は複数のハッシュテーブルを利用することができるので、そちらを使う方が都合が良いことが多い。 hashテーブルをホワイトボード的に使う場合など単一の方が都合が良いときはr無しの方を使えば良さそう。

hsearchに渡すのはENTRY型じゃなく文字列ではだめ?

searchという名前がついているのに検索する際にはchar型配列ではなくENTRY型変数(keyとそれに対応するdataを持つ構造体)を利用するのは少し違和感がある。key文字列だけでもよいのではないかと思っちゃう。

実際のところ、この関数はsearchだけかinsertもやるのかを選択できる。第二引数において、FINDを指定した場合はsearch, ENTERを指定した場合はsearch + insertという挙動※になる。 insert処理を実行することを念頭にプログラムを組むならENTRY型でkey-valueをセットで渡す必要があるため、そこまでカバーすることを考えてENTRY型になっている。

serachが成功したならENTER指定だろうとinsertは実行されないことに注意する。POSIXで決まっている。

timeval構造体

select などの一定時間監視するタイプの関数に引数の型として指定されていることが多い。

long tv_sec 時間の整数部。

long tv_usec 時間の小数部。マイクロ秒単位。

ミリ秒単位で利用するには

表現したいミリ秒の整数値を用意し、その整数を

  • 1000で割ったものをtv_sec
  • 1000で割った余りを1000倍したものをtv_usec

に代入すればよい。 1000で割った余りは必ず3桁以下になり、それを1000倍すると必ず高々6桁の整数になるので、ミリ秒をマイクロ秒に変換したものを表現できる。

typedefの考え方

「普通に変数を宣言するつもりでコードを書き、頭にtypedefをつければ変数の部分を型名として新たな方を定義できる」と考えるとわかりやすい

qiita.com

FD_ISSET(0, &rfds)とselect

linuxjm.osdn.jp

FD_SETを実行後にFD_ISSETで確認を行うと、基本的にはtrueが返ってくる。 ただし、selectを実行してrfdsに含まれる識別子が示すファイルを監視中に該当ファイルに対して接続が発生しなかった場合はfalseとなる。

このため、listenソケットやacceptソケットを生成してファイルディスクリプタがそれぞれのソケットに割り振られている状態だったとしても、接続が発生していなければFD_ISSETはfalseを返す。 確認用プログラムが以下。大分部を上記リンクのサンプルプログラムから流用。

#include <stdio.h>
#include <stdlib.h>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>

int
main(void)
{
    fd_set rfds;
    struct timeval tv;
    int retval;

    /* stdin (fd 0) を監視し、入力があった場合に表示する。*/
    FD_ZERO(&rfds);
    FD_SET(0, &rfds);

    /* 5 秒間監視する。*/
    tv.tv_sec = 5;
    tv.tv_usec = 0;

    retval = select(1, &rfds, NULL, NULL, &tv);
    /* この時点での tv の値を信頼してはならない。*/

    if (retval == -1) {
        perror("select()");
    }
    else if (retval)
    {
        printf("今、データが取得できました。\n");
        printf("select後かつデータ取得成功時のFD_ISSET: %d\n", FD_ISSET(0, &rfds));
        /* FD_ISSET(0, &rfds) が true になる。*/
    }
    else
    {
        printf("5 秒以内にデータが入力されませんでした。\n");
        printf("select後かつデータ取得失敗時のFD_ISSET: %d\n", FD_ISSET(0, &rfds));
    }
    exit(EXIT_SUCCESS);
}

出力

失敗時

select前のFD_ISSET: 1
5 秒以内にデータが入力されませんでした。
select後かつデータ取得失敗時のFD_ISSET: 0

成功時

select前のFD_ISSET: 1
a
今、データが取得できました。
select後かつデータ取得失敗時のFD_ISSET: 1

Apache Module メモ

※この記事は2.x系前提。概ね2.4。

What is Apache Module

www.ibm.com

可変長リストやkey-valueのハッシュテーブルなど、C言語らしからぬリッチな機能が一通りそろっておりC言語と言えど効率的に開発できる。 Apacheモジュール開発に許された特権。 メモリプールをがばっと確保して解放するということをApache側が勝手にやってくれるのでメモリ管理もかなり楽。 そしてなによりCなので高速に動作する。

メモ

おおざっぱに分かっておくとよい事

httpd起動時に準備をしている

主に認識しておくとよいのは以下。

  • confの読み込み
  • 子プロセスのfork, 設定
  • リクエスト処理ループに入る

リクエスト処理ループ

httpリクエストを受け取ったら入る処理フロー。この処理フローのどこかに到達したらモジュールが動作するように設定できる。

www.fmc-modeling.org

Apacheはモジュールの集合体である

これは忘れがちなので覚えておく

ディレクティブはモジュールの設定である

Apacheはモジュールの集合体であることを踏まえると、confはApacheに対する設定ではなくディレクティブに紐づく各モジュールに対する設定と認識する。

お手軽な作り方

CentOS系を想定

必要なrpmを入れる

yum install httpd httpd-devel

testモジュールを生成する

apxs -g -n test

MakeFile, modules.mk, mod_test.cの3つが生成される。

コンパイルする

apxs -c -i -a mod_test.c

基本的には動的リンクを用いた動的組み込みをしたほうがよいため、loadmoduleディレクティブを追加するべく -a オプションをつけてコンパイルしている。 既にLoadModuleを追加している場合は重複が発生してエラーになるようなので注意。

httpd.confにLocationを追加する

<Location "/hoge/">
    SetHandler test
</Location>

リクエストしてみる

curl "http://localhost/hoge/"

The sample page from mod_test.cと帰ってきたら成功。

自動生成したmod_test.cのコメントに書かれているとおりlynxを使っても良い

lynx -mime_header http://localhost/hoge/

mod_○○.cのざっくり全体構成

ヘッダインクルード

設定情報を保持する変数を作成する関数(ここで初期化)

設定情報を更新する関数(コマンドの配列とセットで定義)

コマンドの配列(ディレクティブで定義している設定情報を読み込むための関数や、ディレクティブの構造を指定)

フックに登録したい関数たち(フックをかけるタイミングがわかるように名前を定義するとよい)

フックに実際に登録する関数

↑で定義してきたものをディスパッチするための配列

ログ周り

ログフォーマット

httpd.apache.org

カスタムログ

httpd.apache.org

良くやるのが以下(上記リンクより引用)

LogFormat "%h %l %u %t \"%r\" %>s %b" common
CustomLog logs/access_log common

LogFormatを決めてnicknameを名付ける(ここではcommon)。 CostomLogで既に定義したLogFormatを読み込み、指定したディレクトリにログファイルを生成する。 commonのところにLogFormatが展開されている感じ。

本格的に使っていくなら例えば以下のようにしてログローテートさせる。

CustomLog "|/usr/local/apache/bin/rotatelogs /var/log/access_log 86400" common env=common_env
SetEnvIf Request_URI "^/hoge$"  common_env

env=common_envのところは、CutomLogを用いてログ収集をするのかを決めるための判断軸。 右辺に指定した環境変数が存在する場合にログを落とす。 その環境変数はSetEnvIfを使って指定している。この場合、/hogeに完全一致するURIでリクエストが来ていたらcommon_env環境変数をセットするようにしてある。これによりどのリクエストのログを落とすのかの分岐が可能。

rotatelog

以下を参照。-c -L -l あたりは良く使うはず。 httpd.apache.org

参考になるモジュール

コメントもしっかり書いてあるので勉強になる。 github.com

MySQLメモ

What is MySQL

www.oracle.com

メモ

ダミーデータを突っ込みたいとき

超大量にあるならLOAD DATA LOCAL INFILE がオススメ。バルクインサートよりも圧倒的に早い。

MySQL :: MySQL 5.6 リファレンスマニュアル :: 13.2.6 LOAD DATA INFILE 構文

CSVの場合以下。一番シンプルなので小難しく考えずに使える。このコマンドの場合突っ込むテーブルのカラムを全て順番通りに指定してあげる必要がある。

LOAD DATA LOCAL INFILE (CSVのファイルパス) INTO TABLE (格納したいテーブルの名前) FIELDS TERMINATED BY ','

カラムを明示的に指定したい場合はCOLUMNSオプションを利用する。詳細は公式ページを参照するとよい。

なぜか開発環境のディスク使用量がひっ迫してきたとき

参考:tiny-garage.net

ダンプファイルからのrestore実行時などにバイナリログがもりもり生成されている可能性がある。

ログ確認

SHOW MASTER LOGS;

削除

本番環境などでやらないように

PURGE MASTER LOGS before now();

恒久的にはdisable-log-binをmy.cnfに追記してあげてバイナリログ自体を出力しないようにする。

Redisメモ

What is Redis

redis.io

aws.amazon.com

 

メモ

単一のRedisインスタンスに保存できるKey数の最大値

理論的には2^32まで行ける。もちろん利用者の環境制約に縛られる

Redis can handle up to 2^32 keys, and was tested in practice to handle at least 250 million keys per instance.

Every hash, list, set, and sorted set, can hold 2^32 elements.

In other words your limit is likely the available memory in your system.

Redis FAQ | Redis

MGETなどm系コマンドはクラスタ構成では利用できない

Redisをクラスタ構成にした場合に、複数のノードにまたがって存在するKeyに対してMGETは利用できない。

一つのコマンドは一つのサーバで走るため、複数サーバに情報が分散するクラスタ構成では使えない道理。

使えるようにするにはひとつのサーバに情報を寄せるといった対応が必要。

MGETの代わりにGETのパイプライン処理を使うのも手。ただしクラスタ構成でパイプライン処理を使った場合は、送ったコマンドのkeyの並びと帰ってくるvalueの並びは必ずしも順序が一致しないため注意すること。

RedisのCPUボトルネック

シングルスレッドで動作するためCPU全体の使用率ではなく特定コアの使用率がボトルネックになりやすいという認識を持っておくと取り敢えず良い。

以下の記事がとても分かりやすい。Redisモジュール利用やLuaスクリプティングができるようになるとカスタマイズの余地が大幅に上がる。

※ただし、なんでもかんでもRedisモジュール/Luaの独自カスタマイズに頼らないこと。ほかの手段がないか検討すべきということは常に頭に入れておく。

blog.yuuk.io

Redisモジュール

少しヘビーなので別記事にする。