地方エンジニアの学習日記

興味ある技術の雑なメモだったりを書いてくブログ。たまに日記とガジェット紹介。

【Nginx】proxy_cache_bypassを使ってみる。

Nginx -> appみたいな構成でproxy_cacheを入れているとappまでリクエストが到達せずにNginxがリクエストを返すことができる。これはこれでとても便利なのでがブラウザのスーパリロード時はappまで貫通させたいみたいなケースがあったので調べたメモ。

結論

proxy_cache_bypassを使えば実現可能。

nginx.org

任意のヘッダーに$http_${ヘッダー名}と言う形で参照できるのでその値を使ってキャッシュを使うかを処理させます。今回の場合は"$http_cache_control"

upstream app {
    server flask:5000;
}

proxy_cache_path /var/cache/nginx keys_zone=zone1:1m max_size=1g inactive=24h;
proxy_temp_path  /var/cache/nginx_tmp;

server {
    listen       80;

    location / {
        set $cc_ma  0;
        if ($http_cache_control = "no-cache") {
            set $cc_ma  1;
        }
        proxy_pass http://app;
        proxy_http_version 1.1;
        proxy_set_header Connection "";
        proxy_cache zone1;
        proxy_cache_valid 200 302 10s;
        proxy_cache_bypass  $cc_ma;
        add_header X-Nginx-Cache $upstream_cache_status;
    }
}

こうすることでcurlにCache-Control: no-cacheを渡すことでX-Nginx-CacheがBYPASSとなってappまでリクエストが到達していることを確認することができた。http, server, locationで書けてので結構使い所がありそうと感じた。

< HTTP/1.1 200 OK
< Server: nginx/1.21.1
< Date: Sun, 22 Aug 2021 07:52:56 GMT
< Content-Type: text/html; charset=utf-8
< Content-Length: 8
< Connection: keep-alive
< X-Nginx-Cache: BYPASS

【Ruby】SYNCNTを調整して早めにタイムアウトさせる

自宅で動いてるVMが15台を超えそれぞれどのポートで何してるんだっけってなることが増えてきた。コマンド一発でどのサーバのどのポートがopenになってるのかを確認するコマンドが欲しくなったのでRubyで実装しようと思ってやったメモ。

実装方針

docs.ruby-lang.org

connectを使う。vmごとに色々なportにconnectして成功するかを確認していくイメージで作る。

require 'socket'

vms = [ "192.168.1.1" ]
ports = [21, 22, 23, 80, 443]

s = Socket.new(Socket::AF_INET, Socket::SOCK_STREAM, 0)
vms.each do |vm|
  ports.each do |port|
    begin
      s.connect(Socket.sockaddr_in(port, vm))
    rescue Errno::ECONNREFUSED, Errno::ETIMEDOUT
      puts "close #{vm}:#{port}"
    else
      puts "open #{vm}:#{port}"
    end
  end
end

実装してみたらこんな感じ。openしてるポートがわかれば良いかなぐらいで実行してみる。するとめちゃめちゃ時間がかかることがわかった。原因はRSTを返さずdropするポートがあるらしく再送してくれている模様。

自宅サーバだしfirewalldの設定を変えるか/proc/sys/net/ipv4/tcp_syn_retries辺りを変えてすぐにプロセスに戻すのでも良いかなとか思ったが絶賛Ruby門中だったのでRubyでやることにした。

実装方針2

setsockopt(2)を直接実行するかとも思ったがBasicSocket#setsockoptと言うのがあるらしく公式ドキュメントを読む練習にもなりそうなのでこちらを使ってみる。

docs.ruby-lang.org

tcpのソケットにはTCP_SYNCNTというオプションがあるのでそちらを指定するように修正。再送回数は1回で指定して実行してみる。実行時間が数秒まで短くなることが確認できた。ちなみにMacだとTCP_SYNCNTがないらしく動かなかった。

require 'socket'

vms = [ "192.168.1.207" ]
ports = [1, 2, 3, 21, 22, 23, 80, 443, 8080, 8443]

s = Socket.new(Socket::AF_INET, Socket::SOCK_STREAM, 0)
s.setsockopt("TCP", "SYNCNT", 1)
vms.each do |vm|
  ports.each do |port|
    begin
      s.connect(Socket.sockaddr_in(port, vm))
    rescue Errno::ECONNREFUSED, Errno::ETIMEDOUT
      puts "close #{vm}:#{port}"
    else
      puts "open #{vm}:#{port}"
    end
  end
end

再送回数の最大値

man tcpとかで見ると255 より大きくはできないと書いてある。255まで設定できるのかなと思って設定しようとすると以下のようなエラーとなった。127あたりが限界値となっていそう。

[root@c4acc3700c47 /]# ruby a.rb
a.rb:4:in `setsockopt': Invalid argument (Errno::EINVAL)
    from a.rb:4:in `<main>'

設定しているのは以下のあたりでMAX_TCP_SYNCNTは127とdefineされていた。ここで制限されている模様。

int tcp_sock_set_syncnt(struct sock *sk, int val)
{
    if (val < 1 || val > MAX_TCP_SYNCNT)
        return -EINVAL;

    lock_sock(sk);
    inet_csk(sk)->icsk_syn_retries = val;
    release_sock(sk);
    return 0;
}

127より大きくすることは型を見た感じは可能なようなのでコンパイルし直せば使えそう。(やる機会は永遠になさそうですが😅)

github.com

【MySQL】MySQLのEXPLAIN

mysql> explain select id from friends where sex = 20 and age = 1;
+----+-------------+---------+------------+------+---------------+---------+---------+-------------+------+----------+-------------+
| id | select_type | table   | partitions | type | possible_keys | key     | key_len | ref         | rows | filtered | Extra       |
+----+-------------+---------+------------+------+---------------+---------+---------+-------------+------+----------+-------------+
|  1 | SIMPLE      | friends | NULL       | ref  | index_t       | index_t | 6       | const,const |    1 |   100.00 | Using index |
+----+-------------+---------+------------+------+---------------+---------+---------+-------------+------+----------+-------------+

クエリの最適化 = 「書き換える前と後でクエリの実行結果が同じになる」 AND 「実行速度が上がる」。SELECT文の先頭に「EXPLAIN」をつけて実行することでクエリの実行計画がわかる。その実行計画の見方を書いていく。とりあえずそれぞれのフィールドの意味を自分の理解で書いてみる。(EXPLAINはMySQLオプティマイザーがどの実行計画を選んだかを表示させるステートメントです)

id

select_type

クエリの種類。JOIN、サブクエリ、UNIONなど

table

アクセスする対象のテーブル

partitions

type

対象のテーブルに対してのアクセス方法

possible_keys

オプティマイザがテーブルのアクセスに利用可能なインデックスの候補として挙げたキーの一覧。必ず使われるわけではないし強制的にここにないものを使用させたりすることも可能。

key

テーブルにアクセスするために使ったindex

key_len

選択されたキーの長さ

ref

検索条件で、keyと比較されている値やカラムの種類

rows

そのテーブルからフェッチされる行数の見積もり

filtered

Extra

ほかに使用している条件など

参考

thinkit.co.jp

nippondanji.blogspot.com