Open4

[メモ]ベイズモデリング/WebServer

as_pmlas_pml

ベイズモデリング

センサーデータ解析

  • 生体センサー
  • 位置情報
  • IoTセンサー

難しい点

  • 環境ノイズ・個体差
  • データの欠損・遅延
  • データの時間方向・空間方向の複雑さ
  • ドメイン知識が必須になる可能性

ベイズモデリングとは?

  • データ解析を行うための方法論の一つ
  • データに対してモデルと呼ばれる数学的な仮定を与えることによりデータから何かしら判断に有益な情報を引き出す

特徴

  • 不確実性をともなた予測を行うことができる(点ではなく、幅を持った予測を行う)
  • 課題に合わせた柔軟な解析(データの特性に応じてモデルを自在に拡張できる)
  • オーバーフィットの回避(フィッティングや最適化を行わないので発生しない)
as_pmlas_pml

Webサーバーとは何か?

Webサーバーがやっていること

  • クライアントからの接続を待ち受ける
  • クライアントから送られてきたHTTPリクエストをパースする
  • リクエストに基づいてHTTPレスポンスを生成する
  • クライアントにレスポンスを返す

HTTP規格=>RFC(Request For Comments)

  • IETFによる技術仕様の保存、公開形式
  • プロトコル・フォーマットなどが中心
  • HTTPはRFC7230~7235

言語選定

Python

-> SimpleHTTPServer

Scala (JVM)

  • Javaとの相互運用
     →シームレスな呼び出し、Java標準ライブラリの再利用
  • 簡潔性
     →省略可能な構文、型推論、強力な標準ライブラリ
  • 抽象度の高いコード、新しい制御構文を定義できる表現力
     →Whatの強調、Howの隠蔽
  • 静的型付け
     →検証可能性、リファクタリングのしやすさ、ドキュメント性
    !更なる型安全を求める場合(ScalaでRefinement Typeを実装する)
    →refined/newtypeというライブラリがある

Clojure (JVM)

  • Lisp
     →文法が少ない、データとしてのコード
     * 状態についての考え方:Identity, State, Valueの分離
     * シンプルさについて
  • 関数型プログラミングのための言語
     →第一級関数、イミュータブルなデータ構造、再帰的なループ
  • Javaとの相互運用性
  • 平行処理のために設計
  • REPLを活かしたインタラクティブ/インクリメンタルな開発

Java

Common Lisp

Elixir

Dart

Webサーバを作る

アーキテクチャ

  • 起動し、localhostの特定のポートでHTTPリクエストを待ち受ける
  • 対応するHTTPリクエストメソッドは一旦GETのみ
     ←一旦それ以外のメソッドもGETとみなすように実装
  • publicディレクトリより上の階層へのリクエストは403Forbidden
  • リソースのMIMEは外部ファイルで設定できる
  • リクエストをブロックしない(マルチスレッドにする)
  • Keep-Aliveはしない(コネクションは都度colse)
  • HTTP Cacheはしない(304はリターンしない)

各言語による実装

GitHub のファイルURLまたはパーマリンクを指定してください

Webサーバーアプリケーション

  1. InputStream = MimeDetoctor.java
  2. HTTPリクエストパース = Request.java
  3. Request = RequestHandler.java
  4. リクエストに応じたレスポンスの生成 = RequestParser.java
  5. Respose = Response.java
  6. HTTPレスポンスの返却 = SimpleJavaHttpServer.java
  7. OutputStream = WorkerThread.java

Socketの扱い

=通信におけるエンドポイントを表現したデータモデル
各エンドポイントを識別する2つの値=IPアドレス&ポート番号
→多くの場合ソケット(socket)と呼ばれる

  • ServerSocketオブジェクトを生成し、クライアントからの接続を.acceptで待ち受ける
  • Socketオブジェクト(クライアントソケット)からIn/OutputStreamが取得できる
  • InputStreamからHTTPリクエストをReadし、OutputStreamにHTTPレスポンスをWriteする
//ソケットの生成及び接続の待ち受け
ServerSocket serverSocket = new ServerSocket(8080);
while (true){
    //接続の待ち受け,接続されるまではブロックする
    Socket socket = serverSocket.accept();
    InputStream in = socket.getInputStream();
    OutPutStream out = socket.getOutputStream();
    ...
}
(let [server-socket (new ServerSocket 8080)]
    (while true
        (let [socket (.accept server-socket)
            in (.getInputStream socket)
            out (.getOutputStream socket)]
        ....)
    )
)

正規表現

名前付きグループ(Scala)

  • 正規表現のグループに名前をつけることができる
  • グループの順序が変わったとしても取り出す処理はそのままで良い
    →リクエストラインの各要素に名前をつけて抽出

パターンマッチ(Scala)

  • switch文(Java)に類似しているが柔軟性が高い(Scala)
     ・値一致だけでなく、型、構造による分岐が可能
     ・正規表現オブジェクトに対してもマッチさせることができる
    #### Option型(Scala)
  • JavaでいうところのOptional
  • SomeとNoneからなる型(代数的データ型)
  • 値があるかないか分からない状態を表す
  • 値が存在するのかのチェックを強制できる

re-find(clojure)

  • 正規表現にマッチした文字列を取得
  • 引数にグループ化した正規表現オブジェクトを渡すと先頭にはマッチした文字列全体、以降にキャプチャされた文字列のベクター

zipmap(clojure)

  • 強力なコレクションライブラリ
  • zipmapは2つのコレクションからMapを作る
  • clojureではクラスよりもMapを好む
    →re-findとzipmapを使いリクエストラインの各要素をMapに変換する

文字列の扱い

文字列結合

・スレポンス(オブジェクト/マップ)からHTTPレスポンスヘッダを組み立てる

Java

+/StringBuilder
→responseオブジェクトをHTTPレスポンスへ変換

Scala

String Interpolation
→文字列リテラルの前に$をつけると変数展開できる
Triple Quote
→改行を含む文字列を埋め込むには"""を使う
→インデントを揃えるにはインデント文字(|)とstripMarginを利用する

clojure

・文字列の結合は+ではなくstrで行う
・可変長引数、S式ならでは

リソース管理

Try with Resources (Java)

・try句を抜けると自動でcloseされる
・対象は、java.io.Closeable, java.lang.AutoCloseableの実装クラス

try(InputStream in = new FileInputStream(file)){}

Loan Pattern (Scala)

・「借りたら返す」を確実に行うためのイディオム/制御構文っぽい
(Scalaではあたかも言語が持つ制御構造のようなメソッドをプログラマが定義できる)

//基本形
val reader = new BufferedReader(...)
useing(reader){r=>
}
//loan Patternの実装
def using[A, R<: Closeable] (resource: R)(f: R=>A): A={
    try{
        f(resource)
    } finally{
        resource.close()
    }
}

with-open (clojure)

・Loan Patternに似たwith-openというマクロ,with-openを抜けたらsocketをcloseする
・letと似た感覚で使える

//基本形
(with-open [f (io/file "test.txt")] ...)
//実装
(with-open [s socket]
    (-> (request-parser/from-input-stream
        (.getInputStream s)
    )
    (request-handler/handle-request)
    (response-witer/write (.getOutputStream s))
    )
)

並列処理

  • リクエストをシリアルに処理すると他のリクエストを持たせてしまうためリクエストを受けた先は平行処理にする
  • JVM言語での実装においてはマルチスレッドで組む

Thread (java)

class AThread extends Thread {
    public void run(){
        //非同期処理
    }
}
AThread h = new AThread();
h.start();

class BRunnable implements Runnable{
    public void run(){
        //非同期処理
    }
}
BRunnable f = new Thread(new BRunnable());
f.start();

threadの定義と起動

public class WorkerThread extends Thread {
    private Socket socket;
    private RequestParser parser;
    private RequestHandler handler;

    pubilc WorkerThread(
        Socket socket, RequstParser parser,
        RequestHandler handler{
            ....
        }
    )
}

//threadの起動
Thread worker = 
    new WorkerThread(socket, parser, handler);
worker.start();

Excutor Service (java)

・より高水準な並列処理のための仕組み
・スレッドプールの種類も指定できる

ExecutorService cachedPool = 
    Excutors.newCachedThreadPool();
cachedPool.execute(runnable);

ExecutorService fixedPool =
    Executors.newFixedThreadPool(4);
fixedPool.execute(runnable);

Future (scala)

・Future.appleyは渡された処理を非同期に実行
・いつどのように非同期実行するかはExecutionContext次第

import scala.concurrent.Future
import scala.concurrent.ExcutionContext.Implicits.global
val result: Future[User] = Future{
    userRepository.fetch(userId)
}
result.map(user=> user.id)
result.flatMap(user => tweetRepository.fetch(user.id))

・ExcutionContextは以下のように受け取る
(Future.applyに引数ブロックが2つあり、このうち2つ目の引数ブロックでExcution Contextを受け取る)

Future {
    userRepository.fetch(user)
    Future.apply(
        userRepository.fetch(user)
    )
}

object Future {
    ...
    def apply[T](body: => T)(implicit executor: ExecutionContext): Future[T] = unit.map(_ => body)
    ...
}

implicit: WhatとHowの分離

  • Howが巨大になるとコード本来の意図が埋もれてしまう
  • implicitの大きなモチベーションはHowの隠蔽
  • What, Howを分離することで目的を端的に示すことができる
val values: Seq[(String, Option[Int])]
val sorted = sort(values)(
    tuple2Comparator(
        stringComparator,
        optionComparator(intComparator)
    )
)
val sorted = sort(values) //implicit

clojure

  • 平行処理関数が大量にある
    (Ex: go, thread, send, send-off, future...)
関数 スレッドプール スレッド数
send FixedThreadPool 2+コア数
send-off CachedThreadPool 制限なし
future /future-call/ pmap/ pcalls CachedThreadPool 制限なし
go FixedThreadPool コア数*2 + 42
thread/ thread-call CachedThreadPool 制限なし
reducers ForkJoinPool 自動制御
//CachedThreadPoolを用いた並列処理
(thread (
    with-open [s socket
                in (.getInputStream s)
                out (.getOutputStream s)]
        (-> (request-parser/from-input-stream in)
            (request-handler/handle-request)
            (response-wtiter/write out))
))

IO/CPUバウンド

Appの負荷はIOバウンドとUPCバウンドの2種類に大別される

  • IOバウンド
     ・IOがパフォーマンスのボトルネック
     ・CPUは待機が多くなる
     ・ディスクアクセス、リモート通信など
  • CPUバウンド
     ・CPUがパフォーマンスのボトルネック
     ・CPUはフル稼働
     ・大規模な科学計算など