Jetty9でSPDY+WebSocket

自己紹介

アジェンダ

  • Jettyとは何か
  • SPDY
    • 主要なコンポーネントの概要説明
    • GroovyでSPDYサーバを実行する
  • WebSocket
    • 主要なコンポーネントの概要説明
    • GroovyでWebSocketサーバを実行する
    • WebSocketサーバをSPDY上で実行する
  • まとめ

Jettyとは何か?

  • Javaで実装されたHTTPサーバ。
    • デフォルト非同期I/O。
    • JEE6 WebProfile実装なのでServletをデプロイ出来る。
    • ニッチで先進的な機能が満載。
    • 比較的コードがクリーンでライセンスが緩いのでハックし易い。
    • Stableリリースは7系と8系だが今回は開発版の9系ベースで話をする。
  • Eclipse Public License - Version 1.0 / Apache License - Version 2.0
  • 主にintalioの人がメンテナンスしている。

SPDY

  • npn-boot
  • npn-api
  • spdy-core
  • spdy-client
  • spdy-server
  • spdy-http-server

npn-boot

  • Java用のNext Protocol Negotiation Extension実装。
  • OpenJDKの名前空間である sun.security.ssl をダーティハックしている為、GPL。
  • eclipse foundationでは無く、mortbay.org名義でリリースしているが、作業している人は同じなので単に政治的なアレだと思う。
  • Javaがセキュリティアップデートする度に修正が入ったものがリリースされる。
_人人 人人人_
> 突然のGPL <
 ̄Y^Y^Y^Y^Y ̄

npn-api

  • NPNをJettyから使う為のラッパーレイヤー
  • クラスは1つしかないので特に見るべきものは無い

spdy-core

  • SPDYプロトコルを実装しているモジュール
  • 対応しているバージョンはV2とV3

spdy-core package構成

  • org.eclipse.jetty.spdy
    • SPDY実装の中心
  • org.eclipse.jetty.spdy.api
    • SPDYの基礎的な仕様及びデータ構造定義
  • org.eclipse.jetty.spdy.frames
    • フレーム構造の定義
  • org.eclipse.jetty.spdy.generator
    • 出力仕様に対する実装
  • org.eclipse.jetty.spdy.parser
    • 入力仕様に対する実装
    • 酷いコードの様に見えるかもしれないけど、Javaでプロトコルパーザを普通に書いたらこんなもん。

spdy-client

  • 非同期I/OベースのSPDYクライアントライブラリ
  • SPDYに対応しているサーバをクローリングするなら速いかも
  • 自前SPDYサーバを自動テストするのに使うと良いかも?

spdy-server

  • Jettyのサーバフレームワーク上に実装されているSPDYのサーバ実装
  • 主にSPDY用TCPコネクションの管理コード

spdy-http-server

  • HTTP上で動作する為のサーバ処理
  • 文字列に関する処理は全部ここ
  • SPDY-http-proxyの実装
  • JettyをSPDYサーバとして使う場合このモジュールを使う

SPDYサーバを実行する

new org.eclipse.jetty.server.Server().with {
    def connector = new org.eclipse.jetty.spdy.server.http.HTTPSPDYServerConnector(it,
            new org.eclipse.jetty.util.ssl.SslContextFactory(
            keyStorePath: 'src/main/resources/spdy.jks',
            keyStorePassword: 'password',
            protocol: 'TLSv1'))
    connector.port = 8443
    addConnector connector

    handler = new org.eclipse.jetty.server.handler.ResourceHandler(
            resourceBase: 'src/main/resources/webcontent')

    start()
    join()
    // https://localhost:8443/spdy.html
    // chrome://net-internals/#spdy
}

WebSocket

  • websocket-api
  • websocket-common
  • websocket-client
  • websocket-servlet
  • websocket-server

WebSocketの位置づけ

  • アプリケーションでは直接使うべきではないもの。
  • CometD の様な抽象度が高いフレームワークを使うべき。
  • WebSocketはフレームワークを作る人間が叩くAPI

websocket-api

  • 実装はほぼ無くinterfaceの定義が中心。
  • Futureパターンによる非同期I/O
  • JettyでWebSocketするなら理解する必要がある。
  • JavaEE7ではWebSocketが使える様になる。

websocket-common

  • WebSocketプロトコルを実装しているモジュール
  • RFC6455実装
  • 以前は様々なバージョンのWebSocketに対応していたがJetty9で大胆に削除

websocket-common package構成

  • org.eclipse.jetty.websocket.common
    • Parser見とけばJavaでのWebSocket実装は大体分かる。
  • org.eclipse.jetty.websocket.common.events
    • WebSocketにおけるサーバとアプリケーションのGlueコードが記述されている。
  • org.eclipse.jetty.websocket.common.extensions.compress
    • 圧縮系のアレ
  • org.eclipse.jetty.websocket.common.extensions.fragment
  • org.eclipse.jetty.websocket.common.extensions.identity
    • fragmentとidentityは、よく分からない。
  • org.eclipse.jetty.websocket.common.extensions.mux
    • multiplexing対応の処理

websocket-client

  • 非同期I/OベースのWebSocketクライアントライブラリ。
  • multiplexing対応のコードもある。

websocket-servlet

  • ServletとしてWebSocketアプリケーションをデプロイする為のライブラリ。
  • JettyでWebSocketするなら、このモジュールを使う。

websocket-server

  • Jettyのサーバフレームワーク上に実装されているWebSocketのサーバ実装。
  • multiplexing対応を含むWebSocket用TCPコネクションの管理コード。
  • ExampleEchoServer.javaを見れば使い方は大体分かる。

WebSocketサーバを実行する

@org.eclipse.jetty.websocket.api.annotations.WebSocket
class Echo {
    @org.eclipse.jetty.websocket.api.annotations.OnWebSocketMessage
    void onText(org.eclipse.jetty.websocket.api.Session session, String msg) {
        session.getRemote().sendStringByFuture(msg)
    }
}
new org.eclipse.jetty.server.Server(8080).with {
    handler = new org.eclipse.jetty.websocket.server.WebSocketHandler.Simple(Echo)
    handler.handler = new org.eclipse.jetty.server.handler.ResourceHandler(
            resourceBase: 'src/main/resources/webcontent')

    start()
    join()
    // http://localhost:8080/
}

WebSocket on SPDY

@org.eclipse.jetty.websocket.api.annotations.WebSocket
class DoubleEcho {
    @org.eclipse.jetty.websocket.api.annotations.OnWebSocketMessage
    void onText(org.eclipse.jetty.websocket.api.Session session, String msg) {
        session.getRemote().sendStringByFuture("$msg $msg")
    }
}
new org.eclipse.jetty.server.Server().with {
    def connector = new org.eclipse.jetty.spdy.server.http.HTTPSPDYServerConnector(it,
            new org.eclipse.jetty.util.ssl.SslContextFactory(
            keyStorePath: 'src/main/resources/spdy.jks',
            keyStorePassword: 'password',
            protocol: 'TLSv1'))
    connector.port = 8443
    addConnector connector

    handler = new org.eclipse.jetty.websocket.server.WebSocketHandler.Simple(DoubleEcho)
    handler.handler = new org.eclipse.jetty.server.handler.ResourceHandler(
            resourceBase: 'src/main/resources/webcontent')

    start()
    join()
    // https://localhost:8443/
    // chrome://net-internals/#spdy
}

まとめ

  • Javaでプロトコルパーザを書くと酷い
  • Jettyは全体的に非同期I/O
  • JettyのWebSocketはCometD越しに使うのがオススメ
  • SPDYの上にWebSocket乗せるのは楽勝

ご清聴ありがとうございました