日進日歩で日々進捗

非情報系学生による技術の備忘録。進捗出していきたい

Webプログラマが理解したいHook(フック)とは

フックとは

フックは、プログラマの中にプログラマが独自の処理をインクルードできる処理のことである。
普段ライブラリを使っている側からしたら、当たり前のように使っている機能であるが、実装にどういう箇所で使われているか、その内部実装に関しても解説していきたいと思う。
(ライブラリを実装する側ではないと、作ることのない機能であるけど)

実際にフックが使われている場面

下記のような場面で実際にフックが使われている。Webエンジニアでもイメージがしやすいようにかいた。

・Vue.jsのインスタンスライフサイクルフック
railsのコールバック関数
wordpressのフック
・CPUの割り込み処理など
RDBMSのトリガー処理等


フック処理は、現在の情報科学の多くの文脈で登場するので、エンジニアは積極的に理解しておきたい概念だと個人的には思う。

フックメソッドを実装する代表的なデザインパターン

TemplateMethod




後日追記

Unicord, ASCII文字コードを勉強してみた

文字コードとは??

今回、PostgresQLの開発をするにあたり、文字コードの理解が曖昧と感じる場面が多くあったので、調べたことをまとめたいと思っています。

参考にした記事
gihyo.jp

よく耳にする文字コード

ASCII, UTF-8, Unicord, Shift-JISなどの言葉を耳にしたことがあるエンジニアは多いと思います。これらが文字コードになります。

一番最初にできた文字コードがASCIIコード

ASCIIコードは最初にできた文字コードで、これは7bitと先頭1bitをチェックサムに用いた合計1byteの文字コードで、これらはアルファベットを表すのに使われました。
しかしこれらのASCIIコードで表すことができるのは、2^7 = 128種類しかりません。これでは、英語に発音記号をつけるような欧州文字や漢字、日本語を表現することはできません。
そこで、後に2byteの文字コードが誕生しました。
そこで、日本語に対応する文字コードや、欧州文字、漢字に対応する文字コードが誕生します。

Unicordが国際対応した文字コード

これらの2byte文字コードの誕生によって、多くの国の文字が表現できるようになりましたが、そこで問題になるのが互換性の問題です。
同じバイト列でも文字コードが違うとまったく違う文字列に複合されてしまうので、互換性が非常に大事になってきます。
ここで、国際的な文字コードの規格として誕生したのが、Unicordです。これによって、世界的に文字コードを統一して扱えるようになりました。

PostgreSQLをソースコードからビルドして起動する

PostgreSQLの開発をするにあたって、Ubuntu18.04 LTSで環境構築した手順をまとめます。

qiita.com

こちらのQiitaを参考にしました。

ソースコードのインストール

PostgreSQL: Downloads
こちらのサイトより、適宜自分の環境のソースコードをダウンロードできます。
自分は、Ubuntu18.04でpostgreSQLの18.04の環境構築を行いましたが、適宜自分の構築したいバージョンに置き換えて実行してください。

$ mkdir ~/src && cd ~/src
$ wget "http://ftp.postgresql.org/pub/source/v9.4.0/postgresql-9.4.0.tar.gz"
$ tar xvzf postgresql-9.4.0.tar.gz
$ cd postgresql-9.4.0
$ ./configure
$ make
$ make check
$ sudo make install


./configureで様々なオプションを設定することができますが、今回はデフォルトの設定のままで行います。

ブラウザのHTTPリクエストからレスポンス取得までのHTTPパケットの流れ詳解

Webエンジニアやインフラ系のエンジニアであれば当たり前だと思いますが、HTTPリクエストを送ってからレスポンスを取得するまでの流れをパケットレベルでの理解は、なかなか怪しいと思ったので、このさい復習してみることにしました。

大前提

普段自分が利用しているインターネットのパケットがどういう風に流れているかを理解することは、大前提大事だと思います。
イーサネットやIPパケットのフォーマットも、発表されてからもう30年も立っている歴史の長い技術ですし、今後もこれらの技術を前提に新しい技術が作られることを踏まえると、理解することは当然意味があると思います。
また、普段アプリケーションばかり書いているエンジニアもここら辺のことは、理解せずに普段仕事をしている人も多いと思います、、

使用した環境・ツール

HTTPリクエストのパケット解析: レイヤー2(EtherNet ||)

EtherNet ||のフレームは以下の5つで構成されています。

f:id:low_programing:20191014204215g:plain
https://www.infraexpert.com/study/ethernet4.html

ethernetでは、Ethernetペイロードと呼ばれるデータの部分の大きさの最大値が決まっており、(これをMTU(Maximum Transmission UNit)という)、通常46バイト~1500バイトになっています。
これ以上のデータを送る場合、通常はIPのレイヤーでデータが分割されて、データが送信されることになります。

イーサネットフレームのFCSとは?

FCS(Frame Check Sequence)とは、イーサネットレイヤーのチェックサム機構になります。通常、宛先/送信元 MACアドレスEthernetペイロード、タイプに対してチェックサムの計算が行われます。このデータに対する同様の計算を、受信側、送信側のNICで行うことで、データが正しく送受信できているかを保証することになります。

IPレイヤーだけでなく、イーサネットのレイヤーでもチェックサムの機構あるんですね。
ちなみに、このチェックサムの演算、およびFCSヘッダーの付加はNICドライバ内で行なっています。
ちなみに、ここら辺はNICドライバの内部実装になるので、ネットワークエンジニアでもほとんど扱うことはないかと思います。

MACアドレスとは?

HTTPリクエストのパケット解析: レイヤー3 (IP)

IP ヘッダーは以下のようなヘッダー群で構築されています。

f:id:low_programing:20191014205849g:plain
https://www.infraexpert.com/study/tcpip1.html

IPヘッダー

4bitのフィールドで、ここではIPパケットのバージョンを示しています。
通常、IPv4が使われるので、この値は0100担っています。一部、IPv6の通信では、0110になっています。

IPヘッダー長

IPv4では、この値は5になっています。IPヘッダーの長さは、20byteで、この数字は32bit単位で表記されているので、20 * 8 / 32 = 5となります。・

IPフラグメンテーションとは?

IPフラグメンテーションとはどのようなものでしょうか。
これは先ほど書いたMTUが密接に関わってきます。

HTTPリクエストのパケット解析: レイヤー5 (アプリケーションレイヤー)

[後日追記します]

なぜRailsのWebアプリにunicornが必要なのか

Railsを開発、デプロイする際に、unicornだのpumaだのwebrickだのpassengerだの、、
HTTPサーバーであることは理解しているものの、なぜこれが必要なのか理解していない人も多いのではないでしょうか。
私自身も理解が深いわけではありませんが、現在の段階で自分の理解をまとめてみたいと思います。かなり殴り書きです。

疑問1: なぜ、nginxとunicornを同時に使うのか。片方じゃダメなのか

結論から言うと、nginxがあってもなくてもいいですが、unicorn及びRackに対応しているHTTPサーバーは必須、となります。
なぜかというと、Rails単体ではHTTPサーバーの機能を持っていないため、RackというRailsが対応している規格に対応したwebサーバ0がRailsと紐づいている必要性があるからです。
ではunicornなどのアプリケーションサーバーが必要な理由はそれで良いとして、なぜnginxが必要なのでしょうか。

Railsのよくある構成で使われているnginxはリバースプロキシである

今回はHTTPサーバーがunicornであると言う前提で、話を進めます。
よくある本番環境で動かしているRailsアプリケーションの基本的な構成は下記のようだと思います。

f:id:low_programing:20191010172133p:plain
https://medium.com/@yucunli/a-gentle-intro-to-deploy-rails-application-with-unicorn-c99fee29cb6e

一番手前にnginxがあり、その下にunicornがあり、さらにその下にRailsアプリケーションが動いていて、一番奥にBackendDB(psqlmysql, redis ... etc)があります。

この画像だと、Railsアプリどこにあるの??と思う人がいるかもしれませんが、unicornのworkerプロセスの中でRailsが動いているイメージです。
これを聞くと、あれ??じゃあunicornRailsが一体化しちゃってるじゃん!と思うかもですが、それが正しい認識です。

先ほども言ったようにRails自体は、HTTPサーバーの機能を持っておたず、ただのバックエンドのデータをいい感じにまとめて、データをよしなに加工するだけのアプリケーションなのです。そして、先ほど登場したRackと言うインターフェイスに対応したイベントから呼ばれる前提で設計されています。
よくRailsとはWebアプリケーションフレームワークと言われていますが、正確にはHTTPサーバーとしての機能は提供しておらず、あくまでHTTPリクエストが入ってきた前提で動くものです。

nginxの役割はリバースプロキシ

上の図のように、nginxが外側のリクエストを受け付けています。具体的には、TCP/IPプロトコルで入ってきたリクエストをUNIXドメインソケット通信(同一PC内での通信)に変換して、unicornに渡しています。
unicornはnginxから渡ってきたHTTPリクエスト情報rackのインターフェイスに変換して、Railsに渡しています。

じゃあ、なぜnginxが必要なの?と言う話ですが、これはnginxの特徴として、大量で軽量な処理をさばくのに、向いているアーキテクチャであるからです。
ノンブロッキングI/Oというアーキテクチャで実装されています。詳しくはググるとたくさん出てきますが、難しいです。

Railsアプリケーションは自体はRubyで実装されており、バイナリ形式で動くnginxと比較してもパフォーマンスが良いとはいえません。
なので、簡単な処理、例えばファイルシステムに保存されている画像形式のファイル(静的ファイル)をHTTPレスポンスとして返す、などの簡単な処理においては、railsに処理を渡さない方が、高速にレスポンスを返すことができるからです。

nginxが得意な領域はnginxが引き受けて、面倒な処理が入ってくるようなリクエストの場合だけrailsに渡す、と言うやり方が一番システムとしてパフォーマンスが出るわけです。

逆に、多くのリクエストを同時にさばけるようなパフォーマンスが求められていないような場合は、nginxは必要ありません。
ローカル開発環境では、nginxを起動せずにwebrickやpumaでRailsサーバーを立ち上げていると思いますが、それが理由です。

unicornの内部アーキテクチャ・実装、特徴など

unicornは、基本的にマルチプロセス、ブロッキングI/Oのアーキテクチャを採用しています。
つまり、各プロセスが担当する処理が終わるまでプロセスが解放されないため、大量にリクエストがきた場合、リクエストの待ち行列が増えることになり、パフォーマンスが悪化します。このアーキテクチャはWebサーバーのAppacheと同等のものです。

こちらの記事が大変参考になりました。
blog.willnet.in

Javascriptでブロッキングなsleepメソッドを実装する

基本的にシングルスレッドで実行されるブラウザ上のJavaScriptでは、基本的には、他の処理系でよくあるようなsleep()やdelay()に相当するブロッキングに処理を待つような機能は提供されていません。

そのため、下記のような書き方をすると、

setTimeout(function() {
    console.log("delay about 2sec")
}, 2000)

console.log("call async")
call async
delay about 2sec

と言うような結果になります。
それでは、今回の例のようなブロッキングが発生する処理を待ってから、後続の処理を行いたい場合はどのように書けば良いでしょうか。
いくつかありますが、ここではES7から実装された、async, awaitを使ってみましょう。
async, awaitはJavascriptだけでなく、RustやC#などでも機能が提供されている非同期処理を扱うデザインパターンなので、不明点がある場合はしっかり勉強しておくと後々生きてくるでしょう。

[JavaScript] async, awaitを使って非同期処理を記述する

async, awaitを使うとかなりシンプルに書くことができます。

function sleep(waitSec) {
    return new Promise(function (resolve) {
        setTimeout(function() { resolve() }, waitSec);
    });
} 

async function call() {
    console.log("timer start")
    await sleep(2000);
    console.log("2 sec later")
    await sleep(3000);
    console.log("5 sec later")
    await sleep(1000);
    console.log("6 sec later")
    console.log("timer end")
}

call()

console.log("call faster")

この場合、callメソッド中のawaitでI/O waintingが発生しているため、そのI/O 待ち時間中にcallの後から読んでいるconsole.log("call faster")
が呼ばれるため、出力結果は以下のようになります。

timer start
call faster
2 sec later
5 sec later
6 sec later
timer end
<||

秋葉原でPCパーツを購入するところからUbuntu起動まで

自作パソコンの購入

非情報系学部出身の自分にとって、日々ハードウェアやカーネルの勉強をするものの、なんとなくハードよりの知識が乏しく、ハードウェアのイメージがわかないので、意を決して自作パソコンを作ることにした。 
決めたからには即行動ということで、急遽自宅を飛び出して自作パソコンを購入した。今回はその一連の流れをまとめる。もし自作パソコンに興味があって、躊躇している人の背中をおせればいいなちお思う

パーツの購入からLinux起動までにかかった時間

結論からいうと、自作パソコン未経験の自分でも5時間程度でLinuxを使えるようになった。パソコン作りながらラーメン食べたりしていたので、実働時間としては4時間くらいだろう。

完成したパソコンのスペックは、
・メモリ 64GB
・CPU Intel Core i7 9700 3.0GHZ
SSD 1TB
・HDD 4TB

このスペック+光る筐体(パソコンケース)含めて、なんと210,000万円弱(税抜)でできてしまった。。個人所有のディスクトップでこのスペックがあれば申し分ないものが、この価格でできるなんて、、
なお、今回はNVIDIAGPUも搭載したかったが、予算の都合上一旦諦めることにした。

全体の流れ

  • 秋葉原でPCパーツの購入
  • パソコンの組み立て
  • Boot用のUSBmemoryの作成(MacOS
  • USBをパソコンにさして、BIOSの設定、起動

つまったところ

初めての自作パソコンで、最初マザーボードと電源ユニットの接続が複雑そうに見えて狼狽したが、一つひとつやっていけば簡単だった。とにかく、ネットでぐぐればなんでも出てくるので一つ一つ調べながらやっていけば、大丈夫そう。
あとは、Linux用ISOファイルが入ったUSBメモリの作成であるが、調べたところ4GB以上の容量がないとうまくLinuxが入ってくれない可能性があるので注意したほうがよさそう。所有していない場合は、パソコンパーツを購入する段階で、一緒にかったほうがよさそう。
あと、パソコンパーツの専門店いくと多くの商品が並んでいて、同じマザーボードでも何を選んだらいいかわからないなどある。自分はどういう基準で選べばいいかわからなかったので、ショップのお兄さんに相談して一緒に選んでもらった。知識に自身がないひとはそうしたほうがいいかもしれない。

自作パソコンを作ってみて

なんといっても、高スペックのパソコン安い金額で作れるのが魅力だろう。Dockerもサクサク動かせるし、今このブログもノーストレスでかけている。
地味にLInux使うのも初めてなので、ちょっとわくわくしている。

結論

自作PCは最高。