Submit Search
Upload
Elixir入門「第6回:Elixirはtry…catchを書かない~障害対応のパラダイムシフト~」
•
6 likes
•
2,178 views
fukuoka.ex
Follow
Elixirの「スーパーバイザ」を使って、シンプルかつ強力な耐障害性を実現するコードをサクっと書いてみます
Read less
Read more
Engineering
Report
Share
Report
Share
1 of 31
Recommended
containerdの概要と最近の機能
containerdの概要と最近の機能
Kohei Tokunaga
Elixir入門「第2回:PC間で通信するアプリをサクっと書いてみる」
Elixir入門「第2回:PC間で通信するアプリをサクっと書いてみる」
fukuoka.ex
Serf / Consul 入門 ~仕事を楽しくしよう~
Serf / Consul 入門 ~仕事を楽しくしよう~
Masahito Zembutsu
コンテナネットワーキング(CNI)最前線
コンテナネットワーキング(CNI)最前線
Motonori Shindo
Dockerfile を書くためのベストプラクティス解説編
Dockerfile を書くためのベストプラクティス解説編
Masahito Zembutsu
react-scriptsはwebpackで何をしているのか
react-scriptsはwebpackで何をしているのか
暁 三宅
Elixir入門「第1回:パターンマッチ&パイプでJSONパースアプリをサクっと書いてみる」
Elixir入門「第1回:パターンマッチ&パイプでJSONパースアプリをサクっと書いてみる」
fukuoka.ex
無料で仮想Junos環境を手元に作ろう
無料で仮想Junos環境を手元に作ろう
akira6592
Recommended
containerdの概要と最近の機能
containerdの概要と最近の機能
Kohei Tokunaga
Elixir入門「第2回:PC間で通信するアプリをサクっと書いてみる」
Elixir入門「第2回:PC間で通信するアプリをサクっと書いてみる」
fukuoka.ex
Serf / Consul 入門 ~仕事を楽しくしよう~
Serf / Consul 入門 ~仕事を楽しくしよう~
Masahito Zembutsu
コンテナネットワーキング(CNI)最前線
コンテナネットワーキング(CNI)最前線
Motonori Shindo
Dockerfile を書くためのベストプラクティス解説編
Dockerfile を書くためのベストプラクティス解説編
Masahito Zembutsu
react-scriptsはwebpackで何をしているのか
react-scriptsはwebpackで何をしているのか
暁 三宅
Elixir入門「第1回:パターンマッチ&パイプでJSONパースアプリをサクっと書いてみる」
Elixir入門「第1回:パターンマッチ&パイプでJSONパースアプリをサクっと書いてみる」
fukuoka.ex
無料で仮想Junos環境を手元に作ろう
無料で仮想Junos環境を手元に作ろう
akira6592
そろそろ知っておきたい!!コンテナ技術とDockerのキホン
そろそろ知っておきたい!!コンテナ技術とDockerのキホン
Naoki Nagazumi
Effective Modern C++ 勉強会#7 Item 27
Effective Modern C++ 勉強会#7 Item 27
Mitsuru Kariya
Android起動周りのノウハウ
Android起動周りのノウハウ
chancelab
Wireshark入門(2)
Wireshark入門(2)
彰 村地
The Usage and Patterns of MagicOnion
The Usage and Patterns of MagicOnion
Yoshifumi Kawai
ソニーでElectronアプリをリリースしてみた
ソニーでElectronアプリをリリースしてみた
Yasuharu Seki
Rustで DDD を実践しながら API サーバーを実装・構築した(つもり)
Rustで DDD を実践しながら API サーバーを実装・構築した(つもり)
ShogoOkazaki
Docker入門-基礎編 いまから始めるDocker管理【2nd Edition】
Docker入門-基礎編 いまから始めるDocker管理【2nd Edition】
Masahito Zembutsu
Dockerからcontainerdへの移行
Dockerからcontainerdへの移行
Kohei Tokunaga
Perlの勘所をマスターしよう! コンテキストとリファレンスを我が物に!
Perlの勘所をマスターしよう! コンテキストとリファレンスを我が物に!
近藤 嘉雪
大学研究室レベルでLocal 5Gを導入するための手法の考察
大学研究室レベルでLocal 5Gを導入するための手法の考察
Yutaka Kikuchi
initとプロセス再起動
initとプロセス再起動
Takashi Takizawa
ドメイン駆動設計 本格入門
ドメイン駆動設計 本格入門
増田 亨
nfcpy 0.10.0 でハマった話
nfcpy 0.10.0 でハマった話
Masaki Yamamoto
ツール比較しながら語る O/RマッパーとDBマイグレーションの実際のところ
ツール比較しながら語る O/RマッパーとDBマイグレーションの実際のところ
Y Watanabe
3種類のTEE比較(Intel SGX, ARM TrustZone, RISC-V Keystone)
3種類のTEE比較(Intel SGX, ARM TrustZone, RISC-V Keystone)
Kuniyasu Suzaki
PlaySQLAlchemy: SQLAlchemy入門
PlaySQLAlchemy: SQLAlchemy入門
泰 増田
今こそ知りたいSpring Web(Spring Fest 2020講演資料)
今こそ知りたいSpring Web(Spring Fest 2020講演資料)
NTT DATA Technology & Innovation
ソーシャルゲームのためのデータベース設計
ソーシャルゲームのためのデータベース設計
Yoshinori Matsunobu
脱RESTful API設計の提案
脱RESTful API設計の提案
樽八 仲川
東京Node学園#8 Let It Crash!?
東京Node学園#8 Let It Crash!?
koichik
エキ Py 読書会02 2010/9/7
エキ Py 読書会02 2010/9/7
Tetsuya Morimoto
More Related Content
What's hot
そろそろ知っておきたい!!コンテナ技術とDockerのキホン
そろそろ知っておきたい!!コンテナ技術とDockerのキホン
Naoki Nagazumi
Effective Modern C++ 勉強会#7 Item 27
Effective Modern C++ 勉強会#7 Item 27
Mitsuru Kariya
Android起動周りのノウハウ
Android起動周りのノウハウ
chancelab
Wireshark入門(2)
Wireshark入門(2)
彰 村地
The Usage and Patterns of MagicOnion
The Usage and Patterns of MagicOnion
Yoshifumi Kawai
ソニーでElectronアプリをリリースしてみた
ソニーでElectronアプリをリリースしてみた
Yasuharu Seki
Rustで DDD を実践しながら API サーバーを実装・構築した(つもり)
Rustで DDD を実践しながら API サーバーを実装・構築した(つもり)
ShogoOkazaki
Docker入門-基礎編 いまから始めるDocker管理【2nd Edition】
Docker入門-基礎編 いまから始めるDocker管理【2nd Edition】
Masahito Zembutsu
Dockerからcontainerdへの移行
Dockerからcontainerdへの移行
Kohei Tokunaga
Perlの勘所をマスターしよう! コンテキストとリファレンスを我が物に!
Perlの勘所をマスターしよう! コンテキストとリファレンスを我が物に!
近藤 嘉雪
大学研究室レベルでLocal 5Gを導入するための手法の考察
大学研究室レベルでLocal 5Gを導入するための手法の考察
Yutaka Kikuchi
initとプロセス再起動
initとプロセス再起動
Takashi Takizawa
ドメイン駆動設計 本格入門
ドメイン駆動設計 本格入門
増田 亨
nfcpy 0.10.0 でハマった話
nfcpy 0.10.0 でハマった話
Masaki Yamamoto
ツール比較しながら語る O/RマッパーとDBマイグレーションの実際のところ
ツール比較しながら語る O/RマッパーとDBマイグレーションの実際のところ
Y Watanabe
3種類のTEE比較(Intel SGX, ARM TrustZone, RISC-V Keystone)
3種類のTEE比較(Intel SGX, ARM TrustZone, RISC-V Keystone)
Kuniyasu Suzaki
PlaySQLAlchemy: SQLAlchemy入門
PlaySQLAlchemy: SQLAlchemy入門
泰 増田
今こそ知りたいSpring Web(Spring Fest 2020講演資料)
今こそ知りたいSpring Web(Spring Fest 2020講演資料)
NTT DATA Technology & Innovation
ソーシャルゲームのためのデータベース設計
ソーシャルゲームのためのデータベース設計
Yoshinori Matsunobu
脱RESTful API設計の提案
脱RESTful API設計の提案
樽八 仲川
What's hot
(20)
そろそろ知っておきたい!!コンテナ技術とDockerのキホン
そろそろ知っておきたい!!コンテナ技術とDockerのキホン
Effective Modern C++ 勉強会#7 Item 27
Effective Modern C++ 勉強会#7 Item 27
Android起動周りのノウハウ
Android起動周りのノウハウ
Wireshark入門(2)
Wireshark入門(2)
The Usage and Patterns of MagicOnion
The Usage and Patterns of MagicOnion
ソニーでElectronアプリをリリースしてみた
ソニーでElectronアプリをリリースしてみた
Rustで DDD を実践しながら API サーバーを実装・構築した(つもり)
Rustで DDD を実践しながら API サーバーを実装・構築した(つもり)
Docker入門-基礎編 いまから始めるDocker管理【2nd Edition】
Docker入門-基礎編 いまから始めるDocker管理【2nd Edition】
Dockerからcontainerdへの移行
Dockerからcontainerdへの移行
Perlの勘所をマスターしよう! コンテキストとリファレンスを我が物に!
Perlの勘所をマスターしよう! コンテキストとリファレンスを我が物に!
大学研究室レベルでLocal 5Gを導入するための手法の考察
大学研究室レベルでLocal 5Gを導入するための手法の考察
initとプロセス再起動
initとプロセス再起動
ドメイン駆動設計 本格入門
ドメイン駆動設計 本格入門
nfcpy 0.10.0 でハマった話
nfcpy 0.10.0 でハマった話
ツール比較しながら語る O/RマッパーとDBマイグレーションの実際のところ
ツール比較しながら語る O/RマッパーとDBマイグレーションの実際のところ
3種類のTEE比較(Intel SGX, ARM TrustZone, RISC-V Keystone)
3種類のTEE比較(Intel SGX, ARM TrustZone, RISC-V Keystone)
PlaySQLAlchemy: SQLAlchemy入門
PlaySQLAlchemy: SQLAlchemy入門
今こそ知りたいSpring Web(Spring Fest 2020講演資料)
今こそ知りたいSpring Web(Spring Fest 2020講演資料)
ソーシャルゲームのためのデータベース設計
ソーシャルゲームのためのデータベース設計
脱RESTful API設計の提案
脱RESTful API設計の提案
Similar to Elixir入門「第6回:Elixirはtry…catchを書かない~障害対応のパラダイムシフト~」
東京Node学園#8 Let It Crash!?
東京Node学園#8 Let It Crash!?
koichik
エキ Py 読書会02 2010/9/7
エキ Py 読書会02 2010/9/7
Tetsuya Morimoto
エキ Py 読書会02 2章後半
エキ Py 読書会02 2章後半
Tetsuya Morimoto
Error handling in Erlang and Scala
Error handling in Erlang and Scala
Masahito Ikuta
Kanazawa.js.Next
Kanazawa.js.Next
dynamis
Rust Error Handling
Rust Error Handling
ShunsukeNakamura17
はじめてのCodeIgniter
はじめてのCodeIgniter
Yuya Matsushima
Java/Androidセキュアコーディング
Java/Androidセキュアコーディング
Masaki Kubo
JavaScript基礎勉強会
JavaScript基礎勉強会
大樹 小倉
[db tech showcase Tokyo 2014] B26: PostgreSQLを拡張してみよう by SRA OSS, Inc. 日本支社 高塚遥
[db tech showcase Tokyo 2014] B26: PostgreSQLを拡張してみよう by SRA OSS, Inc. 日本支社 高塚遥
Insight Technology, Inc.
Lisp Tutorial for Pythonista Day 6
Lisp Tutorial for Pythonista Day 6
Ransui Iso
LLVM overview 20110122
LLVM overview 20110122
nothingcosmos
実践 NestJS
実践 NestJS
Ayumi Goto
関数型言語&形式的手法セミナー(3)
関数型言語&形式的手法セミナー(3)
啓 小笠原
LPICレベル1技術解説セミナー(2012/11/11)
LPICレベル1技術解説セミナー(2012/11/11)
Kazuko Itoda
【SQiP2014】システム操作インターフェイス最適化によるテスト自動化ROI向上
【SQiP2014】システム操作インターフェイス最適化によるテスト自動化ROI向上
Tatsuya Ishikawa
Serfが面白いと俺の中で話題にwwwwww 【改訂版】
Serfが面白いと俺の中で話題にwwwwww 【改訂版】
Masahito Zembutsu
Rails3.2ってどう変わるの?
Rails3.2ってどう変わるの?
Takafumi ONAKA
Javaの進化にともなう運用性の向上はシステム設計にどういう変化をもたらすのか
Javaの進化にともなう運用性の向上はシステム設計にどういう変化をもたらすのか
Yoshitaka Kawashima
WTM53 phpフレームワーク いまさらcodeigniter
WTM53 phpフレームワーク いまさらcodeigniter
Masanori Oobayashi
Similar to Elixir入門「第6回:Elixirはtry…catchを書かない~障害対応のパラダイムシフト~」
(20)
東京Node学園#8 Let It Crash!?
東京Node学園#8 Let It Crash!?
エキ Py 読書会02 2010/9/7
エキ Py 読書会02 2010/9/7
エキ Py 読書会02 2章後半
エキ Py 読書会02 2章後半
Error handling in Erlang and Scala
Error handling in Erlang and Scala
Kanazawa.js.Next
Kanazawa.js.Next
Rust Error Handling
Rust Error Handling
はじめてのCodeIgniter
はじめてのCodeIgniter
Java/Androidセキュアコーディング
Java/Androidセキュアコーディング
JavaScript基礎勉強会
JavaScript基礎勉強会
[db tech showcase Tokyo 2014] B26: PostgreSQLを拡張してみよう by SRA OSS, Inc. 日本支社 高塚遥
[db tech showcase Tokyo 2014] B26: PostgreSQLを拡張してみよう by SRA OSS, Inc. 日本支社 高塚遥
Lisp Tutorial for Pythonista Day 6
Lisp Tutorial for Pythonista Day 6
LLVM overview 20110122
LLVM overview 20110122
実践 NestJS
実践 NestJS
関数型言語&形式的手法セミナー(3)
関数型言語&形式的手法セミナー(3)
LPICレベル1技術解説セミナー(2012/11/11)
LPICレベル1技術解説セミナー(2012/11/11)
【SQiP2014】システム操作インターフェイス最適化によるテスト自動化ROI向上
【SQiP2014】システム操作インターフェイス最適化によるテスト自動化ROI向上
Serfが面白いと俺の中で話題にwwwwww 【改訂版】
Serfが面白いと俺の中で話題にwwwwww 【改訂版】
Rails3.2ってどう変わるの?
Rails3.2ってどう変わるの?
Javaの進化にともなう運用性の向上はシステム設計にどういう変化をもたらすのか
Javaの進化にともなう運用性の向上はシステム設計にどういう変化をもたらすのか
WTM53 phpフレームワーク いまさらcodeigniter
WTM53 phpフレームワーク いまさらcodeigniter
More from fukuoka.ex
AI入門「第4回:ディープラーニングの中身を覗いて、育ちを観察する」
AI入門「第4回:ディープラーニングの中身を覗いて、育ちを観察する」
fukuoka.ex
【macOSにも対応】AI入門「第3回:数学が苦手でも作って使えるKerasディープラーニング」
【macOSにも対応】AI入門「第3回:数学が苦手でも作って使えるKerasディープラーニング」
fukuoka.ex
【LT版】Elixir入門「第7回:Python/KerasをElixirから繋いでアレコレする」
【LT版】Elixir入門「第7回:Python/KerasをElixirから繋いでアレコレする」
fukuoka.ex
Elixir入門「第3回:Phoenix 1.3で高速webアプリ & REST APIアプリをサクッと書いてみる」
Elixir入門「第3回:Phoenix 1.3で高速webアプリ & REST APIアプリをサクッと書いてみる」
fukuoka.ex
Elixir入門「第5回:Visualixirで見るマルチプロセス」
Elixir入門「第5回:Visualixirで見るマルチプロセス」
fukuoka.ex
AI入門「第3回:数学が苦手でも作って使えるKerasディープラーニング」【旧版】※新版あります
AI入門「第3回:数学が苦手でも作って使えるKerasディープラーニング」【旧版】※新版あります
fukuoka.ex
重力プログラミング入門「第1回:地球の重力下で人工衛星を公転軌道に乗せる」
重力プログラミング入門「第1回:地球の重力下で人工衛星を公転軌道に乗せる」
fukuoka.ex
AI入門「第1回:AIの歴史とTensorFlow」
AI入門「第1回:AIの歴史とTensorFlow」
fukuoka.ex
やや関数型を意識した風Elixir/Phoenixご紹介
やや関数型を意識した風Elixir/Phoenixご紹介
fukuoka.ex
AI入門「第2回:Scala/Spark/Mahoutでレコメンドエンジンを作る」
AI入門「第2回:Scala/Spark/Mahoutでレコメンドエンジンを作る」
fukuoka.ex
Elixir入門「第1回:パターンマッチ&パイプでJSONパースアプリをサクっと書いてみる」【旧版】※新版あります
Elixir入門「第1回:パターンマッチ&パイプでJSONパースアプリをサクっと書いてみる」【旧版】※新版あります
fukuoka.ex
Elixir入門「第3回:Phoenix 1.2で高速Webアプリ & REST APIをサクッと書いてみる」【旧版】※新版あります
Elixir入門「第3回:Phoenix 1.2で高速Webアプリ & REST APIをサクッと書いてみる」【旧版】※新版あります
fukuoka.ex
More from fukuoka.ex
(12)
AI入門「第4回:ディープラーニングの中身を覗いて、育ちを観察する」
AI入門「第4回:ディープラーニングの中身を覗いて、育ちを観察する」
【macOSにも対応】AI入門「第3回:数学が苦手でも作って使えるKerasディープラーニング」
【macOSにも対応】AI入門「第3回:数学が苦手でも作って使えるKerasディープラーニング」
【LT版】Elixir入門「第7回:Python/KerasをElixirから繋いでアレコレする」
【LT版】Elixir入門「第7回:Python/KerasをElixirから繋いでアレコレする」
Elixir入門「第3回:Phoenix 1.3で高速webアプリ & REST APIアプリをサクッと書いてみる」
Elixir入門「第3回:Phoenix 1.3で高速webアプリ & REST APIアプリをサクッと書いてみる」
Elixir入門「第5回:Visualixirで見るマルチプロセス」
Elixir入門「第5回:Visualixirで見るマルチプロセス」
AI入門「第3回:数学が苦手でも作って使えるKerasディープラーニング」【旧版】※新版あります
AI入門「第3回:数学が苦手でも作って使えるKerasディープラーニング」【旧版】※新版あります
重力プログラミング入門「第1回:地球の重力下で人工衛星を公転軌道に乗せる」
重力プログラミング入門「第1回:地球の重力下で人工衛星を公転軌道に乗せる」
AI入門「第1回:AIの歴史とTensorFlow」
AI入門「第1回:AIの歴史とTensorFlow」
やや関数型を意識した風Elixir/Phoenixご紹介
やや関数型を意識した風Elixir/Phoenixご紹介
AI入門「第2回:Scala/Spark/Mahoutでレコメンドエンジンを作る」
AI入門「第2回:Scala/Spark/Mahoutでレコメンドエンジンを作る」
Elixir入門「第1回:パターンマッチ&パイプでJSONパースアプリをサクっと書いてみる」【旧版】※新版あります
Elixir入門「第1回:パターンマッチ&パイプでJSONパースアプリをサクっと書いてみる」【旧版】※新版あります
Elixir入門「第3回:Phoenix 1.2で高速Webアプリ & REST APIをサクッと書いてみる」【旧版】※新版あります
Elixir入門「第3回:Phoenix 1.2で高速Webアプリ & REST APIをサクッと書いてみる」【旧版】※新版あります
Elixir入門「第6回:Elixirはtry…catchを書かない~障害対応のパラダイムシフト~」
1.
Elixir入門 第6回 Elixirは try…catchを書かない 例外処理から耐障害性へのパラダイムシフト~ 2017/08/17
ver0.5作成 2017/08/23 ver0.9作成
2.
1 1. 例外処理から耐障害性へのシフト 2. try…catchからどう変わるのか? 3.
スーパーバイザのプロセス監視、復旧 4. Phoenixから学ぶ準正常系 5. 耐障害性のための構成と復旧戦略 6. パターンマッチと耐障害性へのシフト 7. 「耐障害性プログラミング」にようこそ 目次
3.
2 1.例外処理から耐障害性へのシフト
4.
3 1.例外処理から耐障害性へのシフト C++やJava、その他、多くのプログラミング言語では、「例外」を 扱うために、try…catchと例外ハンドラのセットで対応します その中には、「プログラムのバグ」のような、人間が想定し切れない ような対象も含める必要があります このように例外処理は、本来のメイン処理で無いハンドラを多数 想定・準備する労力がかかる上、想定外には対応し切れない、 脆弱性を持つアプローチです (ハンドラ自体のバグにも弱い) 一方、Elixirでは、「想定外の発生は仕方無いので、クリーンに 落とし、復旧する」ことに着目した、「耐障害性」を備えています Elixirにおける、耐障害性のデザインや様式を学ぶことで、障害 対応・例外処理に関するパラダイムシフトを体験できます
5.
4 2.try…catchからどう変わるのか?
6.
5 ログアラータ 従来のプログラム 2.try…catchからどう変わるのか? try…catchによる例外処理は、「本体処理」をtry~で囲み、 例外発生時は、各catchで捕まえ、例外ハンドラを起動します 一方、Elixirでは、本体処理には手を加えず、「スーパーバイザ」 と呼ばれる監視プログラムを別系で配備します try 本体処理 例外ハンドラ群 catch① 例外ハンドラ① catch② 例外ハンドラ② Elixirプログラム 本体処理 スーパーバイザ (プロセス監視) 復旧戦略 ダウン 監視 ダウンしたら 再起動 アラート 通知 ログ 監視
7.
6 2.try…catchからどう変わるのか? 監視プログラムからの復旧戦略の代表として「プロセス再起動」が ありますが、プロセス起動が軽量なElixirだから実現可能という、 プログラミング言語の特性が大きく寄与しています (関数型言語のイミュータブルな特性も、この実現に貢献します) ・本体処理と例外処理を1つ のコードにまとめられる ※これは分離できない、という デメリットでもある メリット デメリット Elixir 耐障害性 例外処理 ・メモリリークを作り込みやすい ・不整合を作り込みやすい ・プロセスを再起動する設計が 考慮から漏れる可能性がある ・プロセス再起動で全てが解決 できるとは限らない ※とはいえ、プロセス再起動の 設計は例外処理でも本当 は必要 ・障害対応を本体処理から 分離できる ・メモリリークや不整合を解消 できる構造 ・プロセス再起動を予め設計
8.
7 従来のプログラム 2.try…catchからどう変わるのか? ちなみに、try…catchによる例外処理をしているプログラムでも、 外部の監視系を導入していれば、Elixirのスーパーバイザと同様 の構築にあたりますが、「例外処理と監視系が障害監視の役割 を重複していた」というケースが実態でしょう (もしくは、プロセスの 再起動が考慮されていなかった、というケースもあり得ます) try 本体処理 例外ハンドラ群 catch 例外ハンドラ (外部の監視系) アラート ダウン 監視 ダウンしたら 再起動 復旧戦略 通知
9.
8 3.スーパーバイザのプロセス監視、復旧
10.
9 3.スーパーバイザのプロセス監視、復旧 プロセス監視、再起動の例として、「ファイルを読み込み、内容を 返す」という簡単なサーバプログラムを使って説明します defmodule Pass do #
サーバ def cat_server() do receive do { sender_pid, path } -> { :ok, result } = File.read( path ) send( sender_pid, { true, result } ) end cat_server() end # サーバプロセス起動 def start_cat_server() do pid = spawn( Pass, :cat_server, [] ) :global.register_name( :cat, pid ) end # サーバを呼び出すクライアント def cd( path ) do send( :global.whereis_name( :cat ), { self(), path } ) listen() end end lib/pass.ex ※Elixir入門 第2回「PC間で通信するアプリをサクっと書いてみる」の例と同じコードです
11.
10 3.スーパーバイザのプロセス監視、復旧 まず、「GenServer」という汎用モジュールを使って書き直します (サーバとクライアントの書き分けや起動コードが不要になります) 呼び出し方は以下のように変わりますが、実行結果は同じです defmodule PassGenServer do use
GenServer def start_link() do { :ok, pid } = GenServer.start_link( __MODULE__, "" ) IO.puts( "--- PassGenServer.start_link() PID=#{inspect pid} ---" ) { :ok, pid } end def handle_call( { :cat, path }, _from, _state ) do { :ok, result } = File.read( path ) { :reply, result, "" } end end lib/pass_genserver.ex iex> { :ok, pid } = GenServer.start_link( PassGenServer, "" ) {:ok, #PID<0.211.0>} iex> GenServer.call( pid, { :cat, "a.txt" } ) "I'm a.txt"
12.
11 3.スーパーバイザのプロセス監視、復旧 このサーバにおける「想定外」として、以下のようなパターンがあり ますが、ここでは、存在しないファイルを指定した例を行います 異常系・・・存在しないファイルを指定、読込権限無、等 バグ・・・ファイル名が文字列で無い、長過ぎる、等 例外が発生し、サーバプロセスがダウンします iex>
GenServer.call( pid, { :cat, "b.txt" } ) ** (EXIT from #PID<0.206.0>) an exception was raised: ** (MatchError) no match of right hand side value: {:error, :enoent} (node1) lib/pass_genserver.ex:10: PassGenServer.handle_call/3 (stdlib) gen_server.erl:615: :gen_server.try_handle_call/4 (stdlib) gen_server.erl:647: :gen_server.handle_msg/5 (stdlib) proc_lib.erl:247: :proc_lib.init_p_do_apply/3 14:31:55.520 [error] GenServer #PID<0.211.0> terminating ** (MatchError) no match of right hand side value: {:error, :enoent} (node1) lib/pass_genserver.ex:10: PassGenServer.handle_call/3 (stdlib) gen_server.erl:615: :gen_server.try_handle_call/4 (stdlib) gen_server.erl:647: :gen_server.handle_msg/5 (stdlib) proc_lib.erl:247: :proc_lib.init_p_do_apply/3 Last message: {:cat, "b.txt"}
13.
12 3.スーパーバイザのプロセス監視、復旧 サーバプロセスがダウンした後は、存在するファイルを指定しても、 エラーとなります 再度サーバ起動してやり直すと、今度は正常に返します この流れをプログラム化することで、復旧処理を自動化することが、 「スーパーバイザ」の役割になります iex> GenServer.call( pid,
{ :cat, "a.txt" } ) warning: variable "pid" does not exist and is being expanded to "pid()", please use parentheses to remove the ambiguity or change the variable name iex:1 ** (CompileError) iex:1: undefined function pid/0 (stdlib) lists.erl:1354: :lists.mapfoldl/3 iex> { :ok, pid } = GenServer.start_link( PassGenServer, "" ) iex> GenServer.call( pid, { :cat, "a.txt" } ) "I'm a.txt"
14.
13 3.スーパーバイザのプロセス監視、復旧 スーパーバイザのコードは、以下の通りです たった、これだけのコードを追加するだけで、プロセスダウン監視と プロセスダウン後の再起動が実現されることは、驚異的です 例外処理で、同等の処理を書くことは、不可能に限りなく近く、 また自前の監視系を作るのも、骨が折れる作業です Elixirは、この機能が標準装備されており、非常にお手軽です import Supervisor.Spec defmodule PassSupervisor
do def start_link() do servers = [ worker( PassGenServer, [ 0, [ name: :server_process ] ] ) ] Supervisor.start_link( servers, strategy: :one_for_one ) end end lib/pass_supervisor.ex
15.
14 3.スーパーバイザのプロセス監視、復旧 スーパーバイザを起動します GenServerプロセス (PID=<0.190.0>) が起動され、監視 するスーパーバイザプロセス
(PID=<0.189.0>) が起動され ていることが確認できます これまで、PIDを指定してGenServerを呼んでいましたが、スー パーバイザ経由でのGenServer起動では、PIDを取得すること ができないため、「:server_process」というプロセス名を付与 しており、プロセス名で呼び出しが可能です iex> PassSupervisor.start_link() --- PassGenServer.start_link() PID=#PID<0.190.0> --- {:ok, #PID<0.189.0>} iex> GenServer.call( :server_process, { :cat, "a.txt" } ) "I'm a.txt"
16.
15 3.スーパーバイザのプロセス監視、復旧 では、プロセスダウン監視とプロセス再起動を試してみましょう ログ出力順が逆転していますが、例外が発生し、GenServerが ダウンしていますが、新たなGenServerが自動起動しています iex> GenServer.call( :server_process,
{ :cat, "b.txt" } ) --- PassGenServer.start_link() PID=#PID<0.197.0> --- ** (exit) exited in: GenServer.call(:server_process, {:cat, "b.txt"}, 5000) ** (EXIT) an exception was raised: ** (MatchError) no match of right hand side value: {:error, :enoent} (node1) lib/pass_genserver.ex:16: PassGenServer.handle_call/3 (stdlib) gen_server.erl:615: :gen_server.try_handle_call/4 (stdlib) gen_server.erl:647: :gen_server.handle_msg/5 (stdlib) proc_lib.erl:247: :proc_lib.init_p_do_apply/3 (elixir) lib/gen_server.ex:774: GenServer.call/3 iex(4)> 15:22:39.965 [error] GenServer :server_process terminating ** (MatchError) no match of right hand side value: {:error, :enoent} (node1) lib/pass_genserver.ex:16: PassGenServer.handle_call/3 (stdlib) gen_server.erl:615: :gen_server.try_handle_call/4 (stdlib) gen_server.erl:647: :gen_server.handle_msg/5 (stdlib) proc_lib.erl:247: :proc_lib.init_p_do_apply/3 Last message: {:cat, "b.txt"} State: ""
17.
16 3.スーパーバイザのプロセス監視、復旧 本当に再起動できているか確認します 問題無く、再起動できていることが確認できました このように、スーパーバイザを使うと、とても手軽に、プロセス監視 と再起動を組み込むことができます ここで気になるのが、プロセスを再起動しても済まないケース… つまり「準正常系」をどう扱うか、だと思います この例として、Phoenixのような、エラー時でもレスポンスする 必要があるケースについて、次章で見ていきます iex> GenServer.call( :server_process,
{ :cat, "a.txt" } ) "I'm a.txt"
18.
17 4.Phoenixから学ぶ準正常系
19.
18 4.Phoenixから学ぶ準正常系 ここまでの障害対応は、「異常系」「バグ」の2パターンのみでした 一方で、PhoenixのようなWebアプリでは、404エラーのような、 レスポンスを返すエラーがあり、これは「準正常系」と呼ばれます
20.
19 4.Phoenixから学ぶ準正常系 準正常系は、異常系やバグと異なり、発生が想定可能なため、 try…catchでのエラーハンドリングを作り込むこととなります Phoenixで、404エラーのような準正常系のエラーハンドリングが、 どのように実現されているか見てみましょう tryの中の本体処理で、例外が発生した場合、catch中にある、 エラーレンダラーが呼び出され、エラーページが表示されます defmodule Phoenix.Endpoint do … def
call(conn, opts) do … try do super(conn, opts) catch kind, reason -> Phoenix.Endpoint.RenderErrors.__catch__(conn, kind, reason, @phoenix_render_errors) end end … deps/phoenix/lib/phoenix/endpoint.ex
21.
20 4.Phoenixから学ぶ準正常系 ここまでをまとめると、障害対応において大事なことは、「異常系」 「バグ」「準正常系」の3種類を、「明確に区別する」ということです 「異常系」「バグ」の場合は、想定ができないため、try…catch でのエラーハンドリングは書かず、プロセスをダウンさせ、再起動で クリーンナップする方向で対応します 一方、「準正常系」は、想定可能なため、try…catchでエラー ハンドリングを作り込み、暗黙のプロセスダウン/再起動には 任せません 万全と、「try…catchは普通に書くものだ」と自動思考すること で、この区別が不明確となるような事態を、Elixirは避けられる 構造を持っており、自然と耐障害性が実現されます
22.
21 5.耐障害性のための構成と復旧戦略
23.
22 5.耐障害性のための構成と復旧戦略 ここまでは、単品かつデータも持たない単純なプロセスでの復旧を 見てきましたが、複数プロセスで、共通のデータを保持する場合 には、以下のような構成が必要となってきます 共通データ 保持プロセス スーパーバイザ 監視 本体処理① プロセス 監視 本体処理② プロセス 監視
24.
23 5.耐障害性のための構成と復旧戦略 更に、本体処理自体が、複数のプロセスで構成される場合は、 サブのスーパーバイザを作り、そのプロセスグループの中で再起動 を制御できます 共通データ 保持プロセス スーパーバイザ 監視 本体処理① プロセス 監視 本体処理②-1 プロセス 監視 本体処理②用 スーパーバイザ 本体処理②-2 プロセス 監視 監視
25.
24 5.耐障害性のための構成と復旧戦略 スーパーバイザから、スーパーバイザを起動するコードは、以下の ようになります 通常のサーバを起動するコードと、ほぼ変わりません import Supervisor.Spec defmodule PassSubSupervisor
do def start_link() do servers = [ worker( PassGenServer, [ 0, [ name: :server_process ] ] ) ] Supervisor.start_link( servers, strategy: :one_for_one ) end end lib/pass_sub_supervisor.ex import Supervisor.Spec defmodule PassSupervisor do def start_link() do servers = [ supervisor( PassSubSupervisor, [ 0, [ name: :ssv_process ] ] ) ] Supervisor.start_link( servers, strategy: :one_for_one ) end end lib/pass_supervisor.ex
26.
25 5.耐障害性のための構成と復旧戦略 「復旧戦略」が、何種類か選べます (代表2つを紹介) one_for_one
・・・ 1プロセス落ちたら1プロセス再起動 one_for_all ・・・ 1プロセス落ちたら配下を全再起動 共通データ 保持プロセス スーパーバイザ 監視 本体処理① プロセス 監視 本体処理②-1 プロセス 監視 本体処理②用 スーパーバイザ 本体処理②-2 プロセス 監視 監視
27.
26 6.パターンマッチと耐障害性へのシフト
28.
27 6.パターンマッチと耐障害性へのシフト 「想定外」のうち、バグの例として、「ファイル名が文字列で無い」 「長過ぎる」といった、いわゆる「バリデーションチェック」に相当する ものを紹介しました 他の言語であれば、関数に入った後に、バリデーションチェックを 行いますが、Elixirでは、「パターンマッチ」による、関数呼出前の チェックが行えます この特徴を活用すると、想定外に対するバリデーションチェックの 開発をスキップし、本体処理のコーディングにより専念することが 可能となります 「try…catchの煩雑さ」と「バリデーションチェックの煩雑さ」が、 コードから無くなり、シンプルな本質のみのコードという世界観です
29.
28 7.「耐障害性プログラミング」にようこそ
30.
29 7.「耐障害性プログラミング」にようこそ 今回は、try…catchに変わる障害対応として、スーパーバイザ を使ったプロセス監視と再起動についてご説明しました Elixirの耐障害性プログラミングが「そこまで難しく無いかも?」と 思っていただけたら、この入門としては大成功です 本体処理に例外処理を混ぜず、障害対応を分離し、想定外が 発生した際はプロセス再起動する世界は、これまで親しんだプロ グラミングの概念と、大きく異なるコンセプトでは無かったでしょうか このシンプルで強力な役割の分離により、プログラムの設計という ものが根底から改善されます この新たなパラダイムを活かし、仕事でも趣味でも、プログラミング ライフをエンジョイしてください!
31.
30 ご清聴ありがとうございます