komura-c.log

https://blog.komura-c.page/ に移動しました

AngularでWebWorkerを使ったバックグラウンドで生きるSetInterval

JavaScriptでは、一定の間隔で関数やコードスニペットを繰り返し呼び出す用のSetIntervalというWebAPIが使用できます。

setInterval() - Web API | MDN

こちらをログなどのため、スマホのバックグラウンドでも動かしたいという要件があったのですが、

バックグラウンドのタブによる負荷(および関連するバッテリーの使用量)を軽減するために、ブラウザはアクティブでないタブの最小タイムアウト時間を強制します。

アクティブでないタブの最小タイムアウトは 1 秒です。

setTimeout() - Web API | MDN

などの理由でアクティブなタブとそうでないタブで動作が異なることが分かりました。(実際の実装でも、バックグラウンドでの動作が不安定でした)

そのため、WebWorkerを使うことで、バックグラウンドでもアクティブなタブの時のように指定した秒数でタイマーが動くように実装したのでメモします。

WebWorkerとは

Web Worker とは、ウェブアプリケーションにおけるスクリプトの処理をメインとは別のスレッドに移し、バックグラウンドでの実行を可能にする仕組みのことです。時間のかかる処理を別のスレッドに移すことが出来るため、 UI を担当するメインスレッドの処理を中断・遅延させずに実行できるという利点があります。

とあります。

developer.mozilla.org

AngularでのWebWorkerの作り方

Angularでは、次のngコマンドでWebWorkerを生成することができます。

ng generate web-worker workers/set-interval(対象ディレクトリor使うcomponentパス/Worker名)

Angular 日本語ドキュメンテーション

Workerを使ったSetInterval

その後生成されたxx.worker.tsを次のように書き換えます。

// アプリケーション側からpostMessageされたものが送られる
addEventListener('message', ({ data }) => {
  const intervalTime = event.data.intervalTime;
  if (intervalTime) {
  // WorkerスレッドでsetIntervalする
    setInterval(() => {
      postMessage(null);
    }, intervalTime) as unknown as number;
  }
});

そして該当ServiceやComponentから次のように設定します

worker: Worker = null;
// 2分に設定
intervalTime = 2 * 60 * 1000;
intervalId: number = null; // typeof window.setInterval

if (typeof Worker !== 'undefined') {
  // ワーカーを作成
 this.worker = new Worker(new URL('../workers/set-interval.worker', import.meta.url));
// Worker側からpostMessageされたものが送られる
  this.worker.onmessage = () => {
      // 定期的に実行したい関数
      callback();
  };
  // アプリケーション側から2分間隔で送るようにWorkerのSetIntervalを設定
  this.worker.postMessage({intervalTime: this.intervalTime});
} else {
    // Web Workerがサポートされていない時の処理
     this.intervalId = setInterval(
        callback,
        this.intervalTime,
      )
}

こういった実装をすることで、スマホのバックグラウンドやアクティブでないタブの時も、通常の動作のSetIntervalを設定することができます。

注意点としては、Workerは止めるか、Workerが存在するか、タブが閉じられない限り生き続けてしまい、メモリリークなどの原因となる場合があるため、必要がなくなったときは、次のようにworker.terminate();を呼ぶことで終了できます。

    if (this.worker) {
      // terminateでworkerを強制終了、worker自体をnullに
      this.worker.terminate();
      this.worker = null;
    } else if (this.intervalId) {
    // Web Workerがサポートされていない時に通常のsetIntervalをしていたらclearIntervalする
      clearInterval(this.intervalId);
    }

Worker.terminate() - Web API | MDN

まとめ

WebWorkerを使う機会がなく、初めて使いましたがWorkerスレッドでスマホのバックグラウンドでも実行したい動作を統一することや、メインスレッドの負担になるような重い処理を逃すことに使えるというのを実際に体験できました。 参考になれば嬉しいです。

参考

タブがバックグラウンドになってもサボらない setInterval · GitHub

MarpでGithubActionsを使ってGithubPagesにスライドを公開してみる

はじめに

LTとか発表用のスライドはGoogleスライドなどで作るのが良いなーと思っているんですが、自分の勉強、個人利用で簡単にスライド作成したいのとマークダウン使いたいなー良いのないかなと探してたら、Marpという良いCLIツールを見つけたので書いておきます。

Marpとは

marp.app

Marpは、プレーンなMarkdownを使用してスライドを作成するためのエコシステムです。とあります。 エコシステムという表現をしてるのは、CLIツールだけでなく、最小構成のHTML&CSSフレームワークだったり、変換するエンジンの部分がCoreとして切り出されていたり、VSCodeの拡張があるからっぽいですね。

Marp導入

今回はnodeで使いたいので、npmでインストールします。

npm -y init
npm install --save-dev @marp-team/marp-cli

その後、マークダウンファイルを記述します。

---
marp: true
theme: gaia
class:
    - lead
---

## はじOS読んだまとめ

---

### 読んだ目的
- コンピュータシステムの全体像を理解する

---

...

https://github.com/komura-c/tech-memo/blob/main/src/slides/first-os-code-reading.md

こんな感じで書いた後、mdファイルを置いた読み込むディレクトリ、HTMLファイルを出力するディレクトリを設定します。 設定ファイルは、marp.config.jsとか.marprcとかを作成して書けるみたいですが、面倒なのでpackage.jsonのセクションに書きます。

...
  "marp": {
    "inputDir": "./src/slides/",
    "output": "./public/slides/"
  }
...

https://github.com/marp-team/marp-cli#configuration-file

そして、開発用サーバーを立ち上げます。

marp -s

その開発サーバー(localhost)にアクセスすると、スライドとして出力結果が見れるので、コードを編集しながらスライドを作成できます。

その他できることについては、リポジトリのREADMEにまとまってるのでやりたいことを探してみると良いかもしれません。 GitHub - marp-team/marp-cli: A CLI interface for Marp and Marpit based converters

実際に出力結果を別のファイルに書き出したい時は、次のシンプルなコマンドでできます。

marp

GithubActionsを使ってGithubPagesにスライドを公開してみる

今回は、マークダウンファイルを編集してpushしたら、スライドが公開されてるといいなーと考えたため、簡単なGithubActionsのyamlファイルを記述してGithubPagesに公開してみました。

// 先ほどのコマンドを少し変えてpackage.jsonに書き、npmコマンドで使えるようにしています
  "scripts": {
    "dev": "marp -s",
    "build": "marp && cp src/index.html public"
  },
name: Publish GitHub Pages
on:
  push:
    branches:
      - main

jobs:
  publish:
    runs-on: ubuntu-latest
    timeout-minutes: 5
    steps:
      - uses: actions/checkout@v3
      - uses: actions/setup-node@v3
        with:
          node-version: 14
      - run: npm ci
      - run: npm run build
      - uses: peaceiris/actions-gh-pages@v3
        with:
          deploy_key: ${{ secrets.ACTIONS_DEPLOY_KEY }}
          publish_dir: ./public

やってることは、単純にMarpをインストールして、出力結果をgh-pagesブランチにpushすることで、GithubPagesに公開しているという感じです。

注意点が1つあるとすると、リポジトリにpushするため、そのためのsshキーをdeploy_keyで指定する必要があることです。 これは、actions-gh-pagesのリポジトリのREADMEのCreate SSH Deploy Keyに設定方法が丁寧に書いてあります。 https://github.com/peaceiris/actions-gh-pages#tips-and-faq

手順としては、次のコマンドで生成した公開鍵を自分のリポジトリのSettings→Deploy Keys、秘密鍵を自分のリポジトリのSettings→Secretsに追加するだけです。

ssh-keygen -t rsa -b 4096 -C "$(git config user.email)" -f gh-pages -N ""

Secretsに追加した秘密鍵のタイトルでdeploy_keyに追加することで使えます。

deploy_key: ${{ secrets.ACTIONS_DEPLOY_KEY }}

おわりに

雑にMarpでGithubActionsを使ってGithubPagesにスライドを公開してみる流れを書きました。シンプルに触ってみただけなため、まだ活用できているわけではなく、今後上手く使っていこうと思いました。

WebブラウザがWebページを表示するまで

この2つの記事を読めば分かることなのですが、自分なりにまとめておこうと思います。

URLの解析

アドレスバーにhttps://komura-c.hatenablog.com/が入力されてenterキー(スマホなら開くとか)が押された時、 WebブラウザはURL(Uniform Resource Locators)を次の3つの要素に解析(パース)します。

  1. スキーム

    • スキームはHTMLファイルなどのリソースを取得するための方法を表します。ここではhttpsであり暗号化されたhttp通信を表します。
  2. ホスト名

    • リソースが存在するホスト(コンピュータ)名を表します。ホスト名は、2つの要素に分けられます。1つはローカル名であり、ここではkomura-cです。2つ目はドメイン名であり、ここではkomura-c.hatenablog.comです。
  3. パス名

    • ホスト名で指定されたコンピュータ上のリソースの位置やファイル名を示します。ここでは/です。ブラウザに省略されることがあります。

DNSによる名前解決

URL解析の後、WebブラウザはそのURLのIPアドレスを探します。URLは人間が覚えやすいようにIPアドレスの数字を文字列として置き換えたものであり、実際にはIPアドレスを指定してデータを送受信しています。

DNS(Domain Name System)は、ドメイン名に対応するIPアドレスが自動的にわかる仕組みです。DNSの仕組みは、情報が欲しいクライアント、情報を探すフルリゾルバー、情報を持っている権威DNSサーバーに分けられます。次の手順でIPアドレスを探します。

  1. ルートサーバーにkomura-c.hatenablog.comIPアドレスを聞くと、ドメイン名右端のTopLevelDomein、ここでは.comのネームサーバーを案内されます。
  2. .comのネームサーバーにkomura-c.hatenablog.comIPアドレスを聞くと、.hatenablog.comのネームサーバを案内されます。
  3. .hatenablog.comのネームサーバにkomura-c.hatenablog.comIPアドレスを聞くと、IPアドレスである、35.77.187.130が返ってきます。(ここでは、はてな側で〇〇.hatenablog.comを割り当てるため、IPアドレスを直で入力してもアクセスできません)

この流れをDNSの名前解決といいます。フルリゾルバーは、初回の名前解決の後、ドメイン情報を一定期間保持しておき、次回以降の名前解決で再利用するキャッシュをもつため、実際にはWebブラウザは初めにキャッシュがないか探します。 また、このように階層的にネームサーバーを持ち分散管理することでたくさんのIPアドレスの情報持つことができています。

プロトコルに応じたソケット通信

DNSによる名前解決の後、スキーマに応じたプロトコルで通信を行います。適切なポート(HTTPは80, HTTPSは443)を用いて送信元IPアドレス(送信ポート)と宛先IPアドレス(宛先ポート)を接続するためのソケットを開いて通信を行います。ここでは、HTTPSのため、SSLにより暗号化されたHTTP通信が行われます。

HTTP通信

HTTP(Hypertext Transfer Protocol)は、WebサーバーとクライアントのWebブラウザがデータを送受信するために使用するアプリケーション層のプロトコルです。Webページを記述するために使用するHTMLによる文書、画像、音声、動画等のファイルを表現形式を含めてやりとりできます。ステートレスなプロトコルであり、サーバーはリクエスト間で何もデータを保持しません。また、HTTPはTCPプロトコル上で動作しています。

主な流れとしては、クライアントはサーバーにリクエストを送信するためにポートを開き、サーバー側からのレスポンスが返ってくるまで待機します。つまり、Webブラウザが送り出したHTTPリクエストというTCPパケットを使ったメッセージに対して、WebサーバーがHTTPレスポンスを返します。

TCP/IP

TCP(Transmission Control Protocol)は、IP(Internet Protocol)を基盤にその上層で利用されるプロトコルです。IPネットワーク上の2地点間で信頼性の高い通信を可能にするコネクション型プロトコル(通信相手の応答があってはじめて通信を開始する)であることから、データ転送を行う前にコネクションの確立を行います。その流れを3ウェイハンドシェイクといい、次の手順を踏みます。

  1. クライアントがサーバにコネクション確立の要求
    • SYNパケットを送ります。
  2. サーバはクライアントのコネクション要求に応答、そしてサーバからもコネクション確立を要求
    • サーバが接続を受け入れる場合は、クライアントにSYNフラグとACKフラグの両方が1にセットされたSYN/ACKパケットを応答します。
  3. クライアントもサーバからのコネクション要求に応答
    • ACKフラグを立てたACKパケットを送ることで接続を確立します。

TLS/SSLによる暗号化通信

SSL(Secure Socket Layer)は、インターネット上でデータを暗号化して送受信する方法のひとつです。

HTTPSを有効にしたいウェブサイトは、CA(Certificate Authority)からTLS/SSL証明書を取得する必要があります。SSL証明書は次の3つのレベルがあります。

  1. ドメイン認証(DV)ドメインの使用権があるかどうか。
  2. 企業実在認証・組織認証(OV)登記簿などを確認し、組織として法的に実在しているかどうか。
  3. EV認証(EV)上記2つの認証の項目に加え、物理的に組織が存在しているか、事業が存在・運営されているか、承認者・署名者は誰なのか。

TLS(Transport Layer Security)は、SSLをもとに標準化させたものをいいます。 TLS暗号化通信には、対になる2つの共通鍵暗号方式と公開鍵暗号方式という仕組みを用います。

一般的な公開鍵暗号の一つはRSA暗号で、512bit、1024bit、2048bit、3072bitの鍵長をもちます。一般的な共通鍵暗号の一つは、AES暗号で、128bit、192bit、256bitの鍵長をもちます。鍵長が長ければ長いほど、暗号文は解読しにくくなり安全です。しかし計算手順は増えるため暗号化とその復号に時間がかかります。現在では基本的に鍵長2048bit以上のRSA鍵を使用が推奨されています。

通信手順は次のとおりです。

  1. 接続要求

    • クライアント側からSSL通信のリクエストをサーバ側へ送信します。
  2. SSLサーバ証明書と公開鍵をクライアント側へ送付

    • サーバ側から、公開鍵付きのSSLサーバ証明書をクライアント側に送付します。
    • クライアント側のブラウザに搭載されているルート証明書で署名を確認し、SSLサーバ証明書を検証します。
    • 共通鍵(セッションキー)を生成します。
  3. 共通鍵を暗号化しサーバ側へ送付

    • クライアント側で生成された暗号用の共通鍵を、サーバ側から送られてきた公開鍵を用いて暗号化し、サーバ側へ送信されます。
    • サーバ側に送信された共通鍵は、サーバ側で保持している秘密鍵で復号され、共通鍵が取り出されます。
  4. TLS/SSL暗号化通信開始

    • 個人情報などの機密性の高いデータを、クライアント側で保持している共通鍵で暗号化し、サーバ側へ送信します。
    • サーバ側は、受け取った暗号データをサーバ側で保持している共通鍵で復号してデータを取得します。
    • 共通鍵は、サーバとクライアントが使用するブラウザの双方が対応する、最も強度の高い暗号方式・鍵長が使用されます。

ブラウザのレンダリング

プロトコルに応じたソケット通信の完了、ここではHTTPS通信のレスポンスによって、サーバーからindex.htmlを取得した後ブラウザはレンダリングを開始します。

ブラウザのレンダリングは主に次の手順で行われます。

Loading

  • HTML中のタグでCSSJavaScriptファイルなどの外部リソースを取得します。

Scripting

Rendering

  1. HTML Parsing
    • HTMLファイルを字句解析、構文解析します。
    • HTMLファイルをJavaScriptで操作できるデータ構造であるDOMの、tree(抽象構文木)に変換します。
  2. CSS(Style) Parsing
    • CSSのスタイルを適用します。
    • DOM treeをスタイル情報(ComputedStyle)付きDOM treeに変換します。
  3. Layout
    • HTMLとStyleを基に場所とサイズを計算して、各要素の位置座標を確定します。
    • スタイル情報(ComputedStyle)付きDOM treeをLayout treeに変換します。
  4. Layering
    • 独立に描画可能な範囲をレイヤーに分割します。(iframeなど)
    • Layout treeをLayerに変換します。

Painting

  • どの順番で描画するかを決め、描画します

DOMというデータ構造を渡すことで、JavaScriptなどによる動的レンダリングの高速化、Partial invalidation(部分実行)ができる仕組みになっています。

参考

セルフエンジニア研修

入社した会社で軽く研修っぽいものがあり、 自分の知った気になっている基礎知識が上手く説明できるぐらいに定着していなかったことに気づいたため、 セルフエンジニア研修をやっていくことにした。 主に以下のページを中心にやっていく。

サイボウズの新卒研修(Web、Docker、k8s、gRPC) https://blog.cybozu.io/entry/2021/07/20/100000

リクルートの新卒研修(AWSの基礎) https://recruit-tech.co.jp/blog/2021/08/20/recruit-bootcamp-2021/

ミクシィの新卒研修(iOSアプリ、Androidアプリ、Unityゲーム開発) https://mixi-developers.mixi.co.jp/21-technical-training-a0bcdbf9bca0

iijブートキャンプ(ハンズオン資料集) https://iij.github.io/bootcamp/#_2021%E5%B9%B4%E5%BA%A6%E3%82%AB%E3%83%AA%E3%82%AD%E3%83%A5%E3%83%A9%E3%83%A0%E4%B8%80%E8%A6%A7

各項目について随時メモや備忘録を書いていくこととする。

はじめてのOSコードリーディングを読んだ

内定式でいただいた本だったけれど、低レイヤーの知識があんまりなく腰が上がらなくて、積んであったのをGWで読んだ。

C言語については大学の授業で触れたぐらいで、ソースコード自体を深く読めていなかったが、OSが何をおこなって動いているかの全体像は理解したつもりになれた。ざっくりメモしながら読み進めた。

プロセス管理では、カーネルはプロセスを随時切り替えることで、1つのCPUで複数のプログラムを並行して処理していて、wait, execなどシステムコールをうまく使って資源を効率的に使ってるのが改めて理解できた。

それ以外にも、実行優先度でスワッピングしたり、割り込み、トラップとか、今処理しなきゃいけない端末からの入力に基づく処理などを上手くハンドリングする仕組みがあるところが勉強になった。

一回読んだだけなのでもう少し深い理解をしたくなったらまた読み返したい。

2022年3月20日雑KPT

Keep

  • OSS貢献、翻訳と軽い修正2つやった

  • Node学園みた

  • Next.jsで作り直した github.com

Problem

  • TwitterなどでWebのトレンドを追っているだけになっていて、JavaScriptの仕様とか本質的な理解が進めてない

  • CSの知識もあまりなくて、普通にエンジニアになるの不安だなと感じている

Try

  • ZennとかQiitaの記事を読むのも良いが、公式ドキュメントを読む

  • 技術書を週1冊目標で、最低でも月に1冊読む習慣をつける

  • 来月以降になるが、Recursionというサービスやってみる

2021と2022

今更思考整理のために書く

2021やったこと

2022やりたいこと

  • 仕事

    • 長期インターンから正社員になるので責任もってもっと色々コミットしたい
  • OSSヘコントリビュート

    • 時間みつけたらやっていきたい
    • 翻訳以外もチャレンジしたい
  • 開発関連

    • 触って満足しがちなので、ライブラリ触ったらプロダクト作るか、プルリク投げるか、記事書くかしたい
    • 新卒研修資料公開されてるやつ、3社ぐらいピックアップして見切る
    • テスト周りの知見がないのでやる
    • ビルドツール周り、パーサー周りにも興味あるのでASTとかもっと詳しくなりたい
    • Next.js, Sveltekitキャッチアップしたい
    • サーバーサイドの知識はぼちぼち身につけていきたいが、Webフロントエンドエンジニアとしてもっとやっていきたい
    • でもCS弱いので基本情報とかクラウドベンダー系資格の勉強をしたい気持ちはある
  • LT、登壇

    • 苦手だけど増やしていきたいなあ
    • 技術書、それ以外も全然読めてないので、読んでまとめと感想書きたい