Diary

Diary

日々学んだことをアウトプットする場として初めてみました

ターミナル上で数値の16進数の変換を行う

ターミナル上で数値の16進数の変換を行う

  • bc コマンド, obase
  • カラーコードを、(255,255,255) -> #FFFFFF への変換を行う

bc コマンド

  • 基本的な計算をしてくれるコマンド
$ echo '4*5' | bc
> 20
  • 桁数指定
    • デフォルトでは小数点以下切り捨てになるので、scaleを指定してあげる
    • scaleをつける場所に注意
$ echo '1/3' | bc
> 0
$ echo 'scale=4; 1/3' | bc
> .3333
  • 数学ライブラリを用いるとlogの計算などができる
    • 詳しくはman bc | grep '\-l' -A20などで確認すると良い
    • -lオプションで指定できる
## l(x) = log(x)
$ echo 'l(3)' | bc -l
> 1.09861228866810969139

bcコマンドを用いた16進数への変換

  • obaseを用いる
  • output base(出力の基底)を指定できるオプション(?)
    • ibase: inputバージョンもある
  • 使い方
$ echo 'obase=16; 255' | bc
> FF

カラーコードの変換

目標は

(255, 255, 255) -> #FFFFFF への変換を行う

こと

実際の手順

1 ()や,の前後のスペースを取り除く

$ echo "(255, 255, 255)" | tr -d "() "
> 255,255,255

2 各々の数字に対して上の"bcを用いた基底変換"を使いたかったので、','で区切って行に変換

$ echo "(255, 255, 255)" | tr -d "() " | tr "," "\n"
> 255
> 255
> 255

3 各行に対してbcコマンドを用いて16進数へ変換

$ echo "(255, 255, 255)" | tr -d "() " | tr "," "\n" | xargs -I@ echo "obase=16; @" | bc
> FF
> FF
> FF

4 最後に3行をまとめてあげて、先頭に#をつけたら完成

$ echo "(255, 255, 255)" | tr -d "() " | tr "," "\n" | xargs -I@ echo "obase=16; @" | bc | tr -d "\n" | sed "s@^@#@"
> #FFFFFF
$ echo "(128, 32, 32)" | tr -d "() " | tr "," "\n" | xargs -I@ echo "obase=16; @" | bc | tr -d "\n" | sed "s@^@#@"
> #802020

(おまけ)ファイルから (255,255,255) のようなカラーコードを見つける

  • grep拡張正規表現のオプションを利用する(登場回数を{}で指定できるようになる)
  • (a, b, c)を抜き出すコマンド
    • a,b,cは1-3桁の数字
    • b,c の前には空白があってもなくても良い
$ cat color 
> (255, 255, 255) is one of the ideal color code.
> (2555, 123, 1) is a bad request.
> (12,3,6) is also detected.

$ cat color | grep -Eo '\([0-9]{1,3},\s?[0-9]{1,3},\s?[0-9]{1,3}\)'
> (255, 255, 255)
> (12,3,6)

tag

linux, ワンライナー

curlによるHTTP/3通信

curlによるHTTP/3通信

  • やったこと
    • SSLのインストール(http3は標準でsslを使用)
    • curlビルド時にSSLを紐付ける

現状確認

現在のMacに入っているVersionを確認する

$ curl -V
> curl 7.68.0 (x86_64-apple-darwin13.4.0) libcurl/7.68.0 OpenSSL/1.1.1d zlib/1.2.11 libssh2/1.8.2

この状態では、(helpで確認できるのにも関わらず)http3通信は行えない

$ curl --help | grep http3
>  --http3         Use HTTP v3
$ curl --http3 google.com
> curl: option --http3: the installed libcurl version doesn't support this
> curl: try 'curl --help' or 'curl --manual' for more information

curlインストール

事前準備として、Rustが必要

SSLのビルド

$ git clone https://github.com/cloudflare/quiche --recursive
$ cd quiche
$ cargo build --release --features ffi,pkg-config-meta,qlog
$ mkdir deps/boringssl/src/lib/
$ ln -vnf $(find target/release -name libcrypto.a -o -name libssl.a) deps/boringssl/src/lib

Curlのビルド

$ cd ..
$ git clone https://github.com/curl/curl.git
$ cd curl
$ ./buildconf
$ ./configure LDFLANGS="-Wl,-rpath,$PWD/../quiche/target/release" --with-ssl=$PWD/../quiche/target/release
$ make

確認してみると、下のようにちゃんと新しいバージョンが入っている。(unreleasedとかなってるけどええんか?)

$ ./src/curl -V   
> curl 7.78.0-DEV (x86_64-apple-darwin20.5.0) libcurl/7.78.0-DEV BoringSSL zlib/1.2.11 nghttp2/1.43.0 quiche/0.9.0
Release-Date: [unreleased]
Protocols: dict file ftp ftps gopher gophers http https imap imaps ldap ldaps mqtt pop3 pop3s rtsp smb smbs smtp smtps telnet tftp 
Features: alt-svc AsynchDNS HSTS HTTP2 HTTP3 HTTPS-proxy IPv6 Largefile libz NTLM NTLM_WB SSL UnixSockets

BoringSSLとかも登録されてそうで、なんかうまくいきそうな予感

HTTP3通信を行ってみる

$ ./src/curl -v --http3 https://google.com
>
*   Trying 172.217.31.174:443...
* Connect socket 5 over QUIC to 172.217.31.174:443
* Sent QUIC client Initial, ALPN: h3,h3-29,h3-28,h3-27
* Connected to google.com () port 443 (#0)
* h3 [:method: GET]
* h3 [:path: /]
* h3 [:scheme: https]
* h3 [:authority: google.com]
* h3 [user-agent: curl/7.78.0-DEV]
* h3 [accept: */*]
* Using HTTP/3 Stream ID: 0 (easy handle 0x7fd8c980b600)
> GET / HTTP/3
> Host: google.com
> user-agent: curl/7.78.0-DEV
> accept: */*

なんかそれっぽいことしてそう

あとはデフォルトでこれを使いたければ、~/.vimrc~/.bashrcなどに、

alias curl='src/curlまでのフルパス --http3'

とかを追記しとけばよさそう

脱RDBMS超初心者

RDBMS超初心者

  • 想定する読者
    • 簡単な簡単なクエリ文が書けるようになった人
    • RDBMSの中身を知ってみたい人
  • 目標
    • RDBMSが内部で行なっている処理がなんとなく掴める

RDBMSアーキテクチャ

  • RDBMSとは、Relational DataBase Management Systemの略であり、SQLを用いてDBを管理するようなものを指す

  • アーキテクチャはユーザに近い方から以下のようになっている

    1. 構文解析
    2. 実行計画
    3. クエリエクスキュータ
    4. アクセスメソッド
    5. バッファプールマネジャ
    6. ディスクマネージャ

1.構文解析

SQLのクエリ文をパースし、抽象構文木にする

2.実行計画

「どのようなクエリを発行するか」といった、実行計画を立てる。

EXPLAIN のコマンドを打ち込むことで確認できる

EXPLAIN SELECT * FROM users;

3.クエリエクスキュータ

実行計画をクエリエクスキュータで実行する

この際にIndexが貼られていたらそれに似合った挿入方法や検索方法にする

セカンダリインデックス(PK以外におけるもので高速検索したいもの)があれば、ここで指定しておく。基本的にIndexによる高速スキャンは、key・valueのペアによって保存されたものに対するスキャンで行われている。

4.アクセスメソッド

どのような方法でデータ構造にアクセスするかのアルゴリズムの指定

RDBMSでは、B-Tree(B+Tree)というものが使われる

B+Tree

  • 平衡N分木のデータ構造。N分木は2分木をNに拡張したもの。
  • 値はkey・valueのペアによって保存されている
    • keyのペアによってソートされたものをN本に分割する
    • valueに対応しそうなものが複数ありそうなら、うまく結合して1つにする
  • RDBMSのページ対応と非常に相性が良い

5.バッファプールマネジャ

ディスクアクセスの遅さを隠蔽するための処理

通常はファイルシステムが必要そうな情報を判断してメモリ上にキャッシュを残すが、それを独自仕様でやるための処理階層

どの情報をキャッシュとして残すかのアルゴリズムとしては、Clock-sweepなどがある(PostgreSQL等で採用)

6.ディスクマネージャ

実際にディスクとの読み書きを行う階層

ginを用いたルーティング機能

[golang] ginを用いたルーティング機能

ginを用いてサーバーを構築しチャットアプリを作成したので、その際に気をつけたルーティングやその書き方についてまとめておく

やったこと

  • Ginを用いたサーバー配信
  • Ginが静的サイトの配信とAPIサーバーとしての役割の両方を担っている
  • 一部APIのエンドポイントには、ミドルウェアかましている

注意するところ

  • ginの設定(r.Group, r.Useなど)は、上から順に実行されていく
    • つまり、書いたところより下にのみ適応される
    • grouping, middleware などがそうなっている

コード例

package main

import (
    "api/api"
    "api/db"

    "github.com/gin-contrib/static"
    "github.com/gin-gonic/gin"
)

func setupRouter() *gin.Engine {
    r := gin.New()

    // 静的ファイルのパスを指定
    r.Use(static.Serve("/", static.LocalFile("./front/dist", true)))

    // これより下のパスに対しては、全て/api/~ になる
    apiGroup := r.Group("/api")

    apiGroup.POST("/login", api.LoginCheck)
    apiGroup.POST("/users", api.RegisterNewUser)
    apiGroup.POST("/account", api.UpdateMyInfo)

    apiGroup.Use(api.LoginCheckMiddleWare())
    {
        apiGroup.GET("/users", api.GetAllUsers)
        apiGroup.POST("/logout", api.Logout)

        apiGroup.GET("/chats", api.GetAllMessages)
        apiGroup.PATCH("/chats", api.EditChat)

        apiGroup.POST("/rooms", api.CreateNewGroup)
    }

    return r
}

func main() {
    // DB の初期設定をする
    _db, err := db.DbInit()
    if err != nil {
        panic("")
    }
    defer _db.Close()

    // Gin出力設定
    gin.ForceConsoleColor()
    // setupLogOutput()

    r := setupRouter()
    r.Run(":8888")
}

HTTP/2, HTTP/3概論

HTTP/2, HTTP/3概論

HTTP/1しか知らない、いやHTTP/1すらよくわからない僕ですが、勉強して自分が気になった点についてまとめてみます

HTTP/1

まずはHTTP/1から

基本的な技術

  • HTTPS
    • TLSを利用したSecureなHTTP通信
      • Transport Layer Security
  • TCP
    • 3 way ハンドシェイクによる確実な通信
  • TLS
    • TLS1.2以下では、フルハンドシェイクによる通信があり、最低四回のやりとりが必要

つまり、TCP + TLSでの通信においてのシーケンス図は下のようになる

  • かなり略しているので、詳しくはrfc8446など
  • TCP
    • SYN: 相手先の確認用
    • ACK: 応答確認
  • TLS
    • STCPと同じようにハンドシェイクが必要

f:id:kokoichi206:20210623223509p:plain

問題点

  • 一回接続するたびに接続が切れるので、何回もハンドシェイクをしないといけない
  • TCPTLSでハンドシェイクが被っていて、オーバーヘッドが大きい

改善策

  • Keep-Alive
    • TCP接続を使い回す
  • 同時リクエス
    • 同時に複数のリクエストを送る
  • HTTP/2

HTTP/2

  • HTTP/1のデメリットを補うために開発された(by Google

特徴

  • バイナリ形式のプロトコル
    • フレーム化によりメッセージを分割できる
  • 多重化
    • 単一のTCP接続で複数のリクエストが可能
  • 優先度制御とフロー制御
    • TCP接続を持続させることにより、自ら制御する必要が出てきた
  • ヘッダー圧縮
  • サーバープッシュ
    • サーバーからレスポンスを送信できる

HTTP/3

おまけ

検証ツールを開き、NetworkタブのNameの欄を右クリックし、Protocolを選択(下はspotifyでの例)

[`p:plain]

するとProtocolの欄が追加される

f:id:kokoichi206:20210623225158p:plain

h2はHTTP/2による通信、h3-*はHTTP/3による通信が行われていることを示す。

  • 本文中のシーケンス図は以下のようにmermaidを用いて作成した
sequenceDiagram
participant 1 as client
participant 2 as server

Note over 1, 2: TCP Connection

1->>+2: SYN(シン)
2->>1: SYN + ACK(アック)
1->>+2: ACK

Note over 1, 2: TLS Connection

1->>+2: ClientHellow
2->>1: ServerHellow, ServerHelloDone
1->>+2: KeyExchange, Finished
2->>1: Finished

Note over 1, 2: ハンドシェイク終了

1->>+2: HTTP リクエス

Kubernetesとは

Kubernetesとは

なぜ必要となってきたか

今やアプリにおいてコンテナ化は必須!

  • 軽量・可搬性
  • リソースを無駄なく使える

その流れにおいて、実際の環境では、

  • 同じ設定のコンテナを複数のサーバで起動
  • 通信を複数のコンテナに負担分散
  • サーバが落ちたらコンテナの他サーバを起動し直す(冗長性)
  • オートスケール

などを考える必要があり、とても人間が管理することはできない!!

そこで、コンテナオーケストレーション(大量のコンテナ管理)やクラスタ管理(大量のサーバ管理)を行なってくれるものが必要となった。それがKubernetes

クラスタ

複数のコンピュータを結合して協調動作させるシステムのこと

デメリット?

  • Nodeの障害自体は復旧したい
  • k8s自体が複雑

基本的な考え方

  • 宣言的設定:Declarative Configulation

    • あるべき状態を記述する設定方法
    • 対象の状態に関わらず、あるべき姿に制御しようとする
    • ついになる単語は命令的
      • 命令的が”動作”を指定するのに対し、宣言的は”状態”を指定する
  • 調停ループ:Reconciliation Loop

    • 宣言的設定を実現するための仕組み
    • 差分を検知し、それをゼロにするべく動いている
  • 自己回復:Self-healing

    • 宣言的設定と調停ループによってもたらされる特性

実際にどう実現されるか

部分

  • Pod

    • k8sでの管理上最も根源的な単位
    • 実態は同一のIPアドレスが付与されている
    • 内部のコンテナが以上終了した場合、再起動する
  • Volume

    • Pod内のコンテナで共有されるディスク領域
  • Master

  • Node

    • 動かすアプリケーション・コンテナ
    • Masterからの命令を受け取り、その通りにpodを設定する

概念的なもの

  • Resource(Object)
    • k8sが管理する概念
    • Podやvolumeは基本的Resourceの1つ
  • Controller
    • Resourceを管理するためのResource

Resource詳細

  • ConfigMap/Secret
    • 主にPodに対する設定値を管理する
  • ReplicaSet
    • Podの数を一定に保つ
  • Deployment
    • 複数のReplicaSetを用い、ダウンタイムを最小化してアプリケーションの更新を行う

注意点

  • 1コンテナ1プロセス
  • 環境変数で環境差分を吸収する
  • 状態をコンテナ内部に持たせない
  • ログを標準出力に流す
    • ファイルに吐くとステートフルになってしまう...
    • 暗黙の了解?

これから

ネットワークなどは飛ばしてしまいました。。。

いつか実装などもできたらいいなと思います

バグの切り分け

バグの解決まで

  1. バグの再現方法の確立
  2. 発生環境の確認
  3. 原因の解析
  4. 修正方法の検討
  5. 修正の適応
  6. 修正方法の確認

1.バグの再現方法の確立

  1. バグを再現
  2. 発生事象
  3. 発生環境
  4. 期待結果
  5. 再現手順